nogara-resque-scheduler 2.0.1

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.
@@ -0,0 +1,25 @@
1
+ module ResqueScheduler
2
+ module Plugin
3
+ extend self
4
+ def hooks(job, pattern)
5
+ job.methods.grep(/^#{pattern}/).sort
6
+ end
7
+
8
+ def run_hooks(job, pattern, *args)
9
+ results = hooks(job, pattern).collect do |hook|
10
+ job.send(hook, *args)
11
+ end
12
+
13
+ results.all? { |result| result != false }
14
+ end
15
+
16
+ def method_missing(method_name, *args, &block)
17
+ if method_name.to_s =~ /^run_(.*)_hooks$/
18
+ job = args.shift
19
+ run_hooks job, $1, *args
20
+ else
21
+ super
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,69 @@
1
+ require 'resque_scheduler'
2
+ require 'resque/server'
3
+
4
+ # Extend Resque::Server to add tabs
5
+ module ResqueScheduler
6
+
7
+ module Server
8
+
9
+ def self.included(base)
10
+
11
+ base.class_eval do
12
+
13
+ helpers do
14
+ def format_time(t)
15
+ t.strftime("%Y-%m-%d %H:%M:%S %z")
16
+ end
17
+
18
+ def queue_from_class_name(class_name)
19
+ Resque.queue_from_class(Resque.constantize(class_name))
20
+ end
21
+ end
22
+
23
+ get "/schedule" do
24
+ Resque.reload_schedule! if Resque::Scheduler.dynamic
25
+ # Is there a better way to specify alternate template locations with sinatra?
26
+ erb File.read(File.join(File.dirname(__FILE__), 'server/views/scheduler.erb'))
27
+ end
28
+
29
+ post "/schedule/requeue" do
30
+ config = Resque.schedule[params['job_name']]
31
+ Resque::Scheduler.enqueue_from_config(config)
32
+ redirect u("/overview")
33
+ end
34
+
35
+ get "/delayed" do
36
+ # Is there a better way to specify alternate template locations with sinatra?
37
+ erb File.read(File.join(File.dirname(__FILE__), 'server/views/delayed.erb'))
38
+ end
39
+
40
+ get "/delayed/:timestamp" do
41
+ # Is there a better way to specify alternate template locations with sinatra?
42
+ erb File.read(File.join(File.dirname(__FILE__), 'server/views/delayed_timestamp.erb'))
43
+ end
44
+
45
+ post "/delayed/queue_now" do
46
+ timestamp = params['timestamp']
47
+ Resque::Scheduler.enqueue_delayed_items_for_timestamp(timestamp.to_i) if timestamp.to_i > 0
48
+ redirect u("/overview")
49
+ end
50
+
51
+ post "/delayed/clear" do
52
+ Resque.reset_delayed_queue
53
+ redirect u('delayed')
54
+ end
55
+
56
+ end
57
+
58
+ end
59
+
60
+ Resque::Server.tabs << 'Schedule'
61
+ Resque::Server.tabs << 'Delayed'
62
+
63
+ end
64
+
65
+ end
66
+
67
+ Resque::Server.class_eval do
68
+ include ResqueScheduler::Server
69
+ end
@@ -0,0 +1,48 @@
1
+ <h1>Delayed Jobs</h1>
2
+ <%- size = resque.delayed_queue_schedule_size %>
3
+ <% if size > 0 %>
4
+ <form method="POST" action="<%=u 'delayed/clear'%>" class='clear-delayed'>
5
+ <input type='submit' name='' value='Clear Delayed Jobs' />
6
+ </form>
7
+ <% end %>
8
+
9
+ <p class='intro'>
10
+ This list below contains the timestamps for scheduled delayed jobs.
11
+ </p>
12
+
13
+ <p class='sub'>
14
+ Showing <%= start = params[:start].to_i %> to <%= start + 20 %> of <b><%= size %></b> timestamps
15
+ </p>
16
+
17
+ <table>
18
+ <tr>
19
+ <th></th>
20
+ <th>Timestamp</th>
21
+ <th>Job count</th>
22
+ <th>Class</th>
23
+ <th>Args</th>
24
+ </tr>
25
+ <% resque.delayed_queue_peek(start, 20).each do |timestamp| %>
26
+ <tr>
27
+ <td>
28
+ <form action="<%= u "/delayed/queue_now" %>" method="post">
29
+ <input type="hidden" name="timestamp" value="<%= timestamp.to_i %>">
30
+ <input type="submit" value="Queue now">
31
+ </form>
32
+ </td>
33
+ <td><a href="<%= u "delayed/#{timestamp}" %>"><%= format_time(Time.at(timestamp)) %></a></td>
34
+ <td><%= delayed_timestamp_size = resque.delayed_timestamp_size(timestamp) %></td>
35
+ <% job = resque.delayed_timestamp_peek(timestamp, 0, 1).first %>
36
+ <td>
37
+ <% if job && delayed_timestamp_size == 1 %>
38
+ <%= h(job['class']) %>
39
+ <% else %>
40
+ <a href="<%= u "delayed/#{timestamp}" %>">see details</a>
41
+ <% end %>
42
+ </td>
43
+ <td><%= h(job['args'].inspect) if job && delayed_timestamp_size == 1 %></td>
44
+ </tr>
45
+ <% end %>
46
+ </table>
47
+
48
+ <%= partial :next_more, :start => start, :size => size %>
@@ -0,0 +1,26 @@
1
+ <% timestamp = params[:timestamp].to_i %>
2
+
3
+ <h1>Delayed jobs scheduled for <%= format_time(Time.at(timestamp)) %></h1>
4
+
5
+ <p class='sub'>Showing <%= start = params[:start].to_i %> to <%= start + 20 %> of <b><%=size = resque.delayed_timestamp_size(timestamp)%></b> jobs</p>
6
+
7
+ <table class='jobs'>
8
+ <tr>
9
+ <th>Class</th>
10
+ <th>Args</th>
11
+ </tr>
12
+ <% jobs = resque.delayed_timestamp_peek(timestamp, start, 20) %>
13
+ <% jobs.each do |job| %>
14
+ <tr>
15
+ <td class='class'><%= job['class'] %></td>
16
+ <td class='args'><%=h job['args'].inspect %></td>
17
+ </tr>
18
+ <% end %>
19
+ <% if jobs.empty? %>
20
+ <tr>
21
+ <td class='no-data' colspan='2'>There are no pending jobs scheduled for this time.</td>
22
+ </tr>
23
+ <% end %>
24
+ </table>
25
+
26
+ <%= partial :next_more, :start => start, :size => size %>
@@ -0,0 +1,43 @@
1
+ <h1>Schedule</h1>
2
+
3
+ <p class='intro'>
4
+ The list below contains all scheduled jobs. Click &quot;Queue now&quot; to queue
5
+ a job immediately.
6
+ </p>
7
+
8
+ <table>
9
+ <tr>
10
+ <th></th>
11
+ <th>Name</th>
12
+ <th>Description</th>
13
+ <th>Interval</th>
14
+ <th>Class</th>
15
+ <th>Queue</th>
16
+ <th>Arguments</th>
17
+ </tr>
18
+ <% Resque.schedule.keys.sort.each do |name| %>
19
+ <% config = Resque.schedule[name] %>
20
+ <tr>
21
+ <td>
22
+ <form action="<%= u "/schedule/requeue" %>" method="post">
23
+ <input type="hidden" name="job_name" value="<%= h name %>">
24
+ <input type="submit" value="Queue now">
25
+ </form>
26
+ </td>
27
+ <td><%= h name %></td>
28
+ <td><%= h config['description'] %></td>
29
+ <td style="white-space:nowrap"><%= if !config['every'].nil?
30
+ h('every: ' + config['every'])
31
+ elsif !config['cron'].nil?
32
+ h('cron: ' + config['cron'])
33
+ else
34
+ 'Not currently scheduled'
35
+ end %></td>
36
+ <td><%= (config['class'].nil? && !config['custom_job_class'].nil?) ?
37
+ h(config['custom_job_class']) :
38
+ h(config['class']) %></td>
39
+ <td><%= h config['queue'] || queue_from_class_name(config['class']) %></td>
40
+ <td><%= h config['args'].inspect %></td>
41
+ </tr>
42
+ <% end %>
43
+ </table>
@@ -0,0 +1,34 @@
1
+ require 'resque/tasks'
2
+ # will give you the resque tasks
3
+
4
+ namespace :resque do
5
+ task :setup
6
+
7
+ desc "Start Resque Scheduler"
8
+ task :scheduler => :scheduler_setup do
9
+ require 'resque'
10
+ require 'resque_scheduler'
11
+
12
+ if ENV['BACKGROUND']
13
+ unless Process.respond_to?('daemon')
14
+ abort "env var BACKGROUND is set, which requires ruby >= 1.9"
15
+ end
16
+ Process.daemon(true)
17
+ end
18
+
19
+ File.open(ENV['PIDFILE'], 'w') { |f| f << Process.pid.to_s } if ENV['PIDFILE']
20
+
21
+ Resque::Scheduler.dynamic = true if ENV['DYNAMIC_SCHEDULE']
22
+ Resque::Scheduler.verbose = true if ENV['VERBOSE']
23
+ Resque::Scheduler.run
24
+ end
25
+
26
+ task :scheduler_setup do
27
+ if ENV['INITIALIZER_PATH']
28
+ load ENV['INITIALIZER_PATH'].to_s.strip
29
+ else
30
+ Rake::Task['resque:setup'].invoke
31
+ end
32
+ end
33
+
34
+ end
@@ -0,0 +1,3 @@
1
+ module ResqueScheduler
2
+ VERSION = '2.0.1'
3
+ end
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.unshift File.expand_path("../lib", __FILE__)
3
+ require "resque_scheduler/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "nogara-resque-scheduler"
7
+ s.version = ResqueScheduler::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ['Ben VandenBos']
10
+ s.email = ['bvandenbos@gmail.com']
11
+ s.homepage = "http://github.com/bvandenbos/resque-scheduler"
12
+ s.summary = "Light weight job scheduling on top of Resque"
13
+ s.description = %q{Light weight job scheduling on top of Resque.
14
+ Adds methods enqueue_at/enqueue_in to schedule jobs in the future.
15
+ Also supports queueing jobs on a fixed, cron-like schedule.}
16
+
17
+ s.required_rubygems_version = ">= 1.3.6"
18
+ s.add_development_dependency "bundler", ">= 1.0.0"
19
+
20
+ s.files = `git ls-files`.split("\n")
21
+ s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
22
+ s.require_path = 'lib'
23
+
24
+ s.add_runtime_dependency(%q<redis>, [">= 2.0.1"])
25
+ s.add_runtime_dependency(%q<resque>, [">= 1.20.0"])
26
+ s.add_runtime_dependency(%q<rufus-scheduler>, [">= 0"])
27
+ end
@@ -0,0 +1,2 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
2
+ require 'resque_scheduler/tasks'
@@ -0,0 +1,329 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ context "DelayedQueue" do
4
+
5
+ setup do
6
+ Resque::Scheduler.mute = true
7
+ Resque.redis.flushall
8
+ end
9
+
10
+ test "enqueue_at adds correct list and zset" do
11
+ timestamp = Time.now - 1 # 1 second ago (in the past, should come out right away)
12
+
13
+ assert_equal(0, Resque.redis.llen("delayed:#{timestamp.to_i}").to_i, "delayed queue should be empty to start")
14
+
15
+ Resque.enqueue_at(timestamp, SomeIvarJob, "path")
16
+
17
+ # Confirm the correct keys were added
18
+ assert_equal(1, Resque.redis.llen("delayed:#{timestamp.to_i}").to_i, "delayed queue should have one entry now")
19
+ assert_equal(1, Resque.redis.zcard(:delayed_queue_schedule), "The delayed_queue_schedule should have 1 entry now")
20
+
21
+ read_timestamp = Resque.next_delayed_timestamp
22
+
23
+ # Confirm the timestamp came out correctly
24
+ assert_equal(timestamp.to_i, read_timestamp, "The timestamp we pull out of redis should match the one we put in")
25
+ item = Resque.next_item_for_timestamp(read_timestamp)
26
+
27
+ # Confirm the item came out correctly
28
+ assert_equal('SomeIvarJob', item['class'], "Should be the same class that we queued")
29
+ assert_equal(["path"], item['args'], "Should have the same arguments that we queued")
30
+
31
+ # And now confirm the keys are gone
32
+ assert(!Resque.redis.exists("delayed:#{timestamp.to_i}"))
33
+ assert_equal(0, Resque.redis.zcard(:delayed_queue_schedule), "delayed queue should be empty")
34
+ end
35
+
36
+ test "enqueue_at with queue adds correct list and zset and queue" do
37
+ timestamp = Time.now - 1 # 1 second ago (in the past, should come out right away)
38
+
39
+ assert_equal(0, Resque.redis.llen("delayed:#{timestamp.to_i}").to_i, "delayed queue should be empty to start")
40
+
41
+ Resque.enqueue_at_with_queue('critical', timestamp, SomeIvarJob, "path")
42
+
43
+ # Confirm the correct keys were added
44
+ assert_equal(1, Resque.redis.llen("delayed:#{timestamp.to_i}").to_i, "delayed queue should have one entry now")
45
+ assert_equal(1, Resque.redis.zcard(:delayed_queue_schedule), "The delayed_queue_schedule should have 1 entry now")
46
+
47
+ read_timestamp = Resque.next_delayed_timestamp
48
+
49
+ # Confirm the timestamp came out correctly
50
+ assert_equal(timestamp.to_i, read_timestamp, "The timestamp we pull out of redis should match the one we put in")
51
+ item = Resque.next_item_for_timestamp(read_timestamp)
52
+
53
+ # Confirm the item came out correctly
54
+ assert_equal('SomeIvarJob', item['class'], "Should be the same class that we queued")
55
+ assert_equal(["path"], item['args'], "Should have the same arguments that we queued")
56
+ assert_equal('critical', item['queue'], "Should have the queue that we asked for")
57
+
58
+ # And now confirm the keys are gone
59
+ assert(!Resque.redis.exists("delayed:#{timestamp.to_i}"))
60
+ assert_equal(0, Resque.redis.zcard(:delayed_queue_schedule), "delayed queue should be empty")
61
+ end
62
+
63
+ test "a job in the future doesn't come out" do
64
+ timestamp = Time.now + 600 # 10 minutes from now (in the future, shouldn't come out)
65
+
66
+ assert_equal(0, Resque.redis.llen("delayed:#{timestamp.to_i}").to_i, "delayed queue should be empty to start")
67
+
68
+ Resque.enqueue_at(timestamp, SomeIvarJob, "path")
69
+
70
+ # Confirm the correct keys were added
71
+ assert_equal(1, Resque.redis.llen("delayed:#{timestamp.to_i}").to_i, "delayed queue should have one entry now")
72
+ assert_equal(1, Resque.redis.zcard(:delayed_queue_schedule), "The delayed_queue_schedule should have 1 entry now")
73
+
74
+ read_timestamp = Resque.next_delayed_timestamp
75
+
76
+ assert_nil(read_timestamp, "No timestamps should be ready for queueing")
77
+ end
78
+
79
+ test "a job in the future comes out if you want it to" do
80
+ timestamp = Time.now + 600 # 10 minutes from now
81
+
82
+ Resque.enqueue_at(timestamp, SomeIvarJob, "path")
83
+
84
+ read_timestamp = Resque.next_delayed_timestamp(timestamp)
85
+
86
+ assert_equal(timestamp.to_i, read_timestamp, "The timestamp we pull out of redis should match the one we put in")
87
+ end
88
+
89
+ test "enqueue_at and enqueue_in are equivelent" do
90
+ timestamp = Time.now + 60
91
+
92
+ Resque.enqueue_at(timestamp, SomeIvarJob, "path")
93
+ Resque.enqueue_in(timestamp - Time.now, SomeIvarJob, "path")
94
+
95
+ assert_equal(1, Resque.redis.zcard(:delayed_queue_schedule), "should have one timestamp in the delayed queue")
96
+ assert_equal(2, Resque.redis.llen("delayed:#{timestamp.to_i}"), "should have 2 items in the timestamp queue")
97
+ end
98
+
99
+ test "empty delayed_queue_peek returns empty array" do
100
+ assert_equal([], Resque.delayed_queue_peek(0,20))
101
+ end
102
+
103
+ test "delqyed_queue_peek returns stuff" do
104
+ t = Time.now
105
+ expected_timestamps = (1..5).to_a.map do |i|
106
+ (t + 60 + i).to_i
107
+ end
108
+
109
+ expected_timestamps.each do |timestamp|
110
+ Resque.delayed_push(timestamp, {:class => SomeIvarJob, :args => 'blah1'})
111
+ end
112
+
113
+ timestamps = Resque.delayed_queue_peek(1,2)
114
+
115
+ assert_equal(expected_timestamps[1,2], timestamps)
116
+ end
117
+
118
+ test "delayed_queue_schedule_size returns correct size" do
119
+ assert_equal(0, Resque.delayed_queue_schedule_size)
120
+ Resque.enqueue_at(Time.now+60, SomeIvarJob)
121
+ assert_equal(1, Resque.delayed_queue_schedule_size)
122
+ end
123
+
124
+ test "delayed_timestamp_size returns 0 when nothing is queue" do
125
+ t = Time.now + 60
126
+ assert_equal(0, Resque.delayed_timestamp_size(t))
127
+ end
128
+
129
+ test "delayed_timestamp_size returns 1 when one thing is queued" do
130
+ t = Time.now + 60
131
+ Resque.enqueue_at(t, SomeIvarJob)
132
+ assert_equal(1, Resque.delayed_timestamp_size(t))
133
+ end
134
+
135
+ test "delayed_timestamp_peek returns empty array when nothings in it" do
136
+ t = Time.now + 60
137
+ assert_equal([], Resque.delayed_timestamp_peek(t, 0, 1), "make sure it's an empty array, not nil")
138
+ end
139
+
140
+ test "delayed_timestamp_peek returns an array containing one job when one thing is queued" do
141
+ t = Time.now + 60
142
+ Resque.enqueue_at(t, SomeIvarJob)
143
+ assert_equal [{'args' => [], 'class' => 'SomeIvarJob', 'queue' => 'ivar'}], Resque.delayed_timestamp_peek(t, 0, 1)
144
+ end
145
+
146
+ test "delayed_timestamp_peek returns an array of multiple jobs when more than one job is queued" do
147
+ t = Time.now + 60
148
+ Resque.enqueue_at(t, SomeIvarJob)
149
+ Resque.enqueue_at(t, SomeIvarJob)
150
+ job = {'args' => [], 'class' => 'SomeIvarJob', 'queue' => 'ivar'}
151
+ assert_equal([job, job], Resque.delayed_timestamp_peek(t, 0, 2))
152
+ end
153
+
154
+ test "delayed_timestamp_peek only returns an array of one job if only asked for 1" do
155
+ t = Time.now + 60
156
+ Resque.enqueue_at(t, SomeIvarJob)
157
+ Resque.enqueue_at(t, SomeIvarJob)
158
+ job = {'args' => [], 'class' => 'SomeIvarJob', 'queue' => 'ivar'}
159
+ assert_equal([job], Resque.delayed_timestamp_peek(t, 0, 1))
160
+ end
161
+
162
+ test "handle_delayed_items with no items" do
163
+ Resque::Scheduler.expects(:enqueue).never
164
+ Resque::Scheduler.handle_delayed_items
165
+ end
166
+
167
+ test "handle_delayed_item with items" do
168
+ t = Time.now - 60 # in the past
169
+ Resque.enqueue_at(t, SomeIvarJob)
170
+ Resque.enqueue_at(t, SomeIvarJob)
171
+
172
+ # 2 SomeIvarJob jobs should be created in the "ivar" queue
173
+ Resque::Job.expects(:create).twice.with('ivar', SomeIvarJob, nil)
174
+ Resque::Scheduler.handle_delayed_items
175
+ end
176
+
177
+ test "handle_delayed_items with items in the future" do
178
+ t = Time.now + 60 # in the future
179
+ Resque.enqueue_at(t, SomeIvarJob)
180
+ Resque.enqueue_at(t, SomeIvarJob)
181
+
182
+ # 2 SomeIvarJob jobs should be created in the "ivar" queue
183
+ Resque::Job.expects(:create).twice.with('ivar', SomeIvarJob, nil)
184
+ Resque::Scheduler.handle_delayed_items(t)
185
+ end
186
+
187
+ test "enqueue_delayed_items_for_timestamp creates jobs and empties the delayed queue" do
188
+ t = Time.now + 60
189
+
190
+ Resque.enqueue_at(t, SomeIvarJob)
191
+ Resque.enqueue_at(t, SomeIvarJob)
192
+
193
+ # 2 SomeIvarJob jobs should be created in the "ivar" queue
194
+ Resque::Job.expects(:create).twice.with('ivar', SomeIvarJob, nil)
195
+
196
+ Resque::Scheduler.enqueue_delayed_items_for_timestamp(t)
197
+
198
+ # delayed queue for timestamp should be empty
199
+ assert_equal(0, Resque.delayed_timestamp_peek(t, 0, 3).length)
200
+ end
201
+
202
+ test "handle_delayed_items works with out specifying queue (upgrade case)" do
203
+ t = Time.now - 60
204
+ Resque.delayed_push(t, :class => 'SomeIvarJob')
205
+
206
+ # Since we didn't specify :queue when calling delayed_push, it will be forced
207
+ # to load the class to figure out the queue. This is the upgrade case from 1.0.4
208
+ # to 1.0.5.
209
+ Resque::Job.expects(:create).once.with(:ivar, SomeIvarJob, nil)
210
+
211
+ Resque::Scheduler.handle_delayed_items
212
+ end
213
+
214
+ test "reset_delayed_queue clears the queue" do
215
+ t = Time.now + 120
216
+ 4.times { Resque.enqueue_at(t, SomeIvarJob) }
217
+ 4.times { Resque.enqueue_at(Time.now + rand(100), SomeIvarJob) }
218
+
219
+ Resque.reset_delayed_queue
220
+ assert_equal(0, Resque.delayed_queue_schedule_size)
221
+ end
222
+
223
+ test "remove_delayed removes job and returns the count" do
224
+ t = Time.now + 120
225
+ Resque.enqueue_at(t, SomeIvarJob)
226
+
227
+ assert_equal(1, Resque.remove_delayed(SomeIvarJob))
228
+ end
229
+
230
+ test "remove_delayed doesn't remove things it shouldn't" do
231
+ t = Time.now + 120
232
+ Resque.enqueue_at(t, SomeIvarJob, "foo")
233
+ Resque.enqueue_at(t, SomeIvarJob, "bar")
234
+ Resque.enqueue_at(t, SomeIvarJob, "bar")
235
+ Resque.enqueue_at(t, SomeIvarJob, "baz")
236
+
237
+ assert_equal(0, Resque.remove_delayed(SomeIvarJob))
238
+ end
239
+
240
+ test "remove_delayed respected param" do
241
+ t = Time.now + 120
242
+ Resque.enqueue_at(t, SomeIvarJob, "foo")
243
+ Resque.enqueue_at(t, SomeIvarJob, "bar")
244
+ Resque.enqueue_at(t, SomeIvarJob, "bar")
245
+ Resque.enqueue_at(t, SomeIvarJob, "baz")
246
+
247
+ assert_equal(2, Resque.remove_delayed(SomeIvarJob, "bar"))
248
+ assert_equal(1, Resque.delayed_queue_schedule_size)
249
+ end
250
+
251
+ test "remove_delayed removes items in different timestamps" do
252
+ t = Time.now + 120
253
+ Resque.enqueue_at(t, SomeIvarJob, "foo")
254
+ Resque.enqueue_at(t + 1, SomeIvarJob, "bar")
255
+ Resque.enqueue_at(t + 2, SomeIvarJob, "bar")
256
+ Resque.enqueue_at(t + 3, SomeIvarJob, "baz")
257
+
258
+ assert_equal(2, Resque.remove_delayed(SomeIvarJob, "bar"))
259
+ assert_equal(2, Resque.count_all_scheduled_jobs)
260
+ end
261
+
262
+ test "remove_delayed_job_from_timestamp removes instances of jobs at a given timestamp" do
263
+ t = Time.now + 120
264
+ Resque.enqueue_at(t, SomeIvarJob, "foo")
265
+ assert_equal 1, Resque.remove_delayed_job_from_timestamp(t, SomeIvarJob, "foo")
266
+ assert_equal 0, Resque.delayed_timestamp_size(t)
267
+ end
268
+
269
+ test "remove_delayed_job_from_timestamp doesn't remove items from other timestamps" do
270
+ t1 = Time.now + 120
271
+ t2 = t1 + 1
272
+ Resque.enqueue_at(t1, SomeIvarJob, "foo")
273
+ Resque.enqueue_at(t2, SomeIvarJob, "foo")
274
+ assert_equal 1, Resque.remove_delayed_job_from_timestamp(t2, SomeIvarJob, "foo")
275
+ assert_equal 1, Resque.delayed_timestamp_size(t1)
276
+ assert_equal 0, Resque.delayed_timestamp_size(t2)
277
+ end
278
+
279
+ test "remove_delayed_job_from_timestamp removes nothing if there are no matches" do
280
+ t = Time.now + 120
281
+ assert_equal 0, Resque.remove_delayed_job_from_timestamp(t, SomeIvarJob, "foo")
282
+ end
283
+
284
+ test "remove_delayed_job_from_timestamp only removes items that match args" do
285
+ t = Time.now + 120
286
+ Resque.enqueue_at(t, SomeIvarJob, "foo")
287
+ Resque.enqueue_at(t, SomeIvarJob, "bar")
288
+ assert_equal 1, Resque.remove_delayed_job_from_timestamp(t, SomeIvarJob, "foo")
289
+ assert_equal 1, Resque.delayed_timestamp_size(t)
290
+ end
291
+
292
+ test "remove_delayed_job_from_timestamp returns the number of items removed" do
293
+ t = Time.now + 120
294
+ Resque.enqueue_at(t, SomeIvarJob, "foo")
295
+ assert_equal 1, Resque.remove_delayed_job_from_timestamp(t, SomeIvarJob, "foo")
296
+ end
297
+
298
+ test "remove_delayed_job_from_timestamp should cleanup the delayed timestamp list if not jobs are left" do
299
+ t = Time.now + 120
300
+ Resque.enqueue_at(t, SomeIvarJob, "foo")
301
+ assert_equal 1, Resque.remove_delayed_job_from_timestamp(t, SomeIvarJob, "foo")
302
+ assert !Resque.redis.exists("delayed:#{t.to_i}")
303
+ assert Resque.delayed_queue_peek(0, 100).empty?
304
+ end
305
+
306
+ test "invalid job class" do
307
+ assert_raise Resque::NoClassError do
308
+ Resque.enqueue_in(10, nil)
309
+ end
310
+ assert_raise Resque::NoQueueError do
311
+ Resque.enqueue_in(10, String) # string serves as invalid Job class
312
+ end
313
+ end
314
+
315
+ test "inlining jobs with Resque.inline config" do
316
+ begin
317
+ Resque.inline = true
318
+ Resque::Job.expects(:create).once.with(:ivar, SomeIvarJob, "foo", "bar")
319
+
320
+ timestamp = Time.now + 120
321
+ Resque.enqueue_at(timestamp, SomeIvarJob, "foo", "bar")
322
+
323
+ assert_equal 0, Resque.count_all_scheduled_jobs
324
+ assert !Resque.redis.exists("delayed:#{timestamp.to_i}")
325
+ ensure
326
+ Resque.inline = false
327
+ end
328
+ end
329
+ end