resque-scheduler 2.5.5 → 3.0.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.

Potentially problematic release.


This version of resque-scheduler might be problematic. Click here for more details.

Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +12 -6
  3. data/.rubocop.yml +18 -113
  4. data/.rubocop_todo.yml +29 -0
  5. data/.simplecov +3 -1
  6. data/.travis.yml +12 -4
  7. data/.vagrant-provision-as-vagrant.sh +15 -0
  8. data/.vagrant-provision.sh +23 -0
  9. data/.vagrant-skel/bash_profile +7 -0
  10. data/.vagrant-skel/bashrc +7 -0
  11. data/AUTHORS.md +5 -0
  12. data/Gemfile +1 -2
  13. data/HISTORY.md +18 -0
  14. data/README.md +39 -11
  15. data/ROADMAP.md +0 -6
  16. data/Rakefile +11 -19
  17. data/Vagrantfile +14 -0
  18. data/bin/resque-scheduler +2 -2
  19. data/examples/Rakefile +1 -1
  20. data/examples/config/initializers/resque-web.rb +2 -2
  21. data/examples/dynamic-scheduling/app/jobs/fix_schedules_job.rb +2 -4
  22. data/examples/dynamic-scheduling/app/jobs/send_email_job.rb +1 -1
  23. data/examples/dynamic-scheduling/app/models/user.rb +2 -2
  24. data/examples/dynamic-scheduling/lib/tasks/resque.rake +2 -2
  25. data/lib/resque-scheduler.rb +3 -1
  26. data/lib/resque/scheduler.rb +112 -168
  27. data/lib/resque/scheduler/cli.rb +144 -0
  28. data/lib/resque/scheduler/configuration.rb +73 -0
  29. data/lib/resque/scheduler/delaying_extensions.rb +278 -0
  30. data/lib/resque/scheduler/env.rb +61 -0
  31. data/lib/resque/scheduler/extension.rb +13 -0
  32. data/lib/resque/scheduler/lock.rb +2 -1
  33. data/lib/resque/scheduler/lock/base.rb +6 -2
  34. data/lib/resque/scheduler/lock/basic.rb +4 -5
  35. data/lib/resque/scheduler/lock/resilient.rb +30 -37
  36. data/lib/resque/scheduler/locking.rb +94 -0
  37. data/lib/resque/scheduler/logger_builder.rb +72 -0
  38. data/lib/resque/scheduler/plugin.rb +31 -0
  39. data/lib/resque/scheduler/scheduling_extensions.rb +150 -0
  40. data/lib/resque/scheduler/server.rb +246 -0
  41. data/lib/{resque_scheduler → resque/scheduler}/server/views/delayed.erb +2 -1
  42. data/lib/{resque_scheduler → resque/scheduler}/server/views/delayed_schedules.erb +0 -0
  43. data/lib/{resque_scheduler → resque/scheduler}/server/views/delayed_timestamp.erb +0 -0
  44. data/lib/{resque_scheduler → resque/scheduler}/server/views/requeue-params.erb +0 -0
  45. data/lib/{resque_scheduler → resque/scheduler}/server/views/scheduler.erb +16 -1
  46. data/lib/{resque_scheduler → resque/scheduler}/server/views/search.erb +2 -1
  47. data/lib/{resque_scheduler → resque/scheduler}/server/views/search_form.erb +0 -0
  48. data/lib/resque/scheduler/signal_handling.rb +40 -0
  49. data/lib/{resque_scheduler → resque/scheduler}/tasks.rb +3 -5
  50. data/lib/resque/scheduler/util.rb +41 -0
  51. data/lib/resque/scheduler/version.rb +7 -0
  52. data/resque-scheduler.gemspec +21 -19
  53. data/script/migrate_to_timestamps_set.rb +5 -3
  54. data/tasks/resque_scheduler.rake +1 -1
  55. data/test/cli_test.rb +26 -69
  56. data/test/delayed_queue_test.rb +262 -169
  57. data/test/env_test.rb +41 -0
  58. data/test/resque-web_test.rb +169 -48
  59. data/test/scheduler_args_test.rb +73 -41
  60. data/test/scheduler_hooks_test.rb +9 -8
  61. data/test/scheduler_locking_test.rb +55 -36
  62. data/test/scheduler_setup_test.rb +52 -15
  63. data/test/scheduler_task_test.rb +15 -10
  64. data/test/scheduler_test.rb +215 -114
  65. data/test/support/redis_instance.rb +32 -33
  66. data/test/test_helper.rb +33 -36
  67. data/test/util_test.rb +11 -0
  68. metadata +113 -57
  69. data/lib/resque/scheduler_locking.rb +0 -91
  70. data/lib/resque_scheduler.rb +0 -386
  71. data/lib/resque_scheduler/cli.rb +0 -160
  72. data/lib/resque_scheduler/logger_builder.rb +0 -72
  73. data/lib/resque_scheduler/plugin.rb +0 -28
  74. data/lib/resque_scheduler/server.rb +0 -183
  75. data/lib/resque_scheduler/util.rb +0 -34
  76. data/lib/resque_scheduler/version.rb +0 -5
  77. data/test/redis-test.conf +0 -108
@@ -1,23 +1,24 @@
1
+ # vim:fileencoding=utf-8
1
2
  require_relative 'test_helper'
2
3
 
3
- context "scheduling jobs with hooks" do
4
- setup do
5
- Resque.redis.flushall
6
- end
4
+ context 'scheduling jobs with hooks' do
5
+ setup { Resque.redis.flushall }
7
6
 
8
- test "before_schedule hook that does not return false should be enqueued" do
7
+ test 'before_schedule hook that does not return false should be enqueued' do
9
8
  enqueue_time = Time.now
10
9
  SomeRealClass.expects(:before_schedule_example).with(:foo)
11
10
  SomeRealClass.expects(:after_schedule_example).with(:foo)
12
11
  Resque.enqueue_at(enqueue_time.to_i, SomeRealClass, :foo)
13
- assert_equal(1, Resque.delayed_timestamp_size(enqueue_time.to_i), "job should be enqueued")
12
+ assert_equal(1, Resque.delayed_timestamp_size(enqueue_time.to_i),
13
+ 'job should be enqueued')
14
14
  end
