tresque 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE +20 -0
- data/README.md +186 -0
- data/Rakefile +1 -0
- data/lib/tresque.rb +24 -0
- data/lib/tresque/delay.rb +115 -0
- data/lib/tresque/delay_execution_worker.rb +58 -0
- data/lib/tresque/queue_lock.rb +70 -0
- data/lib/tresque/registry.rb +94 -0
- data/lib/tresque/resque_spec/ext.rb +92 -0
- data/lib/tresque/resque_spec/helpers.rb +25 -0
- data/lib/tresque/resque_spec/matchers.rb +318 -0
- data/lib/tresque/resque_spec/resque_spec.rb +201 -0
- data/lib/tresque/resque_spec/scheduler.rb +93 -0
- data/lib/tresque/spec.rb +2 -0
- data/lib/tresque/spec/delay.rb +27 -0
- data/lib/tresque/util.rb +16 -0
- data/lib/tresque/version.rb +3 -0
- data/lib/tresque/worker.rb +228 -0
- data/lib/tresque/worker_lock.rb +53 -0
- data/tresque.gemspec +30 -0
- metadata +141 -0
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'resque-bus'
|
2
|
+
|
3
|
+
module TResque
|
4
|
+
class Registry
|
5
|
+
class << self
|
6
|
+
def default_weight
|
7
|
+
100
|
8
|
+
end
|
9
|
+
|
10
|
+
def class_hash
|
11
|
+
@class_hash ||= {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def queue_hash
|
15
|
+
@queue_hash ||= {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def class_list
|
19
|
+
class_hash.keys
|
20
|
+
end
|
21
|
+
|
22
|
+
def worker(klass)
|
23
|
+
klass = klass.name if klass.is_a?(Class)
|
24
|
+
klass = klass.to_s
|
25
|
+
class_hash[klass] = 1
|
26
|
+
end
|
27
|
+
|
28
|
+
def queue(queue_name, weight=nil)
|
29
|
+
queue_name = queue_name.to_s
|
30
|
+
if weight
|
31
|
+
# take higher weight
|
32
|
+
if !queue_hash[queue_name] || weight > queue_hash[queue_name]
|
33
|
+
queue_hash[queue_name] = weight
|
34
|
+
end
|
35
|
+
else
|
36
|
+
queue_hash[queue_name] ||= false
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def weight(key)
|
41
|
+
if !self.queue_hash[key]
|
42
|
+
self.default_weight
|
43
|
+
else
|
44
|
+
self.queue_hash[key]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# called to know what queues to set
|
49
|
+
def queues
|
50
|
+
register_classes
|
51
|
+
register_bus
|
52
|
+
sorted_queues
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
|
57
|
+
def register_classes
|
58
|
+
class_list.each do |klass_name|
|
59
|
+
klass = klass_name.constantize
|
60
|
+
queue(klass.queue)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def register_bus
|
65
|
+
manager = QueueBus::TaskManager.new(false)
|
66
|
+
manager.queue_names.each do |name|
|
67
|
+
queue(name)
|
68
|
+
end
|
69
|
+
queue("bus_incoming", 1)
|
70
|
+
end
|
71
|
+
|
72
|
+
def sorted_queues
|
73
|
+
array = queue_hash.keys.clone.shuffle
|
74
|
+
hash = {}
|
75
|
+
array.each do |key|
|
76
|
+
hash[key] = self.weight(key)
|
77
|
+
end
|
78
|
+
# sorted with highest weight first
|
79
|
+
array.sort!{ |x,y| hash[y] <=> hash[x] }
|
80
|
+
array
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
attr_reader :app_key
|
85
|
+
def initialize(app_key)
|
86
|
+
@app_key = Util.normalize(app_key)
|
87
|
+
end
|
88
|
+
|
89
|
+
def queue(name, weight=nil)
|
90
|
+
queue_name = "#{app_key}_#{name}"
|
91
|
+
::TResque::Registry.queue(queue_name, weight)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'resque'
|
2
|
+
|
3
|
+
module Resque
|
4
|
+
class Job
|
5
|
+
class << self
|
6
|
+
alias :create_without_resque_spec :create
|
7
|
+
alias :destroy_without_resque_spec :destroy
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.create(queue, klass, *args)
|
11
|
+
return create_without_resque_spec(queue, klass, *args) if ResqueSpec.disable_ext
|
12
|
+
|
13
|
+
raise ::Resque::NoQueueError.new("Jobs must be placed onto a queue.") if !queue
|
14
|
+
raise ::Resque::NoClassError.new("Jobs must be given a class.") if klass.to_s.empty?
|
15
|
+
ResqueSpec.enqueue(queue, klass, *args)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.destroy(queue, klass, *args)
|
19
|
+
return destroy_without_resque_spec(queue, klass, *args) if ResqueSpec.disable_ext
|
20
|
+
|
21
|
+
raise ::Resque::NoQueueError.new("Jobs must have been placed onto a queue.") if !queue
|
22
|
+
raise ::Resque::NoClassError.new("Jobs must have been given a class.") if klass.to_s.empty?
|
23
|
+
|
24
|
+
old_count = ResqueSpec.queue_by_name(queue).size
|
25
|
+
|
26
|
+
ResqueSpec.dequeue(queue, klass, *args)
|
27
|
+
|
28
|
+
old_count - ResqueSpec.queue_by_name(queue).size
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
alias :enqueue_without_resque_spec :enqueue
|
33
|
+
alias :enqueue_to_without_resque_spec :enqueue_to if Resque.respond_to? :enqueue_to
|
34
|
+
alias :reserve_without_resque_spec :reserve
|
35
|
+
alias :peek_without_resque_spec :peek
|
36
|
+
alias :size_without_resque_spec :size
|
37
|
+
|
38
|
+
def enqueue(klass, *args)
|
39
|
+
return enqueue_without_resque_spec(klass, *args) if ResqueSpec.disable_ext
|
40
|
+
|
41
|
+
enqueue_to(queue_from_class(klass), klass, *args)
|
42
|
+
end
|
43
|
+
|
44
|
+
def enqueue_to(queue, klass, *args)
|
45
|
+
return enqueue_to_without_resque_spec(queue, klass, *args) if ResqueSpec.disable_ext
|
46
|
+
|
47
|
+
if ResqueSpec.inline
|
48
|
+
return if run_before_enqueue(klass, *args)
|
49
|
+
run_after_enqueue(klass, *args)
|
50
|
+
Job.create(queue, klass, *args)
|
51
|
+
else
|
52
|
+
return if run_before_enqueue(klass, *args)
|
53
|
+
Job.create(queue, klass, *args)
|
54
|
+
run_after_enqueue(klass, *args)
|
55
|
+
true
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def peek(queue, start = 0, count = 1)
|
60
|
+
return peek_without_resque_spec(queue, start, count) if ResqueSpec.disable_ext
|
61
|
+
ResqueSpec.peek(queue, start, count).map do |job|
|
62
|
+
job.inject({}) { |a, (k, v)| a[k.to_s] = v; a }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def reserve(queue_name)
|
67
|
+
return reserve_without_resque_spec(queue_name) if ResqueSpec.disable_ext
|
68
|
+
|
69
|
+
ResqueSpec.pop(queue_name)
|
70
|
+
end
|
71
|
+
|
72
|
+
def size(queue_name)
|
73
|
+
return size_without_resque_spec(queue_name) if ResqueSpec.disable_ext
|
74
|
+
|
75
|
+
ResqueSpec.queue_by_name(queue_name).count
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def run_after_enqueue(klass, *args)
|
81
|
+
Plugin.after_enqueue_hooks(klass).each do |hook|
|
82
|
+
klass.send(hook, *args)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def run_before_enqueue(klass, *args)
|
87
|
+
before_hooks = Plugin.before_enqueue_hooks(klass).collect do |hook|
|
88
|
+
klass.send(hook, *args)
|
89
|
+
end
|
90
|
+
before_hooks.any? { |result| result == false }
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module ResqueSpec
|
2
|
+
module Helpers
|
3
|
+
|
4
|
+
def with_resque
|
5
|
+
original = ResqueSpec.inline
|
6
|
+
begin
|
7
|
+
ResqueSpec.inline = true
|
8
|
+
yield
|
9
|
+
ensure
|
10
|
+
ResqueSpec.inline = original
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def without_resque_spec
|
15
|
+
original = ResqueSpec.disable_ext
|
16
|
+
begin
|
17
|
+
ResqueSpec.disable_ext = true
|
18
|
+
yield
|
19
|
+
ensure
|
20
|
+
ResqueSpec.disable_ext = original
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,318 @@
|
|
1
|
+
require 'rspec/core'
|
2
|
+
require 'rspec/expectations'
|
3
|
+
require 'rspec/mocks'
|
4
|
+
|
5
|
+
module InQueueHelper
|
6
|
+
def self.extended(klass)
|
7
|
+
klass.instance_eval do
|
8
|
+
self.queue_name = nil
|
9
|
+
chain :in do |queue_name|
|
10
|
+
self.queue_name = queue_name
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
attr_accessor :queue_name
|
18
|
+
|
19
|
+
def queue(actual)
|
20
|
+
if @queue_name
|
21
|
+
ResqueSpec.queue_by_name(@queue_name)
|
22
|
+
else
|
23
|
+
ResqueSpec.queue_for(actual)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
RSpec::Matchers.define :have_queued do |*expected_args|
|
30
|
+
extend InQueueHelper
|
31
|
+
include InQueueHelper
|
32
|
+
chain :times do |num_times_queued|
|
33
|
+
@times = num_times_queued
|
34
|
+
@times_info = @times == 1 ? ' once' : " #{@times} times"
|
35
|
+
end
|
36
|
+
|
37
|
+
chain :once do |num_times_queued|
|
38
|
+
@times = 1
|
39
|
+
@times_info = ' once'
|
40
|
+
end
|
41
|
+
|
42
|
+
match do |actual|
|
43
|
+
matched = queue(actual).select do |entry|
|
44
|
+
klass = entry.fetch(:class)
|
45
|
+
args = entry.fetch(:args)
|
46
|
+
|
47
|
+
if expected_args.length == 0
|
48
|
+
klass.to_s == actual.to_s
|
49
|
+
elsif expected_args.length == 1 && expected_args[0].is_a?(Hash) && !expected_args[0].has_key?('tz') && !expected_args[0].has_key?('locale')
|
50
|
+
klass.to_s == actual.to_s && args[0].except('locale', 'tz') == expected_args[0]
|
51
|
+
else
|
52
|
+
klass.to_s == actual.to_s && expected_args == args
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
if @times
|
57
|
+
matched.size == @times
|
58
|
+
else
|
59
|
+
matched.size > 0
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
failure_message do |actual|
|
64
|
+
"expected that #{actual} would have [#{expected_args.join(', ')}] queued#{@times_info}"
|
65
|
+
end
|
66
|
+
|
67
|
+
failure_message_when_negated do |actual|
|
68
|
+
"expected that #{actual} would not have [#{expected_args.join(', ')}] queued#{@times_info}"
|
69
|
+
end
|
70
|
+
|
71
|
+
description do
|
72
|
+
"have queued arguments of [#{expected_args.join(', ')}]#{@times_info}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
RSpec::Matchers.define :have_queue_size_of do |size|
|
77
|
+
extend InQueueHelper
|
78
|
+
include InQueueHelper
|
79
|
+
|
80
|
+
match do |actual|
|
81
|
+
queue(actual).size == size
|
82
|
+
end
|
83
|
+
|
84
|
+
failure_message do |actual|
|
85
|
+
"expected that #{actual} would have #{size} entries queued, but got #{queue(actual).size} instead"
|
86
|
+
end
|
87
|
+
|
88
|
+
failure_message_when_negated do |actual|
|
89
|
+
"expected that #{actual} would not have #{size} entries queued, but got #{queue(actual).size} instead"
|
90
|
+
end
|
91
|
+
|
92
|
+
description do
|
93
|
+
"have a queue size of #{size}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
RSpec::Matchers.define :have_queue_size_of_at_least do |size|
|
98
|
+
extend InQueueHelper
|
99
|
+
include InQueueHelper
|
100
|
+
match do |actual|
|
101
|
+
queue(actual).size >= size
|
102
|
+
end
|
103
|
+
|
104
|
+
failure_message do |actual|
|
105
|
+
"expected that #{actual} would have at least #{size} entries queued, but got #{queue(actual).size} instead"
|
106
|
+
end
|
107
|
+
|
108
|
+
failure_message_when_negated do |actual|
|
109
|
+
"expected that #{actual} would not have at least #{size} entries queued, but got #{queue(actual).size} instead"
|
110
|
+
end
|
111
|
+
|
112
|
+
description do
|
113
|
+
"have a queue size of at least #{size}"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
module ScheduleQueueHelper
|
118
|
+
def self.extended(klass)
|
119
|
+
klass.instance_eval do
|
120
|
+
self.queue_name = nil
|
121
|
+
chain :queue do |queue_name|
|
122
|
+
self.queue_name = queue_name
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
attr_accessor :queue_name
|
128
|
+
|
129
|
+
def schedule_queue_for(actual)
|
130
|
+
if @queue_name
|
131
|
+
ResqueSpec.queue_by_name(@queue_name)
|
132
|
+
else
|
133
|
+
ResqueSpec.schedule_for(actual)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
RSpec::Matchers.define :have_scheduled do |*expected_args|
|
140
|
+
extend ScheduleQueueHelper
|
141
|
+
include ScheduleQueueHelper
|
142
|
+
chain :at do |timestamp|
|
143
|
+
@interval = nil
|
144
|
+
@time = timestamp
|
145
|
+
@time_info = "at #{@time}"
|
146
|
+
end
|
147
|
+
|
148
|
+
chain :in do |interval|
|
149
|
+
@time = nil
|
150
|
+
@interval = interval
|
151
|
+
@time_info = "in #{@interval} seconds"
|
152
|
+
end
|
153
|
+
|
154
|
+
match do |actual|
|
155
|
+
schedule_queue_for(actual).any? do |entry|
|
156
|
+
class_matches = entry[:class].to_s == actual.to_s
|
157
|
+
args_match = begin
|
158
|
+
if expected_args.length == 1 && expected_args[0].is_a?(Hash) && !expected_args[0].has_key?('tz') && !expected_args[0].has_key?('locale')
|
159
|
+
entry[:args][0].except('tz', 'locale') == expected_args[0]
|
160
|
+
else
|
161
|
+
entry[:args] == expected_args
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
time_matches = if @time
|
166
|
+
entry[:time] == @time
|
167
|
+
elsif @interval
|
168
|
+
entry[:time].to_i == (entry[:stored_at] + @interval).to_i
|
169
|
+
else
|
170
|
+
true
|
171
|
+
end
|
172
|
+
|
173
|
+
class_matches && args_match && time_matches
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
failure_message do |actual|
|
178
|
+
["expected that #{actual} would have [#{expected_args.join(', ')}] scheduled", @time_info].join(' ')
|
179
|
+
end
|
180
|
+
|
181
|
+
failure_message_when_negated do |actual|
|
182
|
+
["expected that #{actual} would not have [#{expected_args.join(', ')}] scheduled", @time_info].join(' ')
|
183
|
+
end
|
184
|
+
|
185
|
+
description do
|
186
|
+
"have scheduled arguments"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
RSpec::Matchers.define :have_scheduled_at do |*expected_args|
|
191
|
+
extend ScheduleQueueHelper
|
192
|
+
warn "DEPRECATION WARNING: have_scheduled_at(time, *args) is deprecated and will be removed in future. Please use have_scheduled(*args).at(time) instead."
|
193
|
+
|
194
|
+
match do |actual|
|
195
|
+
time = expected_args.first
|
196
|
+
other_args = expected_args[1..-1]
|
197
|
+
schedule_queue_for(actual).any? { |entry| entry[:class].to_s == actual.to_s && entry[:time] == time && other_args == entry[:args] }
|
198
|
+
end
|
199
|
+
|
200
|
+
failure_message do |actual|
|
201
|
+
"expected that #{actual} would have [#{expected_args.join(', ')}] scheduled"
|
202
|
+
end
|
203
|
+
|
204
|
+
failure_message_when_negated do |actual|
|
205
|
+
"expected that #{actual} would not have [#{expected_args.join(', ')}] scheduled"
|
206
|
+
end
|
207
|
+
|
208
|
+
description do
|
209
|
+
"have scheduled at the given time the arguments"
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
RSpec::Matchers.define :have_schedule_size_of do |size|
|
214
|
+
extend ScheduleQueueHelper
|
215
|
+
|
216
|
+
match do |actual|
|
217
|
+
schedule_queue_for(actual).size == size
|
218
|
+
end
|
219
|
+
|
220
|
+
failure_message do |actual|
|
221
|
+
"expected that #{actual} would have #{size} scheduled entries, but got #{schedule_queue_for(actual).size} instead"
|
222
|
+
end
|
223
|
+
|
224
|
+
failure_message_when_negated do |actual|
|
225
|
+
"expected that #{actual} would have #{size} scheduled entries."
|
226
|
+
end
|
227
|
+
|
228
|
+
description do
|
229
|
+
"have schedule size of #{size}"
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
RSpec::Matchers.define :have_schedule_size_of_at_least do |size|
|
234
|
+
extend ScheduleQueueHelper
|
235
|
+
|
236
|
+
match do |actual|
|
237
|
+
schedule_queue_for(actual).size >= size
|
238
|
+
end
|
239
|
+
|
240
|
+
failure_message do |actual|
|
241
|
+
"expected that #{actual} would have at least #{size} scheduled entries, but got #{schedule_queue_for(actual).size} instead"
|
242
|
+
end
|
243
|
+
|
244
|
+
failure_message_when_negated do |actual|
|
245
|
+
"expected that #{actual} would have at least #{size} scheduled entries."
|
246
|
+
end
|
247
|
+
|
248
|
+
description do
|
249
|
+
"have schedule size of #{size}"
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
|
254
|
+
RSpec::Matchers.define :have_delayed do |method_name|
|
255
|
+
extend InQueueHelper
|
256
|
+
include InQueueHelper
|
257
|
+
|
258
|
+
chain :until do |timestamp|
|
259
|
+
@when = timestamp.to_i
|
260
|
+
end
|
261
|
+
|
262
|
+
chain :with do |*args|
|
263
|
+
@args = args
|
264
|
+
end
|
265
|
+
|
266
|
+
chain :in_lock_namespace do |ns|
|
267
|
+
@lock_namespace = ns.to_s
|
268
|
+
end
|
269
|
+
|
270
|
+
chain :with_queue_lock do |lock|
|
271
|
+
@queue_lock = lock.to_s
|
272
|
+
end
|
273
|
+
|
274
|
+
chain :with_worker_lock do |lock|
|
275
|
+
@worker_lock = lock.to_s
|
276
|
+
end
|
277
|
+
|
278
|
+
match do |class_name|
|
279
|
+
|
280
|
+
class_name = case class_name
|
281
|
+
when Class
|
282
|
+
class_name.name
|
283
|
+
when String, Symbol
|
284
|
+
class_name
|
285
|
+
else # instances of objects
|
286
|
+
class_name.class.name
|
287
|
+
end
|
288
|
+
|
289
|
+
[@queue_name || ResqueSpec.queues].flatten.compact.each do |queue|
|
290
|
+
ResqueSpec.queue_by_name(queue).detect do |entry|
|
291
|
+
klass = entry[:class]
|
292
|
+
args = entry[:args]
|
293
|
+
data = args.first if args.is_a?(Array)
|
294
|
+
|
295
|
+
matched = data['class_name'] == class_name.to_s
|
296
|
+
matched &&= data['method_name'] == method_name.to_s
|
297
|
+
matched &&= data['run_at'] == @when if @when
|
298
|
+
matched &&= data['args'] == @args if @args
|
299
|
+
matched &&= data['lock_namespace'] == @lock_namespace if @lock_namespace
|
300
|
+
matched &&= data['queue_lock'] == @queue_lock if @queue_lock
|
301
|
+
matched &&= data['worker_lock'] == @worker_lock if @worker_lock
|
302
|
+
matched
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
failure_message do |actual|
|
308
|
+
"expected that #{method_name} would have been a delayed invocation"
|
309
|
+
end
|
310
|
+
|
311
|
+
failure_message_when_negated do |actual|
|
312
|
+
"expected that #{method_name} would not have been delayed"
|
313
|
+
end
|
314
|
+
|
315
|
+
description do
|
316
|
+
"#{method_name} should be deplayed"
|
317
|
+
end
|
318
|
+
end
|