nogara-resque-scheduler 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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