15
15
 
16
- test "before_schedule hook that returns false should not be enqueued" do
16
+ test 'before_schedule hook that returns false should not be enqueued' do
17
17
  enqueue_time = Time.now
18
18
  SomeRealClass.expects(:before_schedule_example).with(:foo).returns(false)
19
19
  SomeRealClass.expects(:after_schedule_example).never
20
20
  Resque.enqueue_at(enqueue_time.to_i, SomeRealClass, :foo)
21
- assert_equal(0, Resque.delayed_timestamp_size(enqueue_time.to_i), "job should not be enqueued")
21
+ assert_equal(0, Resque.delayed_timestamp_size(enqueue_time.to_i),
22
+ 'job should not be enqueued')
22
23
  end
23
24
  end
@@ -1,3 +1,4 @@
1
+ # vim:fileencoding=utf-8
1
2
  require_relative 'test_helper'
2
3
 
3
4
  module LockTestHelper
@@ -8,36 +9,41 @@ end
8
9
 
9
10
  context '#master_lock_key' do
10
11
  setup do
11
- @subject = Class.new { extend Resque::SchedulerLocking }
12
+ @subject = Class.new { extend Resque::Scheduler::Locking }
12
13
  end
13
14
 
14
15
  teardown do
15
16
  Resque.redis.del(@subject.master_lock.key)
16
17
  end
17
18
 
18
- test 'it should have resque prefix' do
19
- assert_equal @subject.master_lock.key, 'resque:resque_scheduler_master_lock'
19
+ test 'should have resque prefix' do
20
+ assert_equal(
21
+ @subject.master_lock.key, 'resque:resque_scheduler_master_lock'
22
+ )
20
23
  end
21
24
 
22
25
  context 'with a prefix set via ENV' do
23
26
  setup do
24
27
  ENV['RESQUE_SCHEDULER_MASTER_LOCK_PREFIX'] = 'my.prefix'
25
- @subject = Class.new { extend Resque::SchedulerLocking }
28
+ @subject = Class.new { extend Resque::Scheduler::Locking }
26
29
  end
27
30
 
28
31
  teardown do
29
32
  Resque.redis.del(@subject.master_lock.key)
30
33
  end
31
34
 
32
- test 'it should have ENV prefix' do
33
- assert_equal @subject.master_lock.key, 'resque:my.prefix:resque_scheduler_master_lock'
35
+ test 'should have ENV prefix' do
36
+ assert_equal(
37
+ @subject.master_lock.key,
38
+ 'resque:my.prefix:resque_scheduler_master_lock'
39
+ )
34
40
  end
35
41
  end
36
42
 
37
43
  context 'with a namespace set for resque' do
38
44
  setup do
39
45
  Resque.redis.namespace = 'my.namespace'
40
- @subject = Class.new { extend Resque::SchedulerLocking }
46
+ @subject = Class.new { extend Resque::Scheduler::Locking }
41
47
  end
42
48
 
43
49
  teardown do
@@ -45,15 +51,17 @@ context '#master_lock_key' do
45
51
  Resque.redis.del(@subject.master_lock.key)
46
52
  end
47
53
 
48
- test 'it should have resque prefix' do
49
- assert_equal @subject.master_lock.key, 'my.namespace:resque_scheduler_master_lock'
54
+ test 'should have resque prefix' do
55
+ assert_equal(
56
+ @subject.master_lock.key, 'my.namespace:resque_scheduler_master_lock'
57
+ )
50
58
  end
51
59
 
52
60
  context 'with a prefix set via ENV' do
53
61
  setup do
54
62
  Resque.redis.namespace = 'my.namespace'
55
63
  ENV['RESQUE_SCHEDULER_MASTER_LOCK_PREFIX'] = 'my.prefix'
56
- @subject = Class.new { extend Resque::SchedulerLocking }
64
+ @subject = Class.new { extend Resque::Scheduler::Locking }
57
65
  end
58
66
 
59
67
  teardown do
@@ -61,45 +69,50 @@ context '#master_lock_key' do
61
69
  Resque.redis.del(@subject.master_lock.key)
62
70
  end
63
71
 
64
- test 'it should have ENV prefix' do
65
- assert_equal @subject.master_lock.key, 'my.namespace:my.prefix:resque_scheduler_master_lock'
72
+ test 'should have ENV prefix' do
73
+ assert_equal(
74
+ @subject.master_lock.key,
75
+ 'my.namespace:my.prefix:resque_scheduler_master_lock'
76
+ )
66
77
  end
67
78
  end
68
79
  end
69
80
  end
70
81
 
71
- context 'Resque::SchedulerLocking' do
82
+ context 'Resque::Scheduler::Locking' do
72
83
  setup do
73
- @subject = Class.new { extend Resque::SchedulerLocking }
84
+ @subject = Class.new { extend Resque::Scheduler::Locking }
74
85
  end
75
86
 
76
87
  teardown do
77
88
  Resque.redis.del(@subject.master_lock.key)
78
89
  end
79
90
 
80
- test 'it should use the basic lock mechanism for <= Redis 2.4' do
91
+ test 'should use the basic lock mechanism for <= Redis 2.4' do
81
92
  Resque.redis.stubs(:info).returns('redis_version' => '2.4.16')
82
93
 
83
94
  assert_equal @subject.master_lock.class, Resque::Scheduler::Lock::Basic
84
95
  end
85
96
 
86
- test 'it should use the resilient lock mechanism for > Redis 2.4' do
97
+ test 'should use the resilient lock mechanism for > Redis 2.4' do
87
98
  Resque.redis.stubs(:info).returns('redis_version' => '2.5.12')
88
99
 
89
- assert_equal @subject.master_lock.class, Resque::Scheduler::Lock::Resilient
100
+ assert_equal(
101
+ @subject.master_lock.class, Resque::Scheduler::Lock::Resilient
102
+ )
90
103
  end
91
104
 
92
- test 'it should be the master if the lock is held' do
105
+ test 'should be the master if the lock is held' do
93
106
  @subject.master_lock.acquire!
