clockwork 0.7.2 → 0.7.3
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.
- checksums.yaml +4 -4
- data/README.md +124 -0
- data/clockwork.gemspec +1 -1
- data/lib/clockwork/manager_with_database_tasks.rb +87 -0
- data/test/manager_with_database_tasks_test.rb +190 -0
- metadata +24 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fae14bd8566db2cb7c3be8c5c9625b01dc66ae00
|
4
|
+
data.tar.gz: ecbed0c0a9cf6017a0e62f62da9cfb67112624da
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c75686b464777f74d87ca6b15d3ac1d57ee83f190e22d32c6b585fecb86c5192575f90d6151588e9454f7d3f8054bd1c63f3fa4d6a3a067f1d93bbd10a14fe4a
|
7
|
+
data.tar.gz: c68c69e967a99422394197ed570c2c0ce0968e73a7495ec44a55158b99a6c6450a84fd3160d4707d95c5385ddc00b584f4fb350a6b0566b44222f8201215c504
|
data/README.md
CHANGED
@@ -103,6 +103,120 @@ every(1.hour, 'feeds.refresh') { Feed.send_later(:refresh) }
|
|
103
103
|
every(1.day, 'reminders.send', :at => '01:30') { Reminder.send_later(:send_reminders) }
|
104
104
|
```
|
105
105
|
|
106
|
+
Use with database tasks
|
107
|
+
-----------------------
|
108
|
+
|
109
|
+
You can dynamically add tasks from a database to be scheduled along with the regular events in clock.rb.
|
110
|
+
|
111
|
+
To do this, use the `sync_database_tasks` method call:
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
require 'clockwork'
|
115
|
+
require 'clockwork/manager_with_database_tasks'
|
116
|
+
require_relative './config/boot'
|
117
|
+
require_relative './config/environment'
|
118
|
+
|
119
|
+
module Clockwork
|
120
|
+
|
121
|
+
# required to enable database syncing support
|
122
|
+
Clockwork.manager = ManagerWithDatabaseTasks.new
|
123
|
+
|
124
|
+
sync_database_tasks model: MyScheduledTask, every: 1.minute do |instance_job_name|
|
125
|
+
# Where your model will acts as a worker:
|
126
|
+
id = instance_job_name.split(':').last
|
127
|
+
task = MyScheduledTask.find(id)
|
128
|
+
task.perform_async
|
129
|
+
|
130
|
+
# Or, e.g. if your queue system just needs job names
|
131
|
+
# Stalker.enqueue(instance_job_name)
|
132
|
+
end
|
133
|
+
|
134
|
+
[...other tasks if you have...]
|
135
|
+
|
136
|
+
end
|
137
|
+
```
|
138
|
+
|
139
|
+
This tells clockwork to fetch all MyScheduledTask instances from the database, and create an event for each, configured based on the instances' `frequency`, `name`, and `at` methods. It also says to reload the tasks from the database every 1.minute - we need to frequently do this as they could have changed (but you can choose a sensible reload frequency by changing the `every:` option). Note that the database sync event will always run on the minute-boundary (i.e. HH:MM::00), and that events which have been synced from the database won't be run in that same clock cycle (this prevents a task from the database being run twice).
|
140
|
+
|
141
|
+
Rails ActiveRecord models are a perfect candidate for the model class, but you could use something else. The only requirements are:
|
142
|
+
|
143
|
+
1. the class responds to `all` returning an array of instances from the database
|
144
|
+
2. the instances returned respond to:
|
145
|
+
`frequency` returning the how frequently (in seconds) the database task should be run
|
146
|
+
`name` returning the task's job name (this is what gets passed into the block above)
|
147
|
+
`at` return nil or '' if not using :at, or any acceptable clockwork :at string
|
148
|
+
|
149
|
+
Here's an example of one way of setting up your ActiveRecord models, using Sidekiq for background tasks, and making the model class a worker:
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
# db/migrate/20140302220659_create_frequency_periods.rb
|
153
|
+
class CreateFrequencyPeriods < ActiveRecord::Migration
|
154
|
+
def change
|
155
|
+
create_table :frequency_periods do |t|
|
156
|
+
t.string :name
|
157
|
+
|
158
|
+
t.timestamps
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# 20140302221102_create_my_scheduled_tasks.rb
|
164
|
+
class CreateMyScheduledTasks < ActiveRecord::Migration
|
165
|
+
def change
|
166
|
+
create_table :my_scheduled_tasks do |t|
|
167
|
+
t.integer :frequency_quantity
|
168
|
+
t.references :frequency_period
|
169
|
+
t.string :at
|
170
|
+
|
171
|
+
t.timestamps
|
172
|
+
end
|
173
|
+
add_index :my_scheduled_tasks, :frequency_period_id
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# app/models/my_scheduled_task.rb
|
178
|
+
class MyScheduledTask < ActiveRecord::Base
|
179
|
+
include Sidekiq::Worker
|
180
|
+
|
181
|
+
belongs_to :frequency_period
|
182
|
+
attr_accessible :frequency_quantity, :frequency_period_id, :at
|
183
|
+
|
184
|
+
# Used by clockwork to schedule how frequently this task should be run
|
185
|
+
# Should be the intended number of seconds between executions
|
186
|
+
def frequency
|
187
|
+
frequency_quantity.send(frequency_period.name.pluralize)
|
188
|
+
end
|
189
|
+
|
190
|
+
# Used by clockwork to name this task internally for its logging
|
191
|
+
# Should return a reference for this task to be used in clockwork
|
192
|
+
# Include the instance ID if you want to be able to retrieve the
|
193
|
+
# model instance inside the sync_database_tasks block in clock.rb
|
194
|
+
def name
|
195
|
+
"Database_MyScheduledTask:#{id}"
|
196
|
+
end
|
197
|
+
|
198
|
+
# Method required by Sidekiq
|
199
|
+
def perform
|
200
|
+
# the task that will be performed in the background
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
# app/models/frequency_period.rb
|
205
|
+
class FrequencyPeriod < ActiveRecord::Base
|
206
|
+
attr_accessible :name
|
207
|
+
end
|
208
|
+
|
209
|
+
# db/seeds.rb
|
210
|
+
...
|
211
|
+
# creating the FrequencyPeriods
|
212
|
+
[:second, :minute, :hour, :day, :week, :month].each do |period|
|
213
|
+
FrequencyPeriod.create(name: period)
|
214
|
+
end
|
215
|
+
...
|
216
|
+
```
|
217
|
+
|
218
|
+
You could, of course, create a separate Sidekiq or DelayedJob worker class under app/workers, and simply use the model referenced by clockwork to trigger that worker to run asynchronously.
|
219
|
+
|
106
220
|
Event Parameters
|
107
221
|
----------
|
108
222
|
|
@@ -324,6 +438,16 @@ on(:after_tick) do
|
|
324
438
|
end
|
325
439
|
```
|
326
440
|
|
441
|
+
Finally, you can use tasks synchronised from a database as described in detail above:
|
442
|
+
|
443
|
+
```ruby
|
444
|
+
sync_database_tasks model: MyScheduledTask, every: 1.minute do |instance_job_name|
|
445
|
+
# what to do with each instance
|
446
|
+
end
|
447
|
+
```
|
448
|
+
|
449
|
+
You can use multiple `sync_database_tasks` if you wish, so long as you use different model classes for each (ActiveRecord Single Table Inheritance could be a good idea if you're doing this).
|
450
|
+
|
327
451
|
In production
|
328
452
|
-------------
|
329
453
|
|
data/clockwork.gemspec
CHANGED
@@ -0,0 +1,87 @@
|
|
1
|
+
module Clockwork
|
2
|
+
|
3
|
+
module Methods
|
4
|
+
def sync_database_tasks(options={}, &block)
|
5
|
+
Clockwork.manager.sync_database_tasks(options, &block)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
extend Methods
|
10
|
+
|
11
|
+
class ManagerWithDatabaseTasks < Manager
|
12
|
+
|
13
|
+
SECOND_TO_RUN_DATABASE_SYNC_AT = 0
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
super
|
17
|
+
@events_from_database = []
|
18
|
+
@next_sync_database_tasks_identifier = 0
|
19
|
+
end
|
20
|
+
|
21
|
+
def sync_database_tasks(options={}, &block)
|
22
|
+
[:model, :every].each do |option|
|
23
|
+
raise ArgumentError.new("requires :#{option} option") unless options.include?(option)
|
24
|
+
end
|
25
|
+
raise ArgumentError.new(":every must be greater or equal to 1.minute") if options[:every] < 1.minute
|
26
|
+
|
27
|
+
model = options[:model]
|
28
|
+
frequency = options[:every]
|
29
|
+
sync_task_id = get_sync_task_id
|
30
|
+
|
31
|
+
# Prevent database tasks from running in same cycle as the database sync,
|
32
|
+
# as this can lead to the same task being run twice
|
33
|
+
options_to_run_database_sync_in_own_cycle = { :if => lambda { |t| t.sec == SECOND_TO_RUN_DATABASE_SYNC_AT } }
|
34
|
+
|
35
|
+
# create event that syncs clockwork events with database events
|
36
|
+
every frequency, "sync_database_tasks_for_model_#{model}", options_to_run_database_sync_in_own_cycle do
|
37
|
+
reload_events_from_database sync_task_id, model, &block
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
protected
|
43
|
+
|
44
|
+
# sync_task_id's are used to group the database events from a particular sync_database_tasks call
|
45
|
+
# This method hands out the ids, incrementing the id to keep them unique.
|
46
|
+
def get_sync_task_id
|
47
|
+
current_sync_task_id = @next_sync_database_tasks_identifier
|
48
|
+
@next_sync_database_tasks_identifier += 1
|
49
|
+
current_sync_task_id
|
50
|
+
end
|
51
|
+
|
52
|
+
def reload_events_from_database(sync_task_id, model, &block)
|
53
|
+
@events_from_database[sync_task_id] = []
|
54
|
+
|
55
|
+
model.all.each do |db_task|
|
56
|
+
options = { from_database: true, :sync_task_id => sync_task_id }
|
57
|
+
options[:at] = db_task.at.split(',') unless db_task.at.blank?
|
58
|
+
|
59
|
+
# If database tasks can be scheduled in same clock cycle that database syncs occur
|
60
|
+
# then previous copy of database sync task will be stored and set to run (in #tick events variable)
|
61
|
+
# *before* we then delete all database tasks. This causes the task to be run at HH:00 (previous copy)
|
62
|
+
# and at HH:01 (newly fetched copy).
|
63
|
+
option_to_prevent_database_tasks_running_in_same_cycle_as_sync = { :if => lambda{|t| t.sec != SECOND_TO_RUN_DATABASE_SYNC_AT } }
|
64
|
+
every db_task.frequency,
|
65
|
+
db_task.name,
|
66
|
+
options.merge(option_to_prevent_database_tasks_running_in_same_cycle_as_sync),
|
67
|
+
&block
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def events_to_run(t)
|
74
|
+
(@events + @events_from_database.flatten).select{|event| event.run_now?(t) }
|
75
|
+
end
|
76
|
+
|
77
|
+
def register(period, job, block, options)
|
78
|
+
event = Event.new(self, period, job, block || handler, options)
|
79
|
+
if options[:from_database]
|
80
|
+
@events_from_database[options[:sync_task_id]] << event
|
81
|
+
else
|
82
|
+
@events << event
|
83
|
+
end
|
84
|
+
event
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
require File.expand_path('../../lib/clockwork', __FILE__)
|
2
|
+
require 'clockwork/manager_with_database_tasks'
|
3
|
+
require 'rubygems'
|
4
|
+
require 'contest'
|
5
|
+
require 'mocha/setup'
|
6
|
+
require 'time'
|
7
|
+
require 'active_support/time'
|
8
|
+
|
9
|
+
class ManagerWithDatabaseTasksTest < Test::Unit::TestCase
|
10
|
+
|
11
|
+
class ScheduledTask; end
|
12
|
+
class ScheduledTaskType2; end
|
13
|
+
|
14
|
+
setup do
|
15
|
+
@manager = Clockwork::ManagerWithDatabaseTasks.new
|
16
|
+
class << @manager
|
17
|
+
def log(msg); end
|
18
|
+
end
|
19
|
+
@manager.handler { }
|
20
|
+
end
|
21
|
+
|
22
|
+
def tick_at(now = Time.now, options = {})
|
23
|
+
seconds_to_tick_for = options[:and_every_second_for] || 0
|
24
|
+
number_of_ticks = seconds_to_tick_for + 1 # add one for right now
|
25
|
+
number_of_ticks.times{|i| @manager.tick(now + i) }
|
26
|
+
end
|
27
|
+
|
28
|
+
def next_minute(now = Time.now)
|
29
|
+
Time.new(now.year, now.month, now.day, now.hour, now.min + 1, 0)
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "sync_database_tasks" do
|
33
|
+
|
34
|
+
describe "arguments" do
|
35
|
+
|
36
|
+
def test_does_not_raise_error_with_valid_arguments
|
37
|
+
@manager.sync_database_tasks(model: ScheduledTask, every: 1.minute) {}
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_raises_argument_error_if_param_called_model_is_not_set
|
41
|
+
assert_raises ArgumentError do
|
42
|
+
@manager.sync_database_tasks(model: ScheduledTask) {}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_raises_argument_error_if_param_called_every_is_not_set
|
47
|
+
assert_raises ArgumentError do
|
48
|
+
@manager.sync_database_tasks(every: 1.minute) {}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_raises_argument_error_if_param_called_every_is_less_than_1_minute
|
53
|
+
assert_raises ArgumentError do
|
54
|
+
@manager.sync_database_tasks(model: ScheduledTask, every: 59.seconds) {}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
setup do
|
60
|
+
@tasks_run = []
|
61
|
+
@scheduled_task1 = stub(:frequency => 10, :name => 'ScheduledTask:1', :at => nil)
|
62
|
+
@scheduled_task2 = stub(:frequency => 10, :name => 'ScheduledTask:2', :at => nil)
|
63
|
+
@scheduled_task1_modified = stub(:frequency => 5, :name => 'ScheduledTaskModified:1', :at => nil)
|
64
|
+
ScheduledTask.stubs(:all).returns([@scheduled_task1])
|
65
|
+
|
66
|
+
@database_reload_frequency = 1.minute
|
67
|
+
|
68
|
+
@now = Time.now
|
69
|
+
@next_minute = next_minute(@now) # database sync task only happens on minute boundary
|
70
|
+
|
71
|
+
# setup the database sync
|
72
|
+
@manager.sync_database_tasks model: ScheduledTask, every: @database_reload_frequency do |job_name|
|
73
|
+
@tasks_run << job_name
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_does_not_fetch_database_tasks_until_next_minute
|
78
|
+
seconds_upto_and_including_next_minute = (@next_minute - @now).seconds.to_i + 1
|
79
|
+
tick_at(@now, :and_every_second_for => seconds_upto_and_including_next_minute)
|
80
|
+
assert_equal [], @tasks_run
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_fetches_and_registers_database_task
|
84
|
+
tick_at(@next_minute, :and_every_second_for => 1.second)
|
85
|
+
assert_equal ["ScheduledTask:1"], @tasks_run
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_multiple_database_tasks_can_be_registered
|
89
|
+
ScheduledTask.stubs(:all).returns([@scheduled_task1, @scheduled_task2])
|
90
|
+
tick_at(@next_minute, :and_every_second_for => 1.second)
|
91
|
+
assert_equal ["ScheduledTask:1", "ScheduledTask:2"], @tasks_run
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_database_task_does_not_run_again_before_frequency_specified_in_database
|
95
|
+
tick_at(@next_minute, :and_every_second_for => 9.seconds) # runs at 1
|
96
|
+
assert_equal 1, @tasks_run.length
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_database_task_runs_repeatedly_with_frequency_specified_in_database
|
100
|
+
tick_at(@next_minute, :and_every_second_for => 21.seconds) # runs at 1, 11, and 21
|
101
|
+
assert_equal 3, @tasks_run.length
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_reloads_tasks_from_database
|
105
|
+
ScheduledTask.stubs(:all).returns([@scheduled_task1], [@scheduled_task2])
|
106
|
+
tick_at(@next_minute, :and_every_second_for => @database_reload_frequency.seconds)
|
107
|
+
@manager.tick # @scheduled_task2 should run immediately on next tick (then every 10 seconds)
|
108
|
+
|
109
|
+
assert_equal "ScheduledTask:2", @tasks_run.last
|
110
|
+
end
|
111
|
+
|
112
|
+
def test_reloaded_tasks_run_repeatedly
|
113
|
+
ScheduledTask.stubs(:all).returns([@scheduled_task1], [@scheduled_task2])
|
114
|
+
tick_at(@next_minute, :and_every_second_for => @database_reload_frequency.seconds + 11.seconds)
|
115
|
+
assert_equal ["ScheduledTask:2", "ScheduledTask:2"], @tasks_run[-2..-1]
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_stops_running_deleted_database_task
|
119
|
+
ScheduledTask.stubs(:all).returns([@scheduled_task1], [])
|
120
|
+
tick_at(@next_minute, :and_every_second_for => @database_reload_frequency.seconds)
|
121
|
+
before = @tasks_run.dup
|
122
|
+
|
123
|
+
# tick through reload, and run for enough ticks that previous task would have run
|
124
|
+
tick_at(@next_minute + @database_reload_frequency.seconds + 20.seconds)
|
125
|
+
after = @tasks_run
|
126
|
+
|
127
|
+
assert_equal before, after
|
128
|
+
end
|
129
|
+
|
130
|
+
def test_reloading_task_with_modified_frequency_will_run_with_new_frequency
|
131
|
+
ScheduledTask.stubs(:all).returns([@scheduled_task1], [@scheduled_task1_modified])
|
132
|
+
|
133
|
+
tick_at(@next_minute, :and_every_second_for => 66.seconds)
|
134
|
+
|
135
|
+
# task1 runs at: 1, 11, 21, 31, 41, 51 (6 runs)
|
136
|
+
# database tasks are reloaded at: 60
|
137
|
+
# task1_modified runs at: 61 (next tick after reload) and then 66 (2 runs)
|
138
|
+
assert_equal 8, @tasks_run.length
|
139
|
+
end
|
140
|
+
|
141
|
+
# Catch a bug caused by allowing database tasks to be run in the same clock cycle that the database
|
142
|
+
# sync occurs. When this happens, a previously scheduled database task will be scheduled to run,
|
143
|
+
# we then fetch the same task afresh (wiping out the @events_from_database object), but the
|
144
|
+
# previously scheduled task still runs because #task `events` variable already stored it *before*
|
145
|
+
# we wiped out the @events_from_database objects.
|
146
|
+
#
|
147
|
+
# We have a situation like this:
|
148
|
+
#
|
149
|
+
# 12:31:00 #tick loops through events to run
|
150
|
+
# sync_database_tasks_for_model_ task is run
|
151
|
+
# fetches database task 1 with :at => 12:32, and schedules it to run (object task 1')
|
152
|
+
#
|
153
|
+
# ...
|
154
|
+
#
|
155
|
+
# 12:32:00 #tick loops through events that should be run, of which task 1' is included
|
156
|
+
# sync_database_tasks_for_model_ task is run
|
157
|
+
# fetches database task 1 with :at => 12:32, and schedules it to run (object task 1'')
|
158
|
+
# task 1' is run
|
159
|
+
#
|
160
|
+
# 12:32:01 #tick loops through events that should be run, of which task 1'' is included
|
161
|
+
# task 1'' is run
|
162
|
+
def test_daily_task_with_at_should_not_run_twice_when_already_scheduled
|
163
|
+
minute_after_next = next_minute(@next_minute)
|
164
|
+
at = minute_after_next.strftime('%H:%M')
|
165
|
+
@scheduled_task_with_at = stub(:frequency => 1.day, :name => 'ScheduledTaskWithAt:1', :at => at)
|
166
|
+
ScheduledTask.stubs(:all).returns([@scheduled_task_with_at])
|
167
|
+
|
168
|
+
# tick from now, though specified :at time
|
169
|
+
tick_at(@now, :and_every_second_for => (2 * @database_reload_frequency.seconds) + 1.second)
|
170
|
+
|
171
|
+
assert_equal 1, @tasks_run.length
|
172
|
+
end
|
173
|
+
|
174
|
+
def test_having_multiple_sync_database_tasks_will_work
|
175
|
+
ScheduledTask.stubs(:all).returns([@scheduled_task1])
|
176
|
+
|
177
|
+
# setup 2nd database sync
|
178
|
+
@scheduled_task_type2 = stub(:frequency => 10, :name => 'ScheduledTaskType2:1', :at => nil)
|
179
|
+
|
180
|
+
ScheduledTaskType2.stubs(:all).returns([@scheduled_task_type2])
|
181
|
+
@manager.sync_database_tasks model: ScheduledTaskType2, every: @database_reload_frequency do |job_name|
|
182
|
+
@tasks_run << job_name
|
183
|
+
end
|
184
|
+
|
185
|
+
tick_at(@next_minute, :and_every_second_for => 1.second)
|
186
|
+
|
187
|
+
assert_equal ["ScheduledTask:1", "ScheduledTaskType2:1"], @tasks_run
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: clockwork
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Wiggins
|
@@ -9,104 +9,104 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-
|
12
|
+
date: 2014-03-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: tzinfo
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- -
|
18
|
+
- - '>='
|
19
19
|
- !ruby/object:Gem::Version
|
20
20
|
version: '0'
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
|
-
- -
|
25
|
+
- - '>='
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: '0'
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: activesupport
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
|
-
- -
|
32
|
+
- - '>='
|
33
33
|
- !ruby/object:Gem::Version
|
34
34
|
version: '0'
|
35
35
|
type: :runtime
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
|
-
- -
|
39
|
+
- - '>='
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
version: '0'
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
43
|
name: bundler
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
45
45
|
requirements:
|
46
|
-
- -
|
46
|
+
- - ~>
|
47
47
|
- !ruby/object:Gem::Version
|
48
48
|
version: '1.3'
|
49
49
|
type: :development
|
50
50
|
prerelease: false
|
51
51
|
version_requirements: !ruby/object:Gem::Requirement
|
52
52
|
requirements:
|
53
|
-
- -
|
53
|
+
- - ~>
|
54
54
|
- !ruby/object:Gem::Version
|
55
55
|
version: '1.3'
|
56
56
|
- !ruby/object:Gem::Dependency
|
57
57
|
name: rake
|
58
58
|
requirement: !ruby/object:Gem::Requirement
|
59
59
|
requirements:
|
60
|
-
- -
|
60
|
+
- - '>='
|
61
61
|
- !ruby/object:Gem::Version
|
62
62
|
version: '0'
|
63
63
|
type: :development
|
64
64
|
prerelease: false
|
65
65
|
version_requirements: !ruby/object:Gem::Requirement
|
66
66
|
requirements:
|
67
|
-
- -
|
67
|
+
- - '>='
|
68
68
|
- !ruby/object:Gem::Version
|
69
69
|
version: '0'
|
70
70
|
- !ruby/object:Gem::Dependency
|
71
71
|
name: daemons
|
72
72
|
requirement: !ruby/object:Gem::Requirement
|
73
73
|
requirements:
|
74
|
-
- -
|
74
|
+
- - '>='
|
75
75
|
- !ruby/object:Gem::Version
|
76
76
|
version: '0'
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
79
|
version_requirements: !ruby/object:Gem::Requirement
|
80
80
|
requirements:
|
81
|
-
- -
|
81
|
+
- - '>='
|
82
82
|
- !ruby/object:Gem::Version
|
83
83
|
version: '0'
|
84
84
|
- !ruby/object:Gem::Dependency
|
85
85
|
name: contest
|
86
86
|
requirement: !ruby/object:Gem::Requirement
|
87
87
|
requirements:
|
88
|
-
- -
|
88
|
+
- - '>='
|
89
89
|
- !ruby/object:Gem::Version
|
90
90
|
version: '0'
|
91
91
|
type: :development
|
92
92
|
prerelease: false
|
93
93
|
version_requirements: !ruby/object:Gem::Requirement
|
94
94
|
requirements:
|
95
|
-
- -
|
95
|
+
- - '>='
|
96
96
|
- !ruby/object:Gem::Version
|
97
97
|
version: '0'
|
98
98
|
- !ruby/object:Gem::Dependency
|
99
99
|
name: mocha
|
100
100
|
requirement: !ruby/object:Gem::Requirement
|
101
101
|
requirements:
|
102
|
-
- -
|
102
|
+
- - '>='
|
103
103
|
- !ruby/object:Gem::Version
|
104
104
|
version: '0'
|
105
105
|
type: :development
|
106
106
|
prerelease: false
|
107
107
|
version_requirements: !ruby/object:Gem::Requirement
|
108
108
|
requirements:
|
109
|
-
- -
|
109
|
+
- - '>='
|
110
110
|
- !ruby/object:Gem::Version
|
111
111
|
version: '0'
|
112
112
|
description: A scheduler process to replace cron, using a more flexible Ruby syntax
|
@@ -121,8 +121,8 @@ extensions: []
|
|
121
121
|
extra_rdoc_files:
|
122
122
|
- README.md
|
123
123
|
files:
|
124
|
-
-
|
125
|
-
-
|
124
|
+
- .gitignore
|
125
|
+
- .travis.yml
|
126
126
|
- Gemfile
|
127
127
|
- README.md
|
128
128
|
- Rakefile
|
@@ -136,10 +136,12 @@ files:
|
|
136
136
|
- lib/clockwork/at.rb
|
137
137
|
- lib/clockwork/event.rb
|
138
138
|
- lib/clockwork/manager.rb
|
139
|
+
- lib/clockwork/manager_with_database_tasks.rb
|
139
140
|
- test/at_test.rb
|
140
141
|
- test/clockwork_test.rb
|
141
142
|
- test/event_test.rb
|
142
143
|
- test/manager_test.rb
|
144
|
+
- test/manager_with_database_tasks_test.rb
|
143
145
|
homepage: http://github.com/tomykaira/clockwork
|
144
146
|
licenses:
|
145
147
|
- MIT
|
@@ -150,17 +152,17 @@ require_paths:
|
|
150
152
|
- lib
|
151
153
|
required_ruby_version: !ruby/object:Gem::Requirement
|
152
154
|
requirements:
|
153
|
-
- -
|
155
|
+
- - '>='
|
154
156
|
- !ruby/object:Gem::Version
|
155
157
|
version: '0'
|
156
158
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
157
159
|
requirements:
|
158
|
-
- -
|
160
|
+
- - '>='
|
159
161
|
- !ruby/object:Gem::Version
|
160
162
|
version: '0'
|
161
163
|
requirements: []
|
162
164
|
rubyforge_project:
|
163
|
-
rubygems_version: 2.
|
165
|
+
rubygems_version: 2.0.3
|
164
166
|
signing_key:
|
165
167
|
specification_version: 4
|
166
168
|
summary: A scheduler process to replace cron.
|
@@ -169,3 +171,4 @@ test_files:
|
|
169
171
|
- test/clockwork_test.rb
|
170
172
|
- test/event_test.rb
|
171
173
|
- test/manager_test.rb
|
174
|
+
- test/manager_with_database_tasks_test.rb
|