resque-scheduler 2.5.5 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.

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