94
- assert @subject.is_master?, 'should be master'
107
+ assert @subject.master?, 'should be master'
95
108
  end
96
109
 
97
- test 'it should not be the master if the lock is held by someone else' do
110
+ test 'should not be the master if the lock is held by someone else' do
98
111
  Resque.redis.set(@subject.master_lock.key, 'somethingelse:1234')
99
- assert !@subject.is_master?, 'should not be master'
112
+ assert !@subject.master?, 'should not be master'
100
113
  end
101
114
 
102
- test "release_master_lock should delegate to master_lock" do
115
+ test 'release_master_lock should delegate to master_lock' do
103
116
  @subject.master_lock.expects(:release!)
104
117
  @subject.release_master_lock!
105
118
  end
@@ -111,13 +124,13 @@ context 'Resque::Scheduler::Lock::Base' do
111
124
  end
112
125
 
113
126
  test '#acquire! should be not implemented' do
114
- assert_raise(NotImplementedError) do
127
+ assert_raises NotImplementedError do
115
128
  @lock.acquire!
116
129
  end
117
130
  end
118
131
 
119
132
  test '#locked? should be not implemented' do
120
- assert_raise(NotImplementedError) do
133
+ assert_raises NotImplementedError do
121
134
  @lock.locked?
122
135
  end
123
136
  end
@@ -140,16 +153,17 @@ context 'Resque::Scheduler::Lock::Basic' do
140
153
  assert !@lock.locked?
141
154
  end
142
155
 
143
- test 'you should not be able to acquire the lock if someone else holds it' do
156
+ test 'you should not be able to acquire the lock if someone ' \
157
+ 'else holds it' do
144
158
  lock_is_not_held(@lock)
145
159
 
146
160
  assert !@lock.acquire!
147
161
  end
148
162
 
149
- test "the lock should receive a TTL on acquiring" do
163
+ test 'the lock should receive a TTL on acquiring' do
150
164
  @lock.acquire!
151
165
 
152
- assert Resque.redis.ttl(@lock.key) > 0, "lock should expire"
166
+ assert Resque.redis.ttl(@lock.key) > 0, 'lock should expire'
153
167
  end
154
168
 
155
169
  test 'releasing should release the master lock' do
@@ -167,7 +181,7 @@ context 'Resque::Scheduler::Lock::Basic' do
167
181
 
168
182
  @lock.locked?
169
183
 
170
- assert Resque.redis.ttl(@lock.key) > 10, "TTL should have been updated"
184
+ assert Resque.redis.ttl(@lock.key) > 10, 'TTL should have been updated'
171
185
  end
172
186
 
173
187
  test 'checking the lock should not increase the TTL if we do not hold it' do
@@ -176,7 +190,8 @@ context 'Resque::Scheduler::Lock::Basic' do
176
190
 
177
191
  @lock.locked?
178
192
 
179
- assert Resque.redis.ttl(@lock.key) <= 10, "TTL should not have been updated"
193
+ assert Resque.redis.ttl(@lock.key) <= 10,
194
+ 'TTL should not have been updated'
180
195
  end
181
196
  end
182
197
 
@@ -184,7 +199,8 @@ context 'Resque::Scheduler::Lock::Resilient' do
184
199
  include LockTestHelper
185
200
 
186
201
  if !Resque::Scheduler.supports_lua?
187
- puts "*** Skipping Resque::Scheduler::Lock::Resilient tests, as they require Redis >= 2.5."
202
+ puts '*** Skipping Resque::Scheduler::Lock::Resilient ' \
203
+ 'tests, as they require Redis >= 2.5.'
188
204
  else
189
205
  setup do
190
206
  @lock = Resque::Scheduler::Lock::Resilient.new('test_resilient_lock')
@@ -200,16 +216,17 @@ context 'Resque::Scheduler::Lock::Resilient' do
200
216
  assert !@lock.locked?, 'you should not have the lock'
201
217
  end
202
218
 
203
- test 'you should not be able to acquire the lock if someone else holds it' do
219
+ test 'you should not be able to acquire the lock if someone ' \
220
+ 'else holds it' do
204
221
  lock_is_not_held(@lock)
205
222
 
206
223
  assert !@lock.acquire!
207
224
  end
208
225
 
209
- test "the lock should receive a TTL on acquiring" do
226
+ test 'the lock should receive a TTL on acquiring' do
210
227
  @lock.acquire!
211
228
 
212
- assert Resque.redis.ttl(@lock.key) > 0, "lock should expire"
229
+ assert Resque.redis.ttl(@lock.key) > 0, 'lock should expire'
213
230
  end
214
231
 
215
232
  test 'releasing should release the master lock' do
@@ -227,16 +244,18 @@ context 'Resque::Scheduler::Lock::Resilient' do
227
244
 
228
245
  @lock.locked?
229
246
 
230
- assert Resque.redis.ttl(@lock.key) > 10, "TTL should have been updated"
247
+ assert Resque.redis.ttl(@lock.key) > 10, 'TTL should have been updated'
231
248
  end
232
249
 
233
- test 'checking the lock should not increase the TTL if we do not hold it' do
250
+ test 'checking the lock should not increase the TTL if we do ' \
251
+ 'not hold it' do
234
252
  Resque.redis.setex(@lock.key, 10, @lock.value)
235
253
  lock_is_not_held(@lock)
236
254
 
237
255
  @lock.locked?
238
256
 
239
- assert Resque.redis.ttl(@lock.key) <= 10, "TTL should not have been updated"
257
+ assert Resque.redis.ttl(@lock.key) <= 10,
258
+ 'TTL should not have been updated'
240
259
  end
241
260
 
242
261
  test 'setting the lock timeout changes the key TTL if we hold it' do
@@ -1,6 +1,7 @@
1
+ # vim:fileencoding=utf-8
1
2
  require_relative 'test_helper'
2
3
 
3
- context "Resque::Scheduler" do
4
+ context 'Resque::Scheduler' do
4
5
  setup do
5
6
  ENV['VERBOSE'] = nil
6
7
  nullify_logger
