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.
@@ -0,0 +1,201 @@
1
+ require 'tresque/resque_spec/ext'
2
+ require 'tresque/resque_spec/helpers'
3
+ require 'tresque/resque_spec/matchers'
4
+
5
+ module ResqueSpec
6
+
7
+ # methods to be made accessible for every spec
8
+ module BaseMethods
9
+
10
+ def run_resque_workers(options = {})
11
+ quantity = options.fetch(:quantity, 1)
12
+
13
+ quantity.times do
14
+ ResqueSpec.perform_all!
15
+ end
16
+ end
17
+
18
+ end
19
+
20
+
21
+ include ::Resque::Helpers
22
+ extend self
23
+
24
+ attr_accessor :inline
25
+ attr_accessor :disable_ext
26
+
27
+ def dequeue(queue_name, klass, *args)
28
+ queue_by_name(queue_name).delete_if do |job|
29
+ job[:class] == klass.to_s && args.empty? || job[:args] == args
30
+ end
31
+ end
32
+
33
+ def enqueue(queue_name, klass, *args)
34
+ perform_or_store(queue_name, :class => klass.to_s, :args => args)
35
+ end
36
+
37
+ def perform_next(queue_name)
38
+ perform(queue_name, shift_queue(queue_name))
39
+ end
40
+
41
+ def perform_all(queue_name)
42
+ while job = shift_queue(queue_name)
43
+ perform(queue_name, job)
44
+ end
45
+ end
46
+
47
+ def shift_queue(queue_name)
48
+ # pass scheduled ones
49
+ array = queue_by_name(queue_name) || []
50
+ index = nil
51
+ array.each_with_index do |hash, i|
52
+ if hash[:time].to_i <= Time.now.to_i
53
+ index = i
54
+ break
55
+ end
56
+ end
57
+ return nil unless index
58
+ array.delete_at(index)
59
+ end
60
+
61
+ def pop(queue_name)
62
+ return unless payload = shift_queue(queue_name)
63
+ new_job(queue_name, payload)
64
+ end
65
+
66
+ def queue_by_name(name)
67
+ queues[name.to_s]
68
+ end
69
+
70
+ def queue_for(klass)
71
+ queue_by_name(queue_name(klass))
72
+ end
73
+
74
+ def peek(queue_name, start = 0, count = 1)
75
+ queue_by_name(queue_name).slice(start, count)
76
+ end
77
+
78
+ def queue_name(klass)
79
+ if klass.is_a?(String)
80
+ klass = Kernel.const_get(klass) rescue nil
81
+ end
82
+
83
+ name_from_instance_var(klass) or
84
+ name_from_queue_accessor(klass) or
85
+ raise ::Resque::NoQueueError.new("Jobs must be placed onto a queue.")
86
+ end
87
+
88
+ def queues
89
+ @queues ||= Hash.new {|h,k| h[k] = []}
90
+ end
91
+
92
+ def delete_all(queue_name)
93
+ queue = "queue:#{queue_name}"
94
+ Resque.redis.del(queue)
95
+ reset!
96
+ end
97
+
98
+ def perform_all!
99
+ ::TResque::Registry.queues.each do |queue_name|
100
+ ResqueSpec.perform_all(queue_name)
101
+ end
102
+ end
103
+
104
+ # Check if we have queued a delayed worker for `klass` with `*args`
105
+ # Very slow, checks every `delayed` key
106
+ def delayed?(klass, *args)
107
+ klass = klass.to_s unless klass.is_a? String
108
+ [*Resque.redis.keys("delayed:*")].each do |key|
109
+ [*Resque.redis.lrange(key, 0, -1)].each do |item|
110
+ parsed_item = JSON.parse(item)
111
+ return true if parsed_item['class'] == klass && parsed_item['args'] == [*args]
112
+ end
113
+ end
114
+
115
+ false
116
+ end
117
+
118
+ # Get back the key of the delayed worker if it exists
119
+ def delayed_key(klass, *args)
120
+ klass = klass.to_s unless klass.is_a? String
121
+ [*Resque.redis.keys("delayed:*")].each do |key|
122
+ [*Resque.redis.lrange(key, 0, -1)].each do |item|
123
+ parsed_item = JSON.parse(item)
124
+ return key if parsed_item['class'] == klass && parsed_item['args'] == [*args]
125
+ end
126
+ end
127
+
128
+ nil
129
+ end
130
+
131
+ # check if the worker has a lock with the provided args
132
+ def locked?(klass, *args)
133
+ klass = klass.to_s unless klass.is_a? String
134
+
135
+ key = "lock:#{klass}-#{[*args].join('-')}"
136
+ Resque.redis.keys(key).present?
137
+ end
138
+
139
+ def clear_locked!
140
+ [*Resque.redis.keys("lock:*")].each do |key|
141
+ Resque.redis.del(key)
142
+ end
143
+ end
144
+
145
+ def clear_all!
146
+ [*Resque.redis.keys].each do |key|
147
+ Resque.redis.del(key)
148
+ end
149
+ end
150
+
151
+ def reset!
152
+ clear_all!
153
+ queues.clear
154
+ self.inline = false
155
+ end
156
+
157
+ private
158
+
159
+ def name_from_instance_var(klass)
160
+ klass.instance_variable_get(:@queue)
161
+ end
162
+
163
+ def name_from_queue_accessor(klass)
164
+ klass.respond_to?(:queue) and klass.queue
165
+ end
166
+
167
+ def new_job(queue_name, payload)
168
+ Resque::Job.new(queue_name, payload_with_string_keys(payload))
169
+ end
170
+
171
+ def perform(queue_name, payload)
172
+ prev = ENV['QUEUE']
173
+ ENV['QUEUE'] = queue_name
174
+ new_job(queue_name, payload).perform
175
+ ensure
176
+ ENV['QUEUE'] = prev
177
+ end
178
+
179
+ def perform_or_store(queue_name, payload)
180
+ if inline
181
+ perform(queue_name, payload)
182
+ else
183
+ store(queue_name, payload)
184
+ end
185
+ end
186
+
187
+ def store(queue_name, payload)
188
+ queue_by_name(queue_name) << payload
189
+ end
190
+
191
+ def payload_with_string_keys(payload)
192
+ {
193
+ 'class' => payload[:class],
194
+ 'args' => decode(encode(payload[:args])),
195
+ 'stored_at' => payload[:stored_at]
196
+ }
197
+ end
198
+ end
199
+
200
+ config = RSpec.configuration
201
+ config.include ::ResqueSpec::Helpers
@@ -0,0 +1,93 @@
1
+ require_relative 'resque_spec'
2
+
3
+ module ResqueSpec
4
+ module SchedulerExt
5
+ def self.extended(klass)
6
+ if klass.respond_to? :enqueue_at
7
+ klass.instance_eval do
8
+ alias :enqueue_at_without_resque_spec :enqueue_at
9
+ alias :enqueue_in_without_resque_spec :enqueue_in
10
+ alias :remove_delayed_without_resque_spec :remove_delayed
11
+ end
12
+ end
13
+ klass.extend(ResqueSpec::SchedulerExtMethods)
14
+ end
15
+ end
16
+
17
+ module SchedulerExtMethods
18
+ def enqueue_at(time, klass, *args)
19
+ return enqueue_at_without_resque_spec(time, klass, *args) if ResqueSpec.disable_ext && respond_to?(:enqueue_at_without_resque_spec)
20
+
21
+ ResqueSpec.enqueue_at(time, klass, *args)
22
+ end
23
+
24
+ def enqueue_at_with_queue(queue, time, klass, *args)
25
+ return enqueue_at_with_queue_without_resque_spec(time, klass, *args) if ResqueSpec.disable_ext && respond_to?(:enqueue_at_with_queue_without_resque_spec)
26
+
27
+ ResqueSpec.enqueue_at_with_queue(queue, time, klass, *args)
28
+ end
29
+
30
+ def enqueue_in(time, klass, *args)
31
+ return enqueue_in_without_resque_spec(time, klass, *args) if ResqueSpec.disable_ext && respond_to?(:enqueue_in_without_resque_spec)
32
+
33
+ ResqueSpec.enqueue_in(time, klass, *args)
34
+ end
35
+
36
+ def enqueue_in_with_queue(queue, time, klass, *args)
37
+ return enqueue_in_with_queue_without_resque_spec(time, klass, *args) if ResqueSpec.disable_ext && respond_to?(:enqueue_in_with_queue_without_resque_spec)
38
+
39
+ ResqueSpec.enqueue_in_with_queue(queue, time, klass, *args)
40
+ end
41
+
42
+ def remove_delayed(klass, *args)
43
+ return remove_delayed_without_resque_spec(klass, *args) if ResqueSpec.disable_ext && respond_to?(:remove_delayed_without_resque_spec)
44
+
45
+ ResqueSpec.remove_delayed(klass, *args)
46
+ end
47
+ end
48
+
49
+ def enqueue_at(time, klass, *args)
50
+ enqueue_at_with_queue(schedule_queue_name(klass), time, klass, *args)
51
+ end
52
+
53
+ def enqueue_at_with_queue(queue, time, klass, *args)
54
+ is_time?(time)
55
+ perform_or_store(queue, :class => klass.to_s, :time => time, :stored_at => Time.now, :args => args)
56
+ end
57
+
58
+ def enqueue_in(time, klass, *args)
59
+ enqueue_at(Time.now + time, klass, *args)
60
+ end
61
+
62
+ def enqueue_in_with_queue(queue, time, klass, *args)
63
+ enqueue_at_with_queue(queue, Time.now + time, klass, *args)
64
+ end
65
+
66
+ def remove_delayed(klass, *args)
67
+ sched_queue = queue_by_name(schedule_queue_name(klass))
68
+ count_before_remove = sched_queue.length
69
+ sched_queue.delete_if do |job|
70
+ job[:class] == klass.to_s && job[:args] == args
71
+ end
72
+ # Return number of removed items to match Resque Scheduler behaviour
73
+ count_before_remove - sched_queue.length
74
+ end
75
+
76
+ def schedule_for(klass)
77
+ queue_by_name(schedule_queue_name(klass))
78
+ end
79
+
80
+ private
81
+
82
+ def is_time?(time)
83
+ time.to_i
84
+ end
85
+
86
+ def schedule_queue_name(klass)
87
+ # "#{queue_name(klass)}_scheduled"
88
+ # we use the real one
89
+ queue_name(klass)
90
+ end
91
+ end
92
+
93
+ Resque.extend(ResqueSpec::SchedulerExt)
@@ -0,0 +1,2 @@
1
+ require 'tresque/resque_spec/resque_spec'
2
+ require 'tresque/resque_spec/scheduler'
@@ -0,0 +1,27 @@
1
+ module TResque
2
+ module Spec
3
+ module Delay
4
+
5
+ def delay_object
6
+ TResque::Delay::InvocationProxy.any_instance
7
+ end
8
+
9
+ def without_delay
10
+ return yield unless Rails.env.test?
11
+
12
+ q = ENV['QUEUE']
13
+ qs = ENV['QUEUES']
14
+
15
+ ENV['QUEUE'] = "v3_default"
16
+ ENV['QUEUES'] = nil
17
+
18
+ yield
19
+
20
+ ensure
21
+ ENV['QUEUE'] = q
22
+ ENV['QUEUES'] = qs
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,16 @@
1
+ module TResque
2
+ module Util
3
+ extend self
4
+
5
+ def normalize(val)
6
+ val.to_s.gsub(/\W/, "_").downcase
7
+ end
8
+
9
+ def calculate_namespace_from_class(klass)
10
+ klass = klass.class unless klass.is_a?(Class)
11
+ pieces = klass.name.to_s.split("::")
12
+ return "worker" if pieces.size < 2
13
+ pieces.first.underscore
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ module TResque
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,228 @@
1
+ require 'resque-retry'
2
+ require 'digest/sha1'
3
+
4
+ module TResque
5
+ module Worker
6
+ extend ::ActiveSupport::Concern
7
+
8
+ mattr_accessor :skip_check_queues
9
+
10
+ included do
11
+ extend ::Resque::Plugins::ExponentialBackoff
12
+ TResque::Registry.worker(self)
13
+ end
14
+
15
+ module ClassMethods
16
+ def queue(name=nil)
17
+ return @requeue_in_queue if !name && @requeue_in_queue
18
+ return @queue if !name && @queue
19
+ name ||= :default
20
+ full_queue("#{app_key}_#{name}")
21
+ end
22
+
23
+ def full_queue(name)
24
+ @queue = name.to_s
25
+ end
26
+
27
+ def application(app_key)
28
+ @app_key = Util.normalize(app_key)
29
+ end
30
+
31
+ def app_key
32
+ return @app_key if @app_key
33
+ @app_key = Util.calculate_namespace_from_class(self)
34
+ end
35
+
36
+ def enqueue(options = {})
37
+ options = options.with_indifferent_access
38
+
39
+ options[:locale] ||= I18n.locale.to_s
40
+ options[:tz] ||= Time.zone.name
41
+
42
+ run_at = options.delete(:run_at)
43
+ if options[:full_queue]
44
+ queue_name = options[:full_queue]
45
+ elsif options[:queue] || options[:queue_namespace]
46
+ namespace = options[:queue_namespace] || self.app_key
47
+ queue = options[:queue] || "default"
48
+ queue_name = "#{namespace}_#{queue}"
49
+ else
50
+ queue_name = self.queue
51
+ end
52
+
53
+ if queue_name == "t_resque_default"
54
+ message = "QUEUE_ERROR (#{self.class.name}): #{queue_name} will not be worked!"
55
+ Rails.logger.error(message)
56
+ puts message if Rails.env.test?
57
+ end
58
+
59
+ if !TResque::Worker.skip_check_queues && !TResque::Registry.queues.include?(queue_name)
60
+ message = "QUEUE_ERROR (#{self.class.name}): #{queue_name} will not be worked!"
61
+ Rails.logger.error(message)
62
+ puts message if Rails.env.test?
63
+ end
64
+
65
+ options[:full_queue] = queue_name
66
+ if run_at
67
+ Resque.enqueue_at_with_queue(queue_name, run_at, self, options)
68
+ else
69
+ Resque.enqueue_to(queue_name, self, options)
70
+ end
71
+
72
+ # too many events
73
+ # QueueBus.publish_log(:worker_enqueued, {
74
+ # options: options,
75
+ # queue_name: queue_name,
76
+ # worker_name: self.name.to_s,
77
+ # run_at: run_at.to_i
78
+ # }) unless Rails.env.test?
79
+
80
+ options
81
+ end
82
+
83
+ def perform(options)
84
+ Waistband.clear_logs if Waistband.config.logging
85
+
86
+ @previous_locale, @previous_zone = I18n.locale, Time.zone
87
+
88
+ options = options.with_indifferent_access
89
+ obj = self.new(options.except(:locale, :tz, :bus_locale, :bus_timezone))
90
+
91
+ locale = obj.respond_to?(:calculate_locale, true) ? obj.send(:calculate_locale) : nil
92
+ locale ||= options[:locale]
93
+ locale ||= options[:bus_locale]
94
+ locale ||= I18n.locale if Rails.env.production? # don't crash in production, use default
95
+
96
+ zone = obj.respond_to?(:calculate_timezone, true) ? obj.send(:calculate_timezone) : nil
97
+ zone ||= options[:tz]
98
+ zone ||= options[:bus_timezone]
99
+
100
+ I18n.locale = locale
101
+ Time.zone = zone
102
+
103
+ # too many events
104
+ # QueueBus.publish_log(:worker_perform, {
105
+ # options: options,
106
+ # worker_name: self.name.to_s,
107
+ # locale: locale,
108
+ # time_zone: zone
109
+ # }) unless Rails.env.test?
110
+
111
+ obj.worker_perform
112
+ rescue Resque::Job::DontPerform
113
+ # it's cool
114
+ ensure
115
+ # write waistband logs
116
+ Waistband.write_logs(nil) if Waistband.config.logging
117
+ # reset
118
+ I18n.locale, Time.zone = @previous_locale, @previous_zone
119
+ end
120
+
121
+ def inputs(*args)
122
+ args.each do |name|
123
+ define_method name do
124
+ enqueued_options[name]
125
+ end
126
+ end
127
+ end
128
+ alias_method :input, :inputs
129
+
130
+ def turn_retry_off
131
+ @retry_limit = 0
132
+ end
133
+
134
+ def lock_namespace(val)
135
+ @lock_namespace = val.to_s
136
+ end
137
+
138
+ def worker_lock(*args)
139
+ raise ("worker_lock: what should i lock on?") if args.size == 0
140
+ extend ::TResque::WorkerLock
141
+ @worker_lock_attributes = args.collect(&:to_s)
142
+ end
143
+
144
+ def queue_lock(*args)
145
+ raise ("queue_lock: what should i lock on?") if args.size == 0
146
+ extend ::TResque::QueueLock
147
+ @queue_lock_attributes = args.collect(&:to_s)
148
+ end
149
+
150
+ def get_lock_namespace(options)
151
+ @lock_namespace ||= self.name
152
+ end
153
+
154
+ def get_queue_lock_attributes(options)
155
+ @queue_lock_attributes ||= []
156
+ end
157
+
158
+ def get_worker_lock_attributes(options)
159
+ @worker_lock_attributes ||= []
160
+ end
161
+
162
+ def queue_lock_key(options)
163
+ options_lock_key(options, get_queue_lock_attributes(options))
164
+ end
165
+
166
+ def worker_lock_key(options)
167
+ options_lock_key(options, get_worker_lock_attributes(options))
168
+ end
169
+
170
+ def options_lock_key(options, keys)
171
+ return nil unless keys # not actually locking
172
+
173
+ keys = ["all"] if keys.size == 0
174
+ keys = options.keys if keys.size == 1 && keys.first == "all"
175
+ keys.sort!
176
+
177
+ vals = [get_lock_namespace(options)]
178
+ keys.each do |key|
179
+ vals << key
180
+ vals << options[key].to_s
181
+ end
182
+ Digest::SHA1.hexdigest(vals.join("-"))
183
+ end
184
+
185
+ # make sure we put it back in the same queue
186
+ # @failure_hooks_already_ran on https://github.com/defunkt/resque/tree/1-x-stable
187
+ # to prevent running twice
188
+ def on_failure_aaa(exception, *args)
189
+ # note: sorted alphabetically
190
+ # queue needs to be set for rety to work (know what queue in Requeue.class_to_queue)
191
+ @requeue_in_queue = args[0]["full_queue"]
192
+ end
193
+
194
+ def on_failure_zzz(exception, *args)
195
+ # note: sorted alphabetically
196
+ @requeue_in_queue = nil
197
+ end
198
+ end
199
+
200
+ attr_reader :enqueued_options
201
+ def initialize(options = {})
202
+ @enqueued_options = options.with_indifferent_access
203
+ end
204
+
205
+ def worker_perform
206
+ to_call = self.enqueued_options[:action] || :work
207
+ send(to_call)
208
+ end
209
+
210
+ def requeue_delay_seconds
211
+ 1
212
+ end
213
+
214
+ def requeue
215
+ self.enqueued_options["run_at"] = nil
216
+ delay = self.requeue_delay_seconds
217
+ if delay > 0
218
+ self.enqueued_options["run_at"] = delay.seconds.from_now
219
+ end
220
+ self.class.enqueue(self.enqueued_options)
221
+ end
222
+
223
+ def requeue!
224
+ requeue
225
+ raise Resque::Job::DontPerform
226
+ end
227
+ end
228
+ end