Imported Upstream version 0.0.2

parents
*.gem
*.rbc
.bundle
.config
.yardoc
Gemfile.lock
InstalledFiles
_yardoc
coverage
doc/
lib/bundler/man
pkg
rdoc
spec/reports
test/tmp
test/version_tmp
tmp
source 'https://rubygems.org'
# Specify your gem's dependencies in delayer.gemspec
gemspec
Copyright (c) 2013 Toshiaki Asai
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# Delayer
Delay Any task. Similar priority-queue.
## Installation
Add this line to your application's Gemfile:
gem 'delayer'
And then execute:
$ bundle
Or install it yourself as:
$ gem install delayer
## Usage
Task = Delayer.generate_class # Define basic class
Task = Delayer.generate_class(priority: [:high, :middle, :low], default: :middle) # or, Priority delayer
Task = Delayer.generate_class(expire: 0.5) # and/or, Time limited delayer.
task = Task.new { delayed code ... } # Register task
task = Task.new(:high) { delayed code ... } # or, You can specify priority.
task.cancel # Task can cancel before Delayer#run.
Task.run # Execute all tasks.
Task.run(1) # or, You can specify expire.
## Contributing
1. Fork it
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create new Pull Request
require "bundler/gem_tasks"
require 'rake/testtask'
task :default => [:test]
Rake::TestTask.new do |test|
# $LOAD_PATH に追加するパス (デフォルトで 'lib' は入っている)
test.libs << 'test'
# テスト対象ファイルの指定
test.test_files = Dir[ 'test/**/test_*.rb' ]
test.verbose = true
end
\ No newline at end of file
# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'delayer/version'
Gem::Specification.new do |spec|
spec.name = "delayer"
spec.version = Delayer::VERSION
spec.authors = ["Toshiaki Asai"]
spec.email = ["toshi.alternative@gmail.com"]
spec.description = %q{Delay the processing}
spec.summary = %q{Delay the processing}
spec.homepage = ""
spec.license = "MIT"
spec.files = `git ls-files`.split($/)
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ["lib"]
spec.add_development_dependency "bundler", "~> 1.2.3"
spec.add_development_dependency "rake"
end
# -*- coding: utf-8 -*-
require "delayer/version"
require "delayer/error"
require "delayer/extend"
require "delayer/procedure"
require "delayer/priority"
require "monitor"
module Delayer
class << self
attr_accessor :default
def included(klass)
klass.extend Extend
end
# Generate new Delayer class.
# ==== Args
# [options]
# Hash
# expire :: processing expire (secs, 0=unlimited)
# priority :: priorities
# default :: default priotity
# ==== Return
# A new class
def generate_class(options = {})
if options[:priority]
Class.new do
include Priority
@expire = options[:expire] || 0
@priorities = options[:priority]
@default_priority = options[:default]
end
else
Class.new do
include ::Delayer
@expire = options[:expire] || 0
end
end
end
def method_missing(*args, &proc)
(@default ||= generate_class).__send__(*args, &proc)
end
end
def initialize(*args)
super
@procedure = Procedure.new(self, &Proc.new)
end
# Cancel this job
# ==== Exception
# Delayer::AlreadyExecutedError :: if already called run()
# ==== Return
# self
def cancel
@procedure.cancel
self
end
end
# -*- coding: utf-8 -*-
module Delayer
class Error < ::StandardError; end
class TooLate < Error; end
class AlreadyExecutedError < TooLate; end
class AlreadyCanceledError < TooLate; end
class AlreadyRunningError < TooLate; end
class InvalidPriorityError < Error; end
def self.StateError(state)
case state
when :run
AlreadyRunningError
when :done
AlreadyExecutedError
when :cancel
AlreadyCanceledError
end
end
end
# -*- coding: utf-8 -*-
module Delayer
module Extend
attr_accessor :expire
attr_reader :exception
def self.extended(klass)
klass.class_eval do
@first_pointer = @last_pointer = nil
@busy = false
@expire = 0
@remain_hook = nil
@exception = nil
@remain_received = false
@lock = Mutex.new
end
end
# Run registered jobs.
# ==== Args
# [current_expire] expire for processing (secs, 0=unexpired)
# ==== Return
# self
def run(current_expire = @expire)
if 0 == current_expire
run_once while not empty?
else
@end_time = Time.new.to_f + @expire
run_once while not(empty?) and @end_time >= Time.new.to_f
@end_time = nil
end
if @remain_hook
@remain_received = !empty?
@remain_hook.call if @remain_received
end
rescue Exception => e
@exception = e
raise e
end
def expire?
if defined?(@end_time) and @end_time
@end_time < Time.new.to_f
else
false
end
end
# Run a job and forward pointer.
# ==== Return
# self
def run_once
if @first_pointer
@busy = true
procedure = forward
procedure = forward while @first_pointer and procedure.canceled?
procedure.run unless procedure.canceled?
end
ensure
@busy = false
end
# Return if some jobs processing now.
# ==== Args
# [args]
# ==== Return
# true if Delayer processing job
def busy?
@busy
end
# Return true if no jobs has.
# ==== Return
# true if no jobs has.
def empty?
!@first_pointer
end
# Return remain jobs quantity.
# ==== Return
# Count of remain jobs
def size(node = @first_pointer)
if node
1 + size(node.next)
else
0
end
end
# register new job.
# ==== Args
# [procedure] job(Delayer::Procedure)
# ==== Return
# self
def register(procedure)
lock.synchronize do
if @last_pointer
@last_pointer = @last_pointer.break procedure
else
@last_pointer = @first_pointer = procedure
end
if @remain_hook and not @remain_received
@remain_received = true
@remain_hook.call
end
end
self
end
def register_remain_hook
@remain_hook = Proc.new
end
private
def forward
lock.synchronize do
prev = @first_pointer
@first_pointer = @first_pointer.next
@last_pointer = nil unless @first_pointer
prev
end
end
def lock
@lock
end
end
end
# -*- coding: utf-8 -*-
module Delayer
module Priority
attr_reader :priority
def self.included(klass)
klass.class_eval do
include ::Delayer
extend Extend
end
end
def initialize(priority = self.class.instance_eval{ @default_priority }, *args)
self.class.validate_priority priority
@priority = priority
super(*args)
end
module Extend
def self.extended(klass)
klass.class_eval do
@priority_pointer = {}
end
end
# register new job.
# ==== Args
# [procedure] job(Delayer::Procedure)
# ==== Return
# self
def register(procedure)
priority = procedure.delayer.priority
lock.synchronize do
last_pointer = get_prev_point(priority)
if last_pointer
@priority_pointer[priority] = last_pointer.break procedure
else
procedure.next = @first_pointer
@priority_pointer[priority] = @first_pointer = procedure
end
if @last_pointer
@last_pointer = @priority_pointer[priority]
end
if @remain_hook and not @remain_received
@remain_received = true
@remain_hook.call
end
end
self
end
def get_prev_point(priority)
if @priority_pointer[priority]
@priority_pointer[priority]
else
next_index = @priorities.index(priority) - 1
get_prev_point @priorities[next_index] if 0 <= next_index
end
end
def validate_priority(symbol)
unless @priorities.include? symbol
raise Delayer::InvalidPriorityError, "undefined priority '#{symbol}'"
end
end
private
def forward
lock.synchronize do
prev = @first_pointer
@first_pointer = @first_pointer.next
@last_pointer = nil unless @first_pointer
@priority_pointer.each do |priority, pointer|
@priority_pointer[priority] = @first_pointer if prev == pointer
end
prev
end
end
end
end
end
# -*- coding: utf-8 -*-
module Delayer
class Procedure
attr_reader :state, :delayer
attr_accessor :next
def initialize(delayer, &proc)
@delayer, @proc = delayer, proc
@state = :stop
@next = nil
@delayer.class.register(self)
end
# Run a process
# ==== Exception
# Delayer::TooLate :: if already called run()
# ==== Return
# node
def run
unless :stop == @state
raise Delayer::StateError(@state), "call twice Delayer::Procedure"
end
@state = :run
@proc.call
@state = :done
@proc = nil
end
# Cancel this job
# ==== Exception
# Delayer::TooLate :: if already called run()
# ==== Return
# self
def cancel
unless :stop == @state
raise Delayer::StateError(@state), "cannot cancel Delayer::Procedure"
end
@state = :cancel
self
end
# Return true if canceled this task
# ==== Return
# true if canceled this task
def canceled?
:cancel == @state
end
# insert node between self and self.next
# ==== Args
# [node] insertion
# ==== Return
# node
def break(node)
tail = @next
@next = node
node.next = tail
node
end
end
end
module Delayer
VERSION = "0.0.2"
end
--- !ruby/object:Gem::Specification
name: delayer
version: !ruby/object:Gem::Version
version: 0.0.2
prerelease:
platform: ruby
authors:
- Toshiaki Asai
autorequire:
bindir: bin
cert_chain: []
date: 2013-06-12 00:00:00.000000000 Z
dependencies:
- !ruby/object:Gem::Dependency
name: bundler
requirement: &8375180 !ruby/object:Gem::Requirement
none: false
requirements:
- - ~>
- !ruby/object:Gem::Version
version: 1.2.3
type: :development
prerelease: false
version_requirements: *8375180
- !ruby/object:Gem::Dependency
name: rake
requirement: &8374760 !ruby/object:Gem::Requirement
none: false
requirements:
- - ! '>='
- !ruby/object:Gem::Version
version: '0'
type: :development
prerelease: false
version_requirements: *8374760
description: Delay the processing
email:
- toshi.alternative@gmail.com
executables: []
extensions: []
extra_rdoc_files: []
files:
- .gitignore
- Gemfile
- LICENSE.txt
- README.md
- Rakefile
- delayer.gemspec
- lib/delayer.rb
- lib/delayer/error.rb
- lib/delayer/extend.rb
- lib/delayer/priority.rb
- lib/delayer/procedure.rb
- lib/delayer/version.rb
- test/test_delayer.rb
- test/test_priority.rb
homepage: ''
licenses:
- MIT
post_install_message:
rdoc_options: []
require_paths:
- lib
required_ruby_version: !ruby/object:Gem::Requirement
none: false
requirements:
- - ! '>='
- !ruby/object:Gem::Version
version: '0'
required_rubygems_version: !ruby/object:Gem::Requirement
none: false
requirements:
- - ! '>='
- !ruby/object:Gem::Version
version: '0'
requirements: []
rubyforge_project:
rubygems_version: 1.8.11
signing_key:
specification_version: 3
summary: Delay the processing
test_files:
- test/test_delayer.rb
- test/test_priority.rb
# -*- coding: utf-8 -*-
require 'rubygems'
require 'bundler/setup'
require 'test/unit'
require 'delayer'
class TestDelayer < Test::Unit::TestCase
def setup
Delayer.default = nil
end
def test_delayed
delayer = Delayer.generate_class
a = 0
delayer.new { a = 1 }
assert_equal(0, a)
delayer.run
assert_equal(1, a)
end
def test_default
a = 0
Delayer.new { a = 1 }
assert_equal(0, a)
Delayer.run
assert_equal(1, a)
end
def test_timelimited
delayer = Delayer.generate_class(expire: 0.01)
a = 0
delayer.new { sleep 0.1 }
delayer.new { a = 1 }
assert_equal(0, a)
delayer.run
assert_equal(0, a)
delayer.run
assert_equal(1, a)
end
def test_busy
delayer = Delayer.generate_class
a = false
delayer.new { a = delayer.busy? }
assert_equal(false, a)
assert_equal(false, delayer.busy?)
delayer.run
assert_equal(false, delayer.busy?)
assert_equal(true, a)
end
def test_empty
delayer = Delayer.generate_class
a = false
delayer.new { a = delayer.empty? }
assert_equal(false, a)
assert_equal(false, delayer.empty?)
delayer.run
assert_equal(true, delayer.empty?)
assert_equal(true, a)
end
def test_size
delayer = Delayer.generate_class
a = 0
assert_equal(0, delayer.size)
delayer.new { a += 1 }
assert_equal(1, delayer.size)
delayer.new { a += 1 }
delayer.new { a += 1 }
assert_equal(3, delayer.size)
delayer.run
assert_equal(0, delayer.size)
end
def test_cancel_begin
delayer = Delayer.generate_class
a = 0
d = delayer.new { a += 1 }
delayer.new { a += 2 }
delayer.new { a += 4 }
assert_equal(0, a)
d.cancel
delayer.run
assert_equal(6, a)
end
def test_cancel_center
delayer = Delayer.generate_class
a = 0
delayer.new { a += 1 }
d = delayer.new { a += 2 }
delayer.new { a += 4 }
assert_equal(0, a)
d.cancel
delayer.run
assert_equal(5, a)
end
def test_cancel_end
delayer = Delayer.generate_class
a = 0
delayer.new { a += 1 }
delayer.new { a += 2 }
d = delayer.new { a += 4 }
assert_equal(0, a)
d.cancel
delayer.run
assert_equal(3, a)
end
def test_priority_asc
delayer = Delayer.generate_class(priority: [:high, :middle, :low],
default: :middle)
buffer = []
delayer.new(:high) { buffer << 1 }
delayer.new(:middle) { buffer << 2 }
delayer.new(:low) { buffer << 3 }
delayer.run
assert_equal([1,2,3], buffer)
end
def test_priority_desc
delayer = Delayer.generate_class(priority: [:high, :middle, :low],
default: :middle)
buffer = []
delayer.new(:low) { buffer << 3 }
delayer.new(:middle) { buffer << 2 }
delayer.new(:high) { buffer << 1 }
delayer.run
assert_equal([1,2,3], buffer)
end
def test_priority_complex
delayer = Delayer.generate_class(priority: [:high, :middle, :low],
default: :middle)
buffer = []
delayer.new(:high) { buffer << 1 }
delayer.new(:middle) { buffer << 2 }
delayer.new(:low) { buffer << 3 }
delayer.new(:middle) { buffer << 4 }
delayer.new(:high) { buffer << 5 }
delayer.new(:middle) { buffer << 6 }
delayer.new(:low) { buffer << 7 }
delayer.new(:middle) { buffer << 8 }
delayer.new(:high) { buffer << 9 }
delayer.run
assert_equal([1,5,9,2,4,6,8,3,7], buffer)
buffer = []
delayer.new(:high) { buffer << 1 }
delayer.new(:low) { buffer << 2 }
delayer.new(:high) { buffer << 3 }
delayer.new(:low) { buffer << 4 }
delayer.run
assert_equal([1,3,2,4], buffer)
end
def test_priority_cancel_begin
delayer = Delayer.generate_class(priority: [:high, :middle, :low],
default: :middle)
a = 0
d = delayer.new { a += 1 }
delayer.new { a += 2 }
delayer.new { a += 4 }
assert_equal(0, a)
d.cancel
delayer.run
assert_equal(6, a)
a = 0