@@ -13,16 +14,16 @@ context "Resque::Scheduler" do
13
14
 
14
15
  test 'set custom logger' do
15
16
  custom_logger = MonoLogger.new('/dev/null')
16
- Resque::Scheduler.logger = custom_logger
17
- assert_equal(custom_logger, Resque::Scheduler.logger)
17
+ Resque::Scheduler.send(:logger=, custom_logger)
18
+ assert_equal(custom_logger, Resque::Scheduler.send(:logger))
18
19
  end
19
20
 
20
21
  test 'configure block' do
21
- Resque::Scheduler.mute = false
22
+ Resque::Scheduler.quiet = false
22
23
  Resque::Scheduler.configure do |c|
23
- c.mute = true
24
+ c.quiet = true
24
25
  end
25
- assert_equal(Resque::Scheduler.mute, true)
26
+ assert_equal(Resque::Scheduler.quiet, true)
26
27
  end
27
28
 
28
29
  context 'when getting the env' do
@@ -57,16 +58,17 @@ context "Resque::Scheduler" do
57
58
 
58
59
  test 'uses STDOUT' do
59
60
  assert_equal(
60
- Resque::Scheduler.logger.instance_variable_get(:@logdev).dev, $stdout
61
+ Resque::Scheduler.send(:logger)
62
+ .instance_variable_get(:@logdev).dev, $stdout
61
63
  )
62
64
  end
63
65
 
64
66
  test 'not verbose' do
65
- assert Resque::Scheduler.logger.level > MonoLogger::DEBUG
67
+ assert Resque::Scheduler.send(:logger).level > MonoLogger::DEBUG
66
68
  end
67
69
 
68
- test 'not muted' do
69
- assert Resque::Scheduler.logger.level < MonoLogger::FATAL
70
+ test 'not quieted' do
71
+ assert Resque::Scheduler.send(:logger).level < MonoLogger::FATAL
70
72
  end
71
73
  end
72
74
 
@@ -77,19 +79,54 @@ context "Resque::Scheduler" do
77
79
  test 'uses logfile' do
78
80
  Resque::Scheduler.logfile = '/dev/null'
79
81
  assert_equal(
80
- Resque::Scheduler.logger.instance_variable_get(:@logdev).filename,
82
+ Resque::Scheduler.send(:logger)
83
+ .instance_variable_get(:@logdev).filename,
81
84
  '/dev/null'
82
85
  )
83
86
  end
84
87
 
85
88
  test 'set verbosity' do
86
89
  Resque::Scheduler.verbose = true
87
- assert Resque::Scheduler.logger.level == MonoLogger::DEBUG
90
+ assert Resque::Scheduler.send(:logger).level == MonoLogger::DEBUG
88
91
  end
89
92
 
90
- test 'mute logger' do
91
- Resque::Scheduler.mute = true
92
- assert Resque::Scheduler.logger.level == MonoLogger::FATAL
93
+ test 'quiet logger' do
94
+ Resque::Scheduler.quiet = true
95
+ assert Resque::Scheduler.send(:logger).level == MonoLogger::FATAL
96
+ end
97
+ end
98
+
99
+ context 'logger with json formatter' do
100
+ setup do
101
+ nullify_logger
102
+ Resque::Scheduler.logformat = 'json'
103
+ $stdout = StringIO.new
104
+ end
105
+
106
+ teardown do
107
+ $stdout = STDOUT
108
+ end
109
+
110
+ test 'logs with json' do
111
+ Resque::Scheduler.log! 'whatever'
112
+ assert $stdout.string =~ /"msg":"whatever"/
113
+ end
114
+ end
115
+
116
+ context 'logger with text formatter' do
117
+ setup do
118
+ nullify_logger
119
+ Resque::Scheduler.logformat = 'text'
120
+ $stdout = StringIO.new
121
+ end
122
+
123
+ teardown do
124
+ $stdout = STDOUT
125
+ end
126
+
127
+ test 'logs with text' do
128
+ Resque::Scheduler.log! 'another thing'
129
+ assert $stdout.string =~ /: another thing/
93
130
  end
94
131
  end
95
132
  end
@@ -1,28 +1,33 @@
1
+ # vim:fileencoding=utf-8
1
2
  require_relative 'test_helper'
2
3
 
3
- context "Resque::Scheduler" do
4
+ context 'Resque::Scheduler' do
4
5
  setup do
5
- Resque::Scheduler.dynamic = false
6
+ Resque::Scheduler.configure do |c|
7
+ c.dynamic = false
8
+ c.poll_sleep_amount = 0.1
9
+ end
6
10
  Resque.redis.flushall
7
- Resque::Scheduler.mute = true
11
+ Resque::Scheduler.quiet = true
8
12
  Resque::Scheduler.clear_schedule!
9
- Resque::Scheduler.send(:class_variable_set, :@@scheduled_jobs, {})
13
+ Resque::Scheduler.send(:instance_variable_set, :@scheduled_jobs, {})
10
14
  Resque::Scheduler.send(:instance_variable_set, :@shutdown, false)
11
15
  end
12
16
 
13
- test "shutdown raises Interrupt when sleeping" do
17
+ test 'shutdown raises Interrupt when sleeping' do
14
18
  Thread.current.expects(:raise).with(Interrupt)
15
19
  Resque::Scheduler.send(:instance_variable_set, :@th, Thread.current)
16
20
  Resque::Scheduler.send(:instance_variable_set, :@sleeping, true)
17
21
  Resque::Scheduler.shutdown
18
22
  end
19
23
 
20
- test "sending TERM to scheduler breaks out of poll_sleep" do
24
+ test 'sending TERM to scheduler breaks out of poll_sleep' do
21
25
  Resque::Scheduler.expects(:release_master_lock!)
22
- fork do
23
- sleep(0.5)
24
- system("kill -TERM #{Process.ppid}")
25
- exit!
26
+
27
+ @pid = Process.pid
28
+ Thread.new do
29
+ sleep(0.05)
30
+ Process.kill(:TERM, @pid)
26
31
  end
27
32
 
28
33
  assert_raises SystemExit do
