procrastinate 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +23 -0
- data/README +76 -0
- data/Rakefile +69 -0
- data/lib/procrastinate.rb +10 -0
- data/lib/procrastinate/dispatch_strategies.rb +9 -0
- data/lib/procrastinate/dispatch_strategy/simple.rb +47 -0
- data/lib/procrastinate/dispatch_strategy/throttled.rb +25 -0
- data/lib/procrastinate/dispatcher.rb +164 -0
- data/lib/procrastinate/lock.rb +34 -0
- data/lib/procrastinate/proxy.rb +20 -0
- data/lib/procrastinate/runtime.rb +13 -0
- data/lib/procrastinate/scheduler.rb +41 -0
- data/lib/procrastinate/tasks.rb +16 -0
- metadata +105 -0
data/LICENSE
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
Copyright (c) 2010 Kaspar Schiess, Patrick Marchi
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person
|
5
|
+
obtaining a copy of this software and associated documentation
|
6
|
+
files (the "Software"), to deal in the Software without
|
7
|
+
restriction, including without limitation the rights to use,
|
8
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the
|
10
|
+
Software is furnished to do so, subject to the following
|
11
|
+
conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
18
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
20
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
21
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
22
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
23
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
INTRO
|
2
|
+
|
3
|
+
'procrastinate' does the process handling so you don't have to. It leaves you
|
4
|
+
to concentrate on what to run when, not orchestration of low level details.
|
5
|
+
|
6
|
+
This library will be ideal for quickly scheduling of a lot of long running
|
7
|
+
tasks. You can easily control how many processes are run at any time. To get
|
8
|
+
at/cron like scheduling, combine this library with 'rufus-scheduler'.
|
9
|
+
|
10
|
+
SYNOPSIS
|
11
|
+
|
12
|
+
require 'procrastinate'
|
13
|
+
include Procrastinate
|
14
|
+
|
15
|
+
class Worker
|
16
|
+
def do_work
|
17
|
+
puts "> Starting work in process #{Process.pid}"
|
18
|
+
sleep 2
|
19
|
+
puts "< Work completed in process #{Process.pid}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
scheduler = Scheduler.start(DispatchStrategy::Throttled.new(5))
|
24
|
+
worker = scheduler.create_proxy(Worker.new)
|
25
|
+
|
26
|
+
10.times do
|
27
|
+
worker.do_work
|
28
|
+
end
|
29
|
+
|
30
|
+
scheduler.shutdown
|
31
|
+
|
32
|
+
The above example will output something like
|
33
|
+
|
34
|
+
> Starting work in process 6651
|
35
|
+
> Starting work in process 6652
|
36
|
+
> Starting work in process 6653
|
37
|
+
> Starting work in process 6654
|
38
|
+
> Starting work in process 6655
|
39
|
+
< Work completed in process 6651
|
40
|
+
< Work completed in process 6652
|
41
|
+
< Work completed in process 6653
|
42
|
+
< Work completed in process 6654
|
43
|
+
< Work completed in process 6655
|
44
|
+
> Starting work in process 6659
|
45
|
+
> Starting work in process 6660
|
46
|
+
> Starting work in process 6656
|
47
|
+
> Starting work in process 6657
|
48
|
+
> Starting work in process 6658
|
49
|
+
< Work completed in process 6659
|
50
|
+
< Work completed in process 6660
|
51
|
+
< Work completed in process 6656
|
52
|
+
< Work completed in process 6657
|
53
|
+
< Work completed in process 6658
|
54
|
+
|
55
|
+
COMPATIBILITY
|
56
|
+
|
57
|
+
This library runs with at least Ruby 1.8.7 and Ruby 1.9. For running it with
|
58
|
+
Ruby 1.9, you need to patch it with the patch found at
|
59
|
+
|
60
|
+
http://redmine.ruby-lang.org/issues/show/1525
|
61
|
+
|
62
|
+
or run it with at least r25844 of Ruby trunk.
|
63
|
+
|
64
|
+
KNOWN BUGS
|
65
|
+
|
66
|
+
Due to the way we handle signal traps, you cannot start more than one
|
67
|
+
Scheduler. We might allow that in the future.
|
68
|
+
|
69
|
+
STATUS
|
70
|
+
|
71
|
+
This code is not released as a gem yet. Once we iron out the bugs we'll
|
72
|
+
release it as 0.1.0. Alpha quality code.
|
73
|
+
|
74
|
+
Please see the LICENSE file for license information.
|
75
|
+
|
76
|
+
(c) 2010 Kaspar Schiess, Patrick Marchi
|
data/Rakefile
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
Rspec::Core::RakeTask.new
|
4
|
+
task :default => :spec
|
5
|
+
|
6
|
+
task :default => :spec
|
7
|
+
|
8
|
+
require "rubygems"
|
9
|
+
require "rake/gempackagetask"
|
10
|
+
require "rake/rdoctask"
|
11
|
+
|
12
|
+
# This builds the actual gem. For details of what all these options
|
13
|
+
# mean, and other ones you can add, check the documentation here:
|
14
|
+
#
|
15
|
+
# http://rubygems.org/read/chapter/20
|
16
|
+
#
|
17
|
+
spec = Gem::Specification.new do |s|
|
18
|
+
|
19
|
+
# Change these as appropriate
|
20
|
+
s.name = "procrastinate"
|
21
|
+
s.version = "0.1.0"
|
22
|
+
s.summary = "Framework to run tasks in separate processes."
|
23
|
+
s.authors = ['Kaspar Schiess', 'Patrick Marchi']
|
24
|
+
s.email = ['kaspar.schiess@absurd.li', 'mail@patrickmarchi.ch']
|
25
|
+
s.homepage = "http://github.com/kschiess/procrastinate"
|
26
|
+
|
27
|
+
s.has_rdoc = true
|
28
|
+
s.extra_rdoc_files = %w(README)
|
29
|
+
s.rdoc_options = %w(--main README)
|
30
|
+
|
31
|
+
# Add any extra files to include in the gem
|
32
|
+
s.files = %w(LICENSE Rakefile README) + Dir.glob("{spec,lib/**/*}")
|
33
|
+
s.require_paths = ["lib"]
|
34
|
+
|
35
|
+
# If you want to depend on other gems, add them here, along with any
|
36
|
+
# relevant versions
|
37
|
+
# s.add_dependency("blankslate", "~> 2.0")
|
38
|
+
|
39
|
+
# If your tests use any gems, include them here
|
40
|
+
s.add_development_dependency("rspec")
|
41
|
+
s.add_development_dependency("flexmock")
|
42
|
+
end
|
43
|
+
|
44
|
+
# This task actually builds the gem. We also regenerate a static
|
45
|
+
# .gemspec file, which is useful if something (i.e. GitHub) will
|
46
|
+
# be automatically building a gem for this project. If you're not
|
47
|
+
# using GitHub, edit as appropriate.
|
48
|
+
#
|
49
|
+
# To publish your gem online, install the 'gemcutter' gem; Read more
|
50
|
+
# about that here: http://gemcutter.org/pages/gem_docs
|
51
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
52
|
+
pkg.gem_spec = spec
|
53
|
+
|
54
|
+
# Generate the gemspec file for github.
|
55
|
+
file = File.dirname(__FILE__) + "/#{spec.name}.gemspec"
|
56
|
+
File.open(file, "w") {|f| f << spec.to_ruby }
|
57
|
+
end
|
58
|
+
|
59
|
+
# Generate documentation
|
60
|
+
Rake::RDocTask.new do |rd|
|
61
|
+
rd.main = "README"
|
62
|
+
rd.rdoc_files.include("README", "lib/**/*.rb")
|
63
|
+
rd.rdoc_dir = "rdoc"
|
64
|
+
end
|
65
|
+
|
66
|
+
desc 'Clear out RDoc and generated packages'
|
67
|
+
task :clean => [:clobber_rdoc, :clobber_package] do
|
68
|
+
rm "#{spec.name}.gemspec"
|
69
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
|
2
|
+
module Procrastinate; end
|
3
|
+
|
4
|
+
require 'procrastinate/runtime'
|
5
|
+
require 'procrastinate/lock'
|
6
|
+
require 'procrastinate/dispatch_strategies'
|
7
|
+
require 'procrastinate/tasks'
|
8
|
+
require 'procrastinate/proxy'
|
9
|
+
require 'procrastinate/dispatcher'
|
10
|
+
require 'procrastinate/scheduler'
|
@@ -0,0 +1,9 @@
|
|
1
|
+
|
2
|
+
module Procrastinate::DispatchStrategy
|
3
|
+
# Raised when you request a shutdown and then schedule new work.
|
4
|
+
#
|
5
|
+
class ShutdownRequested < StandardError; end
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'procrastinate/dispatch_strategy/simple'
|
9
|
+
require 'procrastinate/dispatch_strategy/throttled'
|
@@ -0,0 +1,47 @@
|
|
1
|
+
|
2
|
+
require 'thread'
|
3
|
+
|
4
|
+
class Procrastinate::DispatchStrategy::Simple
|
5
|
+
attr_reader :queue
|
6
|
+
|
7
|
+
# Client thread
|
8
|
+
def initialize
|
9
|
+
@queue = Queue.new
|
10
|
+
@shutdown_requested = false
|
11
|
+
end
|
12
|
+
|
13
|
+
def shutdown_requested?
|
14
|
+
@shutdown_requested
|
15
|
+
end
|
16
|
+
|
17
|
+
# Client thread
|
18
|
+
def schedule(task)
|
19
|
+
raise ::ShutdownRequested if shutdown_requested?
|
20
|
+
|
21
|
+
queue.push task
|
22
|
+
end
|
23
|
+
|
24
|
+
# Dispatcher thread
|
25
|
+
def spawn_new_workers(dispatcher)
|
26
|
+
# Spawn tasks
|
27
|
+
spawn(dispatcher) while should_spawn?
|
28
|
+
|
29
|
+
# If the queue is empty now, maybe shutdown the dispatcher
|
30
|
+
dispatcher.request_stop if shutdown_requested? && queue.empty?
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
# Spawn a new task from the job queue.
|
35
|
+
# Dispatcher thread
|
36
|
+
#
|
37
|
+
def spawn(dispatcher, &block)
|
38
|
+
dispatcher.spawn(queue.pop, &block)
|
39
|
+
end
|
40
|
+
def should_spawn?
|
41
|
+
not queue.empty?
|
42
|
+
end
|
43
|
+
|
44
|
+
def shutdown
|
45
|
+
@shutdown_requested = true
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
|
2
|
+
# A dispatcher strategy that throttles tasks starting and ensures that no
|
3
|
+
# more than limit processes run concurrently.
|
4
|
+
#
|
5
|
+
class Procrastinate::DispatchStrategy::Throttled < Procrastinate::DispatchStrategy::Simple
|
6
|
+
attr_reader :limit, :current
|
7
|
+
|
8
|
+
# Client thread
|
9
|
+
def initialize(limit)
|
10
|
+
super()
|
11
|
+
|
12
|
+
@limit = limit
|
13
|
+
@current = 0
|
14
|
+
end
|
15
|
+
|
16
|
+
# Dispatcher thread
|
17
|
+
def spawn(dispatcher)
|
18
|
+
super(dispatcher) { @current -= 1 }
|
19
|
+
@current += 1
|
20
|
+
end
|
21
|
+
def should_spawn?
|
22
|
+
super &&
|
23
|
+
current < limit
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
|
2
|
+
# Dispatches and handles tasks and task completion. Only low level unixy
|
3
|
+
# manipulation here, no strategy. The only method you should call from the
|
4
|
+
# outside is #wakeup.
|
5
|
+
#
|
6
|
+
class Procrastinate::Dispatcher
|
7
|
+
# The dispatcher runs in its own thread, which sleeps most of the time.
|
8
|
+
attr_reader :thread
|
9
|
+
|
10
|
+
# This pipe is used to wait for events in the master process.
|
11
|
+
attr_reader :control_pipe
|
12
|
+
|
13
|
+
# A hash of <pid, callback> that contains callbacks for all the child
|
14
|
+
# processes we spawn. Once the process is complete, the callback is called
|
15
|
+
# in the dispatcher/strategy's thread.
|
16
|
+
attr_reader :handlers
|
17
|
+
|
18
|
+
# The strategy for dispatching new tasks. Makes all the decisions about
|
19
|
+
# when to launch what process.
|
20
|
+
#
|
21
|
+
attr_reader :strategy
|
22
|
+
|
23
|
+
def initialize(strategy)
|
24
|
+
@strategy = strategy
|
25
|
+
|
26
|
+
@control_pipe = IO.pipe
|
27
|
+
@handlers = {}
|
28
|
+
@stop_requested = false
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.start(strategy)
|
32
|
+
new(strategy).tap do |dispatcher|
|
33
|
+
dispatcher.start
|
34
|
+
end
|
35
|
+
end
|
36
|
+
def start
|
37
|
+
register_signals
|
38
|
+
start_thread
|
39
|
+
end
|
40
|
+
|
41
|
+
# Called from anywhere, will complete all running tasks and stop the
|
42
|
+
# dispatcher.
|
43
|
+
#
|
44
|
+
def stop
|
45
|
+
request_stop
|
46
|
+
join
|
47
|
+
unregister_signals
|
48
|
+
end
|
49
|
+
|
50
|
+
# Called from the dispatcher thread, will cause the dispatcher to wait on
|
51
|
+
# all running tasks and then stop dispatching.
|
52
|
+
#
|
53
|
+
def request_stop
|
54
|
+
@stop_requested = true
|
55
|
+
wakeup
|
56
|
+
end
|
57
|
+
|
58
|
+
def stop_requested?
|
59
|
+
@stop_requested
|
60
|
+
end
|
61
|
+
|
62
|
+
def register_signals
|
63
|
+
trap('CHLD') { wakeup }
|
64
|
+
end
|
65
|
+
def unregister_signals
|
66
|
+
trap('CHLD', 'DEFAULT')
|
67
|
+
end
|
68
|
+
|
69
|
+
def start_thread
|
70
|
+
@thread = Thread.new do
|
71
|
+
Thread.current.abort_on_exception = true
|
72
|
+
|
73
|
+
# Loop until someone requests a shutdown.
|
74
|
+
loop do
|
75
|
+
wait_for_event
|
76
|
+
reap_workers
|
77
|
+
|
78
|
+
break if stop_requested?
|
79
|
+
|
80
|
+
strategy.spawn_new_workers(self)
|
81
|
+
end
|
82
|
+
|
83
|
+
wait_for_all_childs
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def wait_for_event
|
88
|
+
# Returns array<ready_for_read, ..., ...>
|
89
|
+
IO.select([control_pipe.first], nil, nil)
|
90
|
+
|
91
|
+
# Consume the data (not important)
|
92
|
+
control_pipe.first.read_nonblock(1024)
|
93
|
+
rescue Errno::EAGAIN, Errno::EINTR
|
94
|
+
end
|
95
|
+
|
96
|
+
# Wake up the dispatcher thread.
|
97
|
+
#
|
98
|
+
def wakeup
|
99
|
+
control_pipe.last.write '.'
|
100
|
+
# rescue IOError
|
101
|
+
# Ignore:
|
102
|
+
end
|
103
|
+
|
104
|
+
# Waits until the dispatcher completes its work. If you don't initiate a
|
105
|
+
# shutdown, this may be forever.
|
106
|
+
#
|
107
|
+
def join
|
108
|
+
@thread.join
|
109
|
+
end
|
110
|
+
|
111
|
+
# Calls completion handlers for all the childs that have now exited.
|
112
|
+
#
|
113
|
+
def reap_workers
|
114
|
+
loop do
|
115
|
+
child_pid, status = Process.waitpid2(-1, Process::WNOHANG)
|
116
|
+
break unless child_pid
|
117
|
+
|
118
|
+
# Trigger the completion callback
|
119
|
+
handler = handlers.delete(child_pid)
|
120
|
+
handler.call if handler
|
121
|
+
end
|
122
|
+
rescue Errno::ECHILD
|
123
|
+
# Ignored: Child status has been reaped by someone else
|
124
|
+
end
|
125
|
+
|
126
|
+
# Spawns a process to work on +task+. If a block is given, it is called
|
127
|
+
# when the task completes.
|
128
|
+
#
|
129
|
+
# Example:
|
130
|
+
#
|
131
|
+
# spawn(wi) { puts "Task is complete" }
|
132
|
+
#
|
133
|
+
def spawn(task, &completion_handler)
|
134
|
+
pid = fork do
|
135
|
+
cleanup
|
136
|
+
|
137
|
+
task.run
|
138
|
+
|
139
|
+
exit! # this seems to be needed to avoid rspecs cleanup tasks
|
140
|
+
end
|
141
|
+
|
142
|
+
handlers[pid] = completion_handler
|
143
|
+
end
|
144
|
+
|
145
|
+
# Gets executed in child process to clean up file handles and pipes that the
|
146
|
+
# master holds.
|
147
|
+
#
|
148
|
+
def cleanup
|
149
|
+
# Children dont need the parents signal handler
|
150
|
+
trap(:CHLD, 'DEFAULT')
|
151
|
+
|
152
|
+
# The child doesn't need the control pipe for now.
|
153
|
+
control_pipe.each { |io| io.close }
|
154
|
+
end
|
155
|
+
|
156
|
+
# Waits for all childs to complete.
|
157
|
+
#
|
158
|
+
def wait_for_all_childs
|
159
|
+
until handlers.empty?
|
160
|
+
sleep 0.01
|
161
|
+
reap_workers
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
|
2
|
+
# A file based lock below a base directory that is identified by Lock.base.
|
3
|
+
#
|
4
|
+
class Procrastinate::Lock
|
5
|
+
class << self
|
6
|
+
# Base directory for all lock files that are created.
|
7
|
+
#
|
8
|
+
attr_accessor :base
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :name # name of the lock
|
12
|
+
attr_reader :file # file handle of the lock
|
13
|
+
def initialize(name)
|
14
|
+
@name = name
|
15
|
+
@file = File.open(
|
16
|
+
File.join(
|
17
|
+
self.class.base, name),
|
18
|
+
'w+')
|
19
|
+
end
|
20
|
+
|
21
|
+
def acquire
|
22
|
+
file.flock File::LOCK_EX
|
23
|
+
end
|
24
|
+
def release
|
25
|
+
file.flock File::LOCK_UN
|
26
|
+
end
|
27
|
+
|
28
|
+
def synchronize
|
29
|
+
acquire
|
30
|
+
yield
|
31
|
+
ensure
|
32
|
+
release
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
class Procrastinate::Proxy
|
3
|
+
def initialize(worker, scheduler)
|
4
|
+
@worker = worker
|
5
|
+
@scheduler = scheduler
|
6
|
+
end
|
7
|
+
|
8
|
+
def respond_to?(name)
|
9
|
+
@worker.respond_to?(name)
|
10
|
+
end
|
11
|
+
|
12
|
+
def method_missing(name, *args, &block)
|
13
|
+
if respond_to? name
|
14
|
+
@scheduler.schedule(
|
15
|
+
Procrastinate::Task::MethodCall.new(@worker, name, args, block))
|
16
|
+
else
|
17
|
+
super
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
|
2
|
+
# An instance of this class obtained from the scheduler can be used to perform
|
3
|
+
# synchronisation and other communication with the scheduler.
|
4
|
+
#
|
5
|
+
class Procrastinate::Runtime
|
6
|
+
def lock(name)
|
7
|
+
lock = Procrastinate::Lock.new(name)
|
8
|
+
|
9
|
+
lock.synchronize do
|
10
|
+
yield
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class Procrastinate::Scheduler
|
2
|
+
attr_reader :dispatcher
|
3
|
+
attr_reader :strategy
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@shutdown_requested = false
|
7
|
+
end
|
8
|
+
|
9
|
+
# Start a new scheduler
|
10
|
+
def self.start(strategy=nil)
|
11
|
+
new.start(strategy)
|
12
|
+
end
|
13
|
+
def start(strategy=nil)
|
14
|
+
@strategy = strategy || Procrastinate::DispatchStrategy::Simple.new
|
15
|
+
@dispatcher = Procrastinate::Dispatcher.start(@strategy)
|
16
|
+
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def create_proxy(worker)
|
21
|
+
return Procrastinate::Proxy.new(worker, self)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns a runtime linked to this scheduler.
|
25
|
+
#
|
26
|
+
def runtime
|
27
|
+
Procrastinate::Runtime.new
|
28
|
+
end
|
29
|
+
|
30
|
+
# Called by the proxy to schedule work.
|
31
|
+
#
|
32
|
+
def schedule(task)
|
33
|
+
strategy.schedule(task)
|
34
|
+
dispatcher.wakeup
|
35
|
+
end
|
36
|
+
|
37
|
+
def shutdown
|
38
|
+
strategy.shutdown
|
39
|
+
dispatcher.join
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Procrastinate::Task
|
2
|
+
# Constructs an object of type +klass+ and calls a method on it.
|
3
|
+
#
|
4
|
+
class MethodCall
|
5
|
+
def initialize(instance, method, arguments, block)
|
6
|
+
@instance = instance
|
7
|
+
@method = method
|
8
|
+
@arguments = arguments
|
9
|
+
@block = block
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
@instance.send(@method, *@arguments, &@block)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
metadata
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: procrastinate
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Kaspar Schiess
|
13
|
+
- Patrick Marchi
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-12-10 00:00:00 +01:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: rspec
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
version: "0"
|
32
|
+
type: :development
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: flexmock
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
segments:
|
43
|
+
- 0
|
44
|
+
version: "0"
|
45
|
+
type: :development
|
46
|
+
version_requirements: *id002
|
47
|
+
description:
|
48
|
+
email:
|
49
|
+
- kaspar.schiess@absurd.li
|
50
|
+
- mail@patrickmarchi.ch
|
51
|
+
executables: []
|
52
|
+
|
53
|
+
extensions: []
|
54
|
+
|
55
|
+
extra_rdoc_files:
|
56
|
+
- README
|
57
|
+
files:
|
58
|
+
- LICENSE
|
59
|
+
- Rakefile
|
60
|
+
- README
|
61
|
+
- lib/procrastinate/dispatch_strategies.rb
|
62
|
+
- lib/procrastinate/dispatch_strategy/simple.rb
|
63
|
+
- lib/procrastinate/dispatch_strategy/throttled.rb
|
64
|
+
- lib/procrastinate/dispatcher.rb
|
65
|
+
- lib/procrastinate/lock.rb
|
66
|
+
- lib/procrastinate/proxy.rb
|
67
|
+
- lib/procrastinate/runtime.rb
|
68
|
+
- lib/procrastinate/scheduler.rb
|
69
|
+
- lib/procrastinate/tasks.rb
|
70
|
+
- lib/procrastinate.rb
|
71
|
+
has_rdoc: true
|
72
|
+
homepage: http://github.com/kschiess/procrastinate
|
73
|
+
licenses: []
|
74
|
+
|
75
|
+
post_install_message:
|
76
|
+
rdoc_options:
|
77
|
+
- --main
|
78
|
+
- README
|
79
|
+
require_paths:
|
80
|
+
- lib
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
82
|
+
none: false
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
segments:
|
87
|
+
- 0
|
88
|
+
version: "0"
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
segments:
|
95
|
+
- 0
|
96
|
+
version: "0"
|
97
|
+
requirements: []
|
98
|
+
|
99
|
+
rubyforge_project:
|
100
|
+
rubygems_version: 1.3.7
|
101
|
+
signing_key:
|
102
|
+
specification_version: 3
|
103
|
+
summary: Framework to run tasks in separate processes.
|
104
|
+
test_files: []
|
105
|
+
|