@@ -1,16 +1,17 @@
1
+ # vim:fileencoding=utf-8
1
2
  require_relative 'test_helper'
2
3
 
3
4
  context 'Resque::Scheduler' do
4
5
  setup do
5
6
  Resque::Scheduler.configure do |c|
6
7
  c.dynamic = false
7
- c.mute = true
8
+ c.quiet = true
8
9
  c.env = nil
9
10
  c.app_name = nil
10
11
  end
11
12
  Resque.redis.flushall
12
13
  Resque::Scheduler.clear_schedule!
13
- Resque::Scheduler.send(:class_variable_set, :@@scheduled_jobs, {})
14
+ Resque::Scheduler.send(:instance_variable_set, :@scheduled_jobs, {})
14
15
  end
15
16
 
16
17
  test 'enqueue constantizes' do
@@ -54,45 +55,56 @@ context 'Resque::Scheduler' do
54
55
  Resque::Scheduler.enqueue_from_config(config)
55
56
  end
56
57
 
57
- test "config makes it into the rufus_scheduler" do
58
+ test 'config makes it into the rufus_scheduler' do
58
59
  assert_equal(0, Resque::Scheduler.rufus_scheduler.all_jobs.size)
59
60
 
60
- Resque.schedule = {:some_ivar_job => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp"}}
61
+ Resque.schedule = {
62
+ some_ivar_job: {
63
+ 'cron' => '* * * * *',
64
+ 'class' => 'SomeIvarJob',
65
+ 'args' => '/tmp'
66
+ }
67
+ }
61
68
  Resque::Scheduler.load_schedule!
62
69
 
63
70
  assert_equal(1, Resque::Scheduler.rufus_scheduler.all_jobs.size)
64
71
  assert Resque::Scheduler.scheduled_jobs.include?('some_ivar_job')
65
72
  end
66
73
 
67
- test "can reload schedule" do
74
+ test 'can reload schedule' do
68
75
  Resque::Scheduler.dynamic = true
69
- Resque.schedule = {"some_ivar_job" => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp"}}
76
+ Resque.schedule = {
77
+ 'some_ivar_job' => {
78
+ 'cron' => '* * * * *',
79
+ 'class' => 'SomeIvarJob',
80
+ 'args' => '/tmp'
81
+ }
82
+ }
70
83
 
71
84
  Resque::Scheduler.load_schedule!
72
85
 
73
86
  assert_equal(1, Resque::Scheduler.rufus_scheduler.all_jobs.size)
74
- assert Resque::Scheduler.scheduled_jobs.include?("some_ivar_job")
87
+ assert Resque::Scheduler.scheduled_jobs.include?('some_ivar_job')
75
88
 
76
89
  Resque.redis.del(:schedules)
77
- Resque.redis.hset(:schedules, "some_ivar_job2", Resque.encode(
78
- 'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/2"
90
+ Resque.redis.hset(:schedules, 'some_ivar_job2', Resque.encode(
91
+ 'cron' => '* * * * *', 'class' => 'SomeIvarJob', 'args' => '/tmp/2'
79
92
  ))
80
93
 
81
94
  Resque::Scheduler.reload_schedule!
82
95
 
83
96
  assert_equal(1, Resque::Scheduler.rufus_scheduler.all_jobs.size)
84
97
 
85
- assert_equal '/tmp/2', Resque.schedule["some_ivar_job2"]["args"]
86
- assert Resque::Scheduler.scheduled_jobs.include?("some_ivar_job2")
98
+ assert_equal '/tmp/2', Resque.schedule['some_ivar_job2']['args']
99
+ assert Resque::Scheduler.scheduled_jobs.include?('some_ivar_job2')
87
100
  end
88
101
 
89
102
  test 'load_schedule_job loads a schedule' do
90
103
  Resque::Scheduler.load_schedule_job(
91
- 'some_ivar_job', {
92
- 'cron' => '* * * * *',
93
- 'class' => 'SomeIvarJob',
94
- 'args' => '/tmp'
95
- }
104
+ 'some_ivar_job',
105
+ 'cron' => '* * * * *',
106
+ 'class' => 'SomeIvarJob',
107
+ 'args' => '/tmp'
96
108
  )
97
109
 
98
110
  assert_equal(1, Resque::Scheduler.rufus_scheduler.all_jobs.size)
@@ -102,11 +114,10 @@ context 'Resque::Scheduler' do
102
114
 
103
115
  test 'load_schedule_job with every with options' do
104
116
  Resque::Scheduler.load_schedule_job(
105
- 'some_ivar_job', {
106
- 'every' => ['30s', { 'first_in' => '60s' }],
107
- 'class' => 'SomeIvarJob',
108
- 'args' => '/tmp'
109
- }
117
+ 'some_ivar_job',
118
+ 'every' => ['30s', { 'first_in' => '60s' }],
119
+ 'class' => 'SomeIvarJob',
120
+ 'args' => '/tmp'
110
121
  )
111
122
 
112
123
  assert_equal(1, Resque::Scheduler.rufus_scheduler.all_jobs.size)
@@ -118,11 +129,10 @@ context 'Resque::Scheduler' do
118
129
 
119
130
  test 'load_schedule_job with cron with options' do
120
131
  Resque::Scheduler.load_schedule_job(
121
- 'some_ivar_job', {
122
- 'cron' => ['* * * * *', { 'allow_overlapping' => 'true' }],
123
- 'class' => 'SomeIvarJob',
124
- 'args' => '/tmp'
125
- }
132
+ 'some_ivar_job',
133
+ 'cron' => ['* * * * *', { 'allow_overlapping' => 'true' }],
134
+ 'class' => 'SomeIvarJob',
135
+ 'args' => '/tmp'
126
136
  )
127
137
 
128
138
  assert_equal(1, Resque::Scheduler.rufus_scheduler.all_jobs.size)
@@ -134,10 +144,9 @@ context 'Resque::Scheduler' do
134
144
 
135
145
  test 'load_schedule_job without cron' do
136
146
  Resque::Scheduler.load_schedule_job(
137
- 'some_ivar_job', {
138
- 'class' => 'SomeIvarJob',
139
- 'args' => '/tmp'
140
- }
147
+ 'some_ivar_job',
148
+ 'class' => 'SomeIvarJob',
149
+ 'args' => '/tmp'
141
150
  )
142
151
 
143
152
  assert_equal(0, Resque::Scheduler.rufus_scheduler.all_jobs.size)
@@ -147,11 +156,10 @@ context 'Resque::Scheduler' do
147
156
 
148
157
  test 'load_schedule_job with an empty cron' do
149
158
  Resque::Scheduler.load_schedule_job(
150
- 'some_ivar_job', {
151
- 'cron' => '',
152
- 'class' => 'SomeIvarJob',
153
- 'args' => '/tmp'
154
- }
159
+ 'some_ivar_job',
160
+ 'cron' => '',
161
+ 'class' => 'SomeIvarJob',
162
+ 'args' => '/tmp'
155
163
  )
156
164
 
157
165
  assert_equal(0, Resque::Scheduler.rufus_scheduler.all_jobs.size)
@@ -159,26 +167,35 @@ context 'Resque::Scheduler' do
159
167
  assert !Resque::Scheduler.scheduled_jobs.keys.include?('some_ivar_job')
160
168
  end
161
169
 
162
- test "update_schedule" do
170
+ test 'update_schedule' do
163
171
  Resque::Scheduler.dynamic = true
164
172
  Resque.schedule = {
165
- "some_ivar_job" => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp"},
166
- "another_ivar_job" => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/5"},
167
- "stay_put_job" => {'cron' => "* * * * *", 'class' => 'SomeJob', 'args' => "/tmp"}
173
+ 'some_ivar_job' => {
174
+ 'cron' => '* * * * *', 'class' => 'SomeIvarJob', 'args' => '/tmp'
175
+ },
176
+ 'another_ivar_job' => {
177
+ 'cron' => '* * * * *', 'class' => 'SomeIvarJob', 'args' => '/tmp/5'
178
+ },
179
+ 'stay_put_job' => {
180
+ 'cron' => '* * * * *', 'class' => 'SomeJob', 'args' => '/tmp'
181
+ }
168
182
  }
169
183
 
170
184
  Resque::Scheduler.load_schedule!
171
185
 
172
- Resque.set_schedule("some_ivar_job",
173
- {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/2"}
186
+ Resque.set_schedule(
187
+ 'some_ivar_job',
188
+ 'cron' => '* * * * *', 'class' => 'SomeIvarJob', 'args' => '/tmp/2 '
174
189
  )
175
- Resque.set_schedule("new_ivar_job",
176
- {'cron' => "* * * * *", 'class' => 'SomeJob', 'args' => "/tmp/3"}
190
+ Resque.set_schedule(
191
+ 'new_ivar_job',
192
+ 'cron' => '* * * * *', 'class' => 'SomeJob', 'args' => '/tmp/3 '
177
193
  )
178
- Resque.set_schedule("stay_put_job",
179
- {'cron' => "* * * * *", 'class' => 'SomeJob', 'args' => "/tmp"}
194
+ Resque.set_schedule(
195
+ 'stay_put_job',
196
+ 'cron' => '* * * * *', 'class' => 'SomeJob', 'args' => '/tmp '
180
197
  )
181
- Resque.remove_schedule("another_ivar_job")
198
+ Resque.remove_schedule('another_ivar_job')
182
199
 
183
200
  Resque::Scheduler.update_schedule
184
201
 
@@ -188,34 +205,40 @@ context 'Resque::Scheduler' do
188
205
  assert Resque::Scheduler.scheduled_jobs.keys.include?(job_name)
189
206
  assert Resque.schedule.keys.include?(job_name)
190
207
  end
191
- assert !Resque::Scheduler.scheduled_jobs.keys.include?("another_ivar_job")
192
- assert !Resque.schedule.keys.include?("another_ivar_job")
208
+ assert !Resque::Scheduler.scheduled_jobs.keys.include?('another_ivar_job')
209
+ assert !Resque.schedule.keys.include?('another_ivar_job')
193
210
  assert_equal 0, Resque.redis.scard(:schedules_changed)
194
211
  end
195
212
 
196
- test "update_schedule with mocks" do
213
+ test 'update_schedule with mocks' do
197
214
  Resque::Scheduler.dynamic = true
198
215
  Resque.schedule = {
199
- "some_ivar_job" => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp"},
200
- "another_ivar_job" => {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/5"},
201
- "stay_put_job" => {'cron' => "* * * * *", 'class' => 'SomeJob', 'args' => "/tmp"}
216
+ 'some_ivar_job' => {
217
+ 'cron' => '* * * * *', 'class' => 'SomeIvarJob', 'args' => '/tmp'
218
+ },
219
+ 'another_ivar_job' => {
220
+ 'cron' => '* * * * *', 'class' => 'SomeIvarJob', 'args' => '/tmp/5'
221
+ },
222
+ 'stay_put_job' => {
223
+ 'cron' => '* * * * *', 'class' => 'SomeJob', 'args' => '/tmp'
224
+ }
202
225
  }
203
226
 
204
227
  Resque::Scheduler.load_schedule!
205
228
 
206
- Resque::Scheduler.rufus_scheduler.expects(:unschedule).with(Resque::Scheduler.scheduled_jobs["some_ivar_job"].job_id)
207
- Resque::Scheduler.rufus_scheduler.expects(:unschedule).with(Resque::Scheduler.scheduled_jobs["another_ivar_job"].job_id)
208
-
209
- Resque.set_schedule("some_ivar_job",
210
- {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/2"}
229
+ Resque.set_schedule(
230
+ 'some_ivar_job',
231
+ 'cron' => '* * * * *', 'class' => 'SomeIvarJob', 'args' => '/tmp/2 '
211
232
  )
212
- Resque.set_schedule("new_ivar_job",
213
- {'cron' => "* * * * *", 'class' => 'SomeJob', 'args' => "/tmp/3"}
233
+ Resque.set_schedule(
234
+ 'new_ivar_job',
235
+ 'cron' => '* * * * *', 'class' => 'SomeJob', 'args' => '/tmp/3 '
214
236
  )
215
- Resque.set_schedule("stay_put_job",
216
- {'cron' => "* * * * *", 'class' => 'SomeJob', 'args' => "/tmp"}
237
+ Resque.set_schedule(
238
+ 'stay_put_job',
239
+ 'cron' => '* * * * *', 'class' => 'SomeJob', 'args' => '/tmp '
217
240
  )
218
- Resque.remove_schedule("another_ivar_job")
241
+ Resque.remove_schedule('another_ivar_job')
219
242
 
220
243
  Resque::Scheduler.update_schedule
221
244
 
@@ -224,99 +247,162 @@ context 'Resque::Scheduler' do
224
247
  assert Resque::Scheduler.scheduled_jobs.keys.include?(job_name)
225
248
  assert Resque.schedule.keys.include?(job_name)
226
249
  end
227
- assert !Resque::Scheduler.scheduled_jobs.keys.include?("another_ivar_job")
228
- assert !Resque.schedule.keys.include?("another_ivar_job")
250
+ assert !Resque::Scheduler.scheduled_jobs.keys.include?('another_ivar_job')
251
+ assert !Resque.schedule.keys.include?('another_ivar_job')
229
252
  assert_equal 0, Resque.redis.scard(:schedules_changed)
230
253
  end
231
254
 
232
- test "schedule= sets the schedule" do
255
+ test 'concurrent update_schedule calls' do
233
256
  Resque::Scheduler.dynamic = true
234
- Resque.schedule = {"my_ivar_job" => {
235
- 'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/75"
236
- }}
237
- assert_equal({'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/75"},
238
- Resque.decode(Resque.redis.hget(:schedules, "my_ivar_job")))
257
+ Resque::Scheduler.load_schedule!
258
+ jobs_count = 100
259
+
260
+ background_delayed_update = Thread.new do
261
+ sleep(0.01)
262
+ Resque::Scheduler.update_schedule
263
+ end
264
+
265
+ (0...jobs_count).each do |i|
266
+ Resque.set_schedule(
267
+ "some_ivar_job#{i}",
268
+ 'cron' => '* * * * *', 'class' => 'SomeIvarJob', 'args' => "/tmp/#{i}"
269
+ )
270
+ end
271
+
272
+ background_delayed_update.join
273
+ Resque::Scheduler.update_schedule
274
+ assert_equal(jobs_count, Resque::Scheduler.rufus_scheduler.all_jobs.size)
275
+ assert_equal(jobs_count, Resque::Scheduler.scheduled_jobs.size)
276
+ assert_equal 0, Resque.redis.scard(:schedules_changed)
239
277
  end
240
278
 
241
- test "schedule= removes schedules not present in the given schedule argument" do
279
+ test 'schedule= sets the schedule' do
242
280
  Resque::Scheduler.dynamic = true
281
+ Resque.schedule = {
282
+ 'my_ivar_job' => {
283
+ 'cron' => '* * * * *',
284
+ 'class' => 'SomeIvarJob',
285
+ 'args' => '/tmp/75'
286
+ }
287
+ }
288
+ assert_equal(
289
+ { 'cron' => '* * * * *', 'class' => 'SomeIvarJob', 'args' => '/tmp/75' },
290
+ Resque.decode(Resque.redis.hget(:schedules, 'my_ivar_job'))
291
+ )
292
+ end
243
293
 
244
- Resque.schedule = {"old_job" => {'cron' => "* * * * *", 'class' => 'OldJob'}}
245
- assert_equal({"old_job" => {'cron' => "* * * * *", 'class' => 'OldJob'}}, Resque.schedule)
294
+ test 'schedule= removes schedules not present in the given ' \
295
+ 'schedule argument' do
296
+ Resque::Scheduler.dynamic = true
297
+
298
+ Resque.schedule = {
299
+ 'old_job' => { 'cron' => '* * * * *', 'class' => 'OldJob' }
300
+ }
301
+ assert_equal(
302
+ { 'old_job' => { 'cron' => '* * * * *', 'class' => 'OldJob' } },
303
+ Resque.schedule
304
+ )
246
305
 
247
- Resque.schedule = {"new_job" => {'cron' => "* * * * *", 'class' => 'NewJob'}}
306
+ Resque.schedule = {
307
+ 'new_job' => { 'cron' => '* * * * *', 'class' => 'NewJob' }
308
+ }
248
309
  Resque.reload_schedule!
249
- assert_equal({"new_job" => {'cron' => "* * * * *", 'class' => 'NewJob'}}, Resque.schedule)
310
+ assert_equal(
311
+ { 'new_job' => { 'cron' => '* * * * *', 'class' => 'NewJob' } },
312
+ Resque.schedule
313
+ )
250
314
  end
251
315
 
252
316
  test "schedule= uses job name as 'class' argument if it's missing" do
253
317
  Resque::Scheduler.dynamic = true
254
- Resque.schedule = {"SomeIvarJob" => {
255
- 'cron' => "* * * * *", 'args' => "/tmp/75"
256
- }}
257
- assert_equal({'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/75"},
258
- Resque.decode(Resque.redis.hget(:schedules, "SomeIvarJob")))
318
+ Resque.schedule = { 'SomeIvarJob' => {
319
+ 'cron' => '* * * * *', 'args' => '/tmp/75'
320
+ } }
321
+ assert_equal(
322
+ { 'cron' => '* * * * *', 'class' => 'SomeIvarJob', 'args' => '/tmp/75' },
323
+ Resque.decode(Resque.redis.hget(:schedules, 'SomeIvarJob'))
324
+ )
259
325
  assert_equal('SomeIvarJob', Resque.schedule['SomeIvarJob']['class'])
260
326
  end
261
327
 
262
- test "schedule= does not mutate argument" do
263
- schedule = {"SomeIvarJob" => {
264
- 'cron' => "* * * * *", 'args' => "/tmp/75"
265
- }}
328
+ test 'schedule= does not mutate argument' do
329
+ schedule = { 'SomeIvarJob' => {
330
+ 'cron' => '* * * * *', 'args' => '/tmp/75'
331
+ } }
266
332
  Resque.schedule = schedule
267
333
  assert !schedule['SomeIvarJob'].key?('class')
268
334
  end
269
335
 
270
- test "set_schedule can set an individual schedule" do
271
- Resque.set_schedule("some_ivar_job", {
272
- 'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/22"
273
- })
274
- assert_equal({'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/22"},
275
- Resque.decode(Resque.redis.hget(:schedules, "some_ivar_job")))
276
- assert Resque.redis.sismember(:schedules_changed, "some_ivar_job")
336
+ test 'set_schedule can set an individual schedule' do
337
+ Resque.set_schedule(
338
+ 'some_ivar_job',
339
+ 'cron' => '* * * * *', 'class' => 'SomeIvarJob', 'args' => '/tmp/22'
340
+ )
341
+ assert_equal(
342
+ { 'cron' => '* * * * *', 'class' => 'SomeIvarJob', 'args' => '/tmp/22' },
343
+ Resque.decode(Resque.redis.hget(:schedules, 'some_ivar_job'))
344
+ )
345
+ assert Resque.redis.sismember(:schedules_changed, 'some_ivar_job')
277
346
  end
278
347
 
279
- test "get_schedule returns a schedule" do
280
- Resque.redis.hset(:schedules, "some_ivar_job2", Resque.encode(
281
- 'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/33"
348
+ test 'fetch_schedule returns a schedule' do
349
+ Resque.redis.hset(:schedules, 'some_ivar_job2', Resque.encode(
350
+ 'cron' => '* * * * *', 'class' => 'SomeIvarJob', 'args' => '/tmp/33'
282
351
  ))
283
- assert_equal({'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/33"},
284
- Resque.get_schedule("some_ivar_job2"))
352
+ assert_equal(
353
+ { 'cron' => '* * * * *', 'class' => 'SomeIvarJob', 'args' => '/tmp/33' },
354
+ Resque.fetch_schedule('some_ivar_job2')
355
+ )
285
356
  end
286
357
 
287
- test "remove_schedule removes a schedule" do
288
- Resque.set_schedule("some_ivar_job3",
289
- {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/44", 'persist' => true}
358
+ test 'remove_schedule removes a schedule' do
359
+ Resque.set_schedule(
360
+ 'some_ivar_job3',
361
+ 'cron' => '* * * * *',
362
+ 'class' => 'SomeIvarJob',
363
+ 'args' => '/tmp/44',
364
+ 'persist' => true
290
365
  )
291
366
  Resque::Scheduler.load_schedule!
292
- Resque.remove_schedule("some_ivar_job3")
293
- assert_equal nil, Resque.redis.hget(:schedules, "some_ivar_job3")
294
- assert Resque.redis.sismember(:schedules_changed, "some_ivar_job3")
367
+ Resque.remove_schedule('some_ivar_job3')
368
+ assert_equal nil, Resque.redis.hget(:schedules, 'some_ivar_job3')
369
+ assert Resque.redis.sismember(:schedules_changed, 'some_ivar_job3')
295
370
  assert_equal [], Resque.redis.smembers(:persisted_schedules)
296
371
  end
297
372
 
298
- test "persisted schedules" do
299
- Resque.set_schedule("some_ivar_job",
300
- {'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/2", 'persist' => true}
373
+ test 'persisted schedules' do
374
+ Resque.set_schedule(
375
+ 'some_ivar_job',
376
+ 'cron' => '* * * * *',
377
+ 'class' => 'SomeIvarJob',
378
+ 'args' => '/tmp/2',
379
+ 'persist' => true
301
380
  )
302
- Resque.set_schedule("new_ivar_job",
303
- {'cron' => "* * * * *", 'class' => 'SomeJob', 'args' => "/tmp/3"}
381
+ Resque.set_schedule(
382
+ 'new_ivar_job',
383
+ 'cron' => '* * * * *',
384
+ 'class' => 'SomeJob',
385
+ 'args' => '/tmp/3 '
304
386
  )
305
387
 
306
- Resque.schedule=({
307
- 'a_schedule' => {'cron' => "* * * * *", 'class' => 'SomeOtherJob', 'args' => '/tmp'}
308
- })
388
+ Resque.schedule = {
389
+ 'a_schedule' => {
390
+ 'cron' => '* * * * *', 'class' => 'SomeOtherJob', 'args' => '/tmp'
391
+ }
392
+ }
309
393
  Resque::Scheduler.load_schedule!
310
394
 
311
- assert_equal({'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/2"},
312
- Resque.schedule['some_ivar_job'])
395
+ assert_equal(
396
+ { 'cron' => '* * * * *', 'class' => 'SomeIvarJob', 'args' => '/tmp/2' },
397
+ Resque.schedule['some_ivar_job']
398
+ )
313
399
  assert_equal(nil, Resque.schedule['some_job'])
314
400
  end
315
401
 
316
- test "adheres to lint" do
402
+ test 'adheres to lint' do
317
403
  assert_nothing_raised do
318
404
  Resque::Plugin.lint(Resque::Scheduler)
319
- Resque::Plugin.lint(ResqueScheduler)
405
+ Resque::Plugin.lint(Resque::Scheduler::Extension)
320
406
  end
321
407
  end
322
408
 
@@ -341,4 +427,19 @@ context 'Resque::Scheduler' do
341
427
  assert Resque::Scheduler.send(:build_procline, 'cage') =~
342
428
  /#{Resque::Scheduler.send(:internal_name)}: cage/
343
429
  end
430
+
431
+ context 'printing schedule' do
432
+ setup do
433
+ Resque::Scheduler.expects(:log!).at_least_once
434
+ end
435
+
436
+ test 'prints schedule' do
437
+ fake_rufus_scheduler = mock
438
+ fake_rufus_scheduler.expects(:all_jobs).at_least_once
439
+ .returns(foo: OpenStruct.new(t: nil, last: nil))
440
+ Resque::Scheduler.expects(:rufus_scheduler).at_least_once
441
+ .returns(fake_rufus_scheduler)
442
+ Resque::Scheduler.print_schedule
443
+ end
444
+ end
344
445
  end