faktory_worker_ruby 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a613d7ced7f98e8ec3112b9be6a31b367b1ce4bf
4
- data.tar.gz: 71a106dc660e67ecd0833a1e1815148de8022fb8
3
+ metadata.gz: 672affacf993528eedff478961a96bbc222e62bc
4
+ data.tar.gz: 8b58b6e46e4d6965690bc4a17f3067548671edd0
5
5
  SHA512:
6
- metadata.gz: 657cce3245c5c4dd8d790b041d212a20d2cb477dfcf54176040450df6ee7634da443f79b0e2076c5ab372176f8dda18a4dc2329c6a35b91bd94c47bba0236285
7
- data.tar.gz: 132a39b2db88d47f5b7e770f125b612326a34183982eb3231dea407cbaae16207601ddd580c7a7440ed9e388c3c066650b5b6c76bbc75cabb563d5a043a4462f
6
+ metadata.gz: 7210a8933baf0a671a608d41b5bb183fec3ea93b5c233c937c3882e921634d1baedb5985be1ecd6142ee1ca9257d3483012a7511f40c4f58fc4c13d0f34a4f4f
7
+ data.tar.gz: 1a2e769ad7b96d539842e29c7056e36523d878cb81ccda8610247b99e8f239877dfbe7caee93be6d194213801c2a31fbfb876c69f5a5850bad6b15461661c4e4
data/Changes.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changes
2
2
 
3
+ ## 0.7.0
4
+
5
+ - Add testing API, almost identical to Sidekiq's `sidekiq/testing` API.
6
+ [#7, thebadmonkeydev, jagthedrummer]
7
+
3
8
  ## 0.6.1
4
9
 
5
10
  - Fix password hashing
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- faktory_worker_ruby (0.6.1)
4
+ faktory_worker_ruby (0.7.0)
5
5
  connection_pool (~> 2.2, >= 2.2.1)
6
6
 
7
7
  GEM
@@ -22,4 +22,4 @@ DEPENDENCIES
22
22
  rake (~> 12)
23
23
 
24
24
  BUNDLED WITH
25
- 1.15.4
25
+ 1.16.0
@@ -7,7 +7,7 @@ Gem::Specification.new do |gem|
7
7
  gem.email = ["mike@contribsys.com"]
8
8
  gem.summary = "Ruby worker for Faktory"
9
9
  gem.description = "Ruby worker for Faktory."
10
- gem.homepage = "http://github.com/contribsys/faktory_worker_ruby"
10
+ gem.homepage = "https://github.com/contribsys/faktory_worker_ruby"
11
11
  gem.license = "LGPL-3.0"
12
12
 
13
13
  gem.executables = ['faktory-worker']
data/lib/faktory/cli.rb CHANGED
@@ -99,7 +99,18 @@ module Faktory
99
99
 
100
100
  def self.banner
101
101
  %q{
102
- INSERT SWEET FAKTORY BANNER HERE
102
+ ,,,,
103
+ ,,,, | |
104
+ | | | |
105
+ | | | |
106
+ | |,,~~' '~, ___ _
107
+ ,,~~'' ,~~, '~, / __) | | _
108
+ ,,~~'' ,~~, | | | _| |__ _____| | _ _| |_ ___ ____ _ _
109
+ | ,~~, | | | | | (_ __|____ | |_/ |_ _) _ \ / ___) | | |
110
+ | | | | | | | | | | / ___ | _ ( | || |_| | | | |_| |
111
+ | |__| |__| |__| | |_| \_____|_| \_) \__)___/|_| \__ |
112
+ |__________________________| (____/
113
+
103
114
  }
104
115
  end
105
116
 
@@ -35,9 +35,9 @@ module Faktory
35
35
  # MY_FAKTORY_URL=tcp://:somepass@my-server.example.com:7419
36
36
  #
37
37
  # Note above, the URL can contain the password for secure installations.
38
- def initialize(url: 'tcp://localhost:7419', debug: false)
38
+ def initialize(url: uri_from_env || 'tcp://localhost:7419', debug: false)
39
39
  @debug = debug
40
- @location = uri_from_env || URI(url)
40
+ @location = URI(url)
41
41
  open
42
42
  end
43
43
 
@@ -235,14 +235,15 @@ module Faktory
235
235
  # MY_FAKTORY_URL=tcp://:some-pass@some-hostname:7419
236
236
  def uri_from_env
237
237
  prov = ENV['FAKTORY_PROVIDER']
238
- return nil unless prov
239
- raise(ArgumentError, <<-EOM) if prov.index(":")
240
- Invalid FAKTORY_PROVIDER '#{prov}', it should be the name of the ENV variable that contains the URL
241
- FAKTORY_PROVIDER=MY_FAKTORY_URL
242
- MY_FAKTORY_URL=tcp://:some-pass@some-hostname:7419
243
- EOM
244
- val = ENV[prov]
245
- return URI(val) if val
238
+ if prov
239
+ raise(ArgumentError, <<-EOM) if prov.index(":")
240
+ Invalid FAKTORY_PROVIDER '#{prov}', it should be the name of the ENV variable that contains the URL
241
+ FAKTORY_PROVIDER=MY_FAKTORY_URL
242
+ MY_FAKTORY_URL=tcp://:some-pass@some-hostname:7419
243
+ EOM
244
+ val = ENV[prov]
245
+ return URI(val) if val
246
+ end
246
247
 
247
248
  val = ENV['FAKTORY_URL']
248
249
  return URI(val) if val
@@ -9,9 +9,10 @@ module Faktory
9
9
  attr_accessor :manager
10
10
 
11
11
  def initialize(options)
12
- @manager = Faktory::Manager.new(options)
12
+ merged_options = Faktory.options.merge(options)
13
+ @manager = Faktory::Manager.new(merged_options)
13
14
  @done = false
14
- @options = options
15
+ @options = merged_options
15
16
  end
16
17
 
17
18
  def run
@@ -9,7 +9,7 @@ module Faktory
9
9
  # To add middleware to run when a job is pushed to Faktory:
10
10
  #
11
11
  # Faktory.configure_client do |config|
12
- # config.push_middleware do |chain|
12
+ # config.client_middleware do |chain|
13
13
  # chain.add MyClientHook
14
14
  # end
15
15
  # end
@@ -27,15 +27,15 @@ module Faktory
27
27
  # To insert immediately preceding another entry:
28
28
  #
29
29
  # Faktory.configure_client do |config|
30
- # config.middleware do |chain|
31
- # chain.insert_before ActiveRecord, MyClientHook
30
+ # config.client_middleware do |chain|
31
+ # chain.insert_before SomeOtherMiddleware, MyClientHook
32
32
  # end
33
33
  # end
34
34
  #
35
35
  # To insert immediately after another entry:
36
36
  #
37
37
  # Faktory.configure_client do |config|
38
- # config.middleware do |chain|
38
+ # config.client_middleware do |chain|
39
39
  # chain.insert_after ActiveRecord, MyClientHook
40
40
  # end
41
41
  # end
@@ -39,9 +39,9 @@ module Faktory
39
39
  @down = false
40
40
  @done = false
41
41
  @thread = nil
42
- @reloader = Faktory.options[:reloader]
42
+ @reloader = mgr.options[:reloader]
43
43
  @logging = (mgr.options[:job_logger] || Faktory::JobLogger).new
44
- @fetcher = Faktory::Fetcher.new(Faktory.options)
44
+ @fetcher = Faktory::Fetcher.new(mgr.options)
45
45
  end
46
46
 
47
47
  def terminate(wait=false)
@@ -153,7 +153,7 @@ module Faktory
153
153
  # within the timeout. Don't acknowledge the work since
154
154
  # we didn't properly finish it.
155
155
  rescue Exception => ex
156
- handle_exception(ex, { :context => "Job raised exception", :job => job })
156
+ handle_exception(ex, { :context => "Job raised exception", :job => work.job })
157
157
  work.fail(ex)
158
158
  raise ex
159
159
  end
@@ -0,0 +1,336 @@
1
+ module Faktory
2
+ module Testing
3
+ def self.__test_mode
4
+ @__test_mode
5
+ end
6
+
7
+ def self.__test_mode=(mode)
8
+ @__test_mode = mode
9
+ end
10
+
11
+ def self.__set_test_mode(mode)
12
+ if block_given?
13
+ current_mode = self.__test_mode
14
+ begin
15
+ self.__test_mode = mode
16
+ yield
17
+ ensure
18
+ self.__test_mode = current_mode
19
+ end
20
+ else
21
+ self.__test_mode = mode
22
+ end
23
+ end
24
+
25
+ def self.disable!(&block)
26
+ __set_test_mode(:disable, &block)
27
+ end
28
+
29
+ def self.fake!(&block)
30
+ __set_test_mode(:fake, &block)
31
+ end
32
+
33
+ def self.inline!(&block)
34
+ # Only allow blockless inline via `blockless_inline_is_a_bad_idea_but_I_wanna_do_it_anyway!`
35
+ # https://github.com/mperham/sidekiq/issues/3495
36
+ unless block_given?
37
+ raise 'Must provide a block to Faktory::Testing.inline!'
38
+ end
39
+
40
+ __set_test_mode(:inline, &block)
41
+ end
42
+
43
+ def self.blockless_inline_is_a_bad_idea_but_I_wanna_do_it_anyway!
44
+ __set_test_mode(:inline)
45
+ end
46
+
47
+ def self.enabled?
48
+ self.__test_mode != :disable
49
+ end
50
+
51
+ def self.disabled?
52
+ self.__test_mode == :disable
53
+ end
54
+
55
+ def self.fake?
56
+ self.__test_mode == :fake
57
+ end
58
+
59
+ def self.inline?
60
+ self.__test_mode == :inline
61
+ end
62
+
63
+ def self.constantize(str)
64
+ names = str.split('::')
65
+ names.shift if names.empty? || names.first.empty?
66
+
67
+ names.inject(Object) do |constant, name|
68
+ # the false flag limits search for name to under the constant namespace
69
+ # which mimics Rails' behaviour
70
+ constant.const_defined?(name, false) ? constant.const_get(name, false) : constant.const_missing(name)
71
+ end
72
+ end
73
+ end
74
+
75
+ # Test modes have to be opted into explicitly.
76
+ # Just requiring the testing module shouldn't automatically change the testing mode.
77
+ Faktory::Testing.disable!
78
+
79
+ class EmptyQueueError < RuntimeError; end
80
+
81
+ class Client
82
+ alias_method :real_push, :push
83
+ alias_method :real_open, :open
84
+
85
+ def push(job)
86
+ if Faktory::Testing.inline?
87
+ job = Faktory.load_json(Faktory.dump_json(job))
88
+ job_class = Faktory::Testing.constantize(job['jobtype'])
89
+ job_class.new.perform(*job['args'])
90
+ return job['jid']
91
+ elsif Faktory::Testing.fake?
92
+ job = Faktory.load_json(Faktory.dump_json(job))
93
+ job.merge!('enqueued_at' => Time.now.to_f) unless job['at']
94
+ Queues.push(job['queue'], job['jobtype'], job)
95
+ return job['jid']
96
+ else
97
+ real_push(job)
98
+ end
99
+ end
100
+
101
+ def open
102
+ unless Faktory::Testing.enabled?
103
+ real_open
104
+ end
105
+ end
106
+ end
107
+
108
+ module Queues
109
+ ##
110
+ # The Queues class is only for testing the fake queue implementation.
111
+ # There are 2 data structures involved in tandem. This is due to the
112
+ # Rspec syntax of change(QueueWorker.jobs, :size). It keeps a reference
113
+ # to the array. Because the array was dervied from a filter of the total
114
+ # jobs enqueued, it appeared as though the array didn't change.
115
+ #
116
+ # To solve this, we'll keep 2 hashes containing the jobs. One with keys based
117
+ # on the queue, and another with keys of the worker names, so the array for
118
+ # QueueWorker.jobs is a straight reference to a real array.
119
+ #
120
+ # Queue-based hash:
121
+ #
122
+ # {
123
+ # "default"=>[
124
+ # {
125
+ # "class"=>"TestTesting::QueueWorker",
126
+ # "args"=>[1, 2],
127
+ # "retry"=>true,
128
+ # "queue"=>"default",
129
+ # "jid"=>"abc5b065c5c4b27fc1102833",
130
+ # "created_at"=>1447445554.419934
131
+ # }
132
+ # ]
133
+ # }
134
+ #
135
+ # Worker-based hash:
136
+ #
137
+ # {
138
+ # "TestTesting::QueueWorker"=>[
139
+ # {
140
+ # "class"=>"TestTesting::QueueWorker",
141
+ # "args"=>[1, 2],
142
+ # "retry"=>true,
143
+ # "queue"=>"default",
144
+ # "jid"=>"abc5b065c5c4b27fc1102833",
145
+ # "created_at"=>1447445554.419934
146
+ # }
147
+ # ]
148
+ # }
149
+ #
150
+ # Example:
151
+ #
152
+ # require 'faktory/testing'
153
+ #
154
+ # assert_equal 0, Faktory::Queues["default"].size
155
+ # HardWorker.perform_async(:something)
156
+ # assert_equal 1, Faktory::Queues["default"].size
157
+ # assert_equal :something, Faktory::Queues["default"].first['args'][0]
158
+ #
159
+ # You can also clear all workers' jobs:
160
+ #
161
+ # assert_equal 0, Faktory::Queues["default"].size
162
+ # HardWorker.perform_async(:something)
163
+ # Faktory::Queues.clear_all
164
+ # assert_equal 0, Faktory::Queues["default"].size
165
+ #
166
+ # This can be useful to make sure jobs don't linger between tests:
167
+ #
168
+ # RSpec.configure do |config|
169
+ # config.before(:each) do
170
+ # Faktory::Queues.clear_all
171
+ # end
172
+ # end
173
+ #
174
+ class << self
175
+ def [](queue)
176
+ jobs_by_queue[queue]
177
+ end
178
+
179
+ def push(queue, klass, job)
180
+ jobs_by_queue[queue] << job
181
+ jobs_by_worker[klass] << job
182
+ end
183
+
184
+ def jobs_by_queue
185
+ @jobs_by_queue ||= Hash.new { |hash, key| hash[key] = [] }
186
+ end
187
+
188
+ def jobs_by_worker
189
+ @jobs_by_worker ||= Hash.new { |hash, key| hash[key] = [] }
190
+ end
191
+
192
+ def delete_for(jid, queue, klass)
193
+ jobs_by_queue[queue.to_s].delete_if { |job| job["jid"] == jid }
194
+ jobs_by_worker[klass].delete_if { |job| job["jid"] == jid }
195
+ end
196
+
197
+ def clear_for(queue, klass)
198
+ jobs_by_queue[queue].clear
199
+ jobs_by_worker[klass].clear
200
+ end
201
+
202
+ def clear_all
203
+ jobs_by_queue.clear
204
+ jobs_by_worker.clear
205
+ end
206
+ end
207
+ end
208
+
209
+ module Job
210
+ ##
211
+ # The Faktory testing infrastructure overrides perform_async
212
+ # so that it does not actually touch the network. Instead it
213
+ # stores the asynchronous jobs in a per-class array so that
214
+ # their presence/absence can be asserted by your tests.
215
+ #
216
+ # This is similar to ActionMailer's :test delivery_method and its
217
+ # ActionMailer::Base.deliveries array.
218
+ #
219
+ # Example:
220
+ #
221
+ # require 'faktory/testing'
222
+ #
223
+ # assert_equal 0, HardWorker.jobs.size
224
+ # HardWorker.perform_async(:something)
225
+ # assert_equal 1, HardWorker.jobs.size
226
+ # assert_equal :something, HardWorker.jobs[0]['args'][0]
227
+ #
228
+ # assert_equal 0, Faktory::Extensions::DelayedMailer.jobs.size
229
+ # MyMailer.delay.send_welcome_email('foo@example.com')
230
+ # assert_equal 1, Faktory::Extensions::DelayedMailer.jobs.size
231
+ #
232
+ # You can also clear and drain all workers' jobs:
233
+ #
234
+ # assert_equal 0, Faktory::Extensions::DelayedMailer.jobs.size
235
+ # assert_equal 0, Faktory::Extensions::DelayedModel.jobs.size
236
+ #
237
+ # MyMailer.delay.send_welcome_email('foo@example.com')
238
+ # MyModel.delay.do_something_hard
239
+ #
240
+ # assert_equal 1, Faktory::Extensions::DelayedMailer.jobs.size
241
+ # assert_equal 1, Faktory::Extensions::DelayedModel.jobs.size
242
+ #
243
+ # Faktory::Worker.clear_all # or .drain_all
244
+ #
245
+ # assert_equal 0, Faktory::Extensions::DelayedMailer.jobs.size
246
+ # assert_equal 0, Faktory::Extensions::DelayedModel.jobs.size
247
+ #
248
+ # This can be useful to make sure jobs don't linger between tests:
249
+ #
250
+ # RSpec.configure do |config|
251
+ # config.before(:each) do
252
+ # Faktory::Worker.clear_all
253
+ # end
254
+ # end
255
+ #
256
+ # or for acceptance testing, i.e. with cucumber:
257
+ #
258
+ # AfterStep do
259
+ # Faktory::Worker.drain_all
260
+ # end
261
+ #
262
+ # When I sign up as "foo@example.com"
263
+ # Then I should receive a welcome email to "foo@example.com"
264
+ #
265
+ module ClassMethods
266
+
267
+ # Queue for this worker
268
+ def queue
269
+ self.faktory_options["queue"]
270
+ end
271
+
272
+ # Jobs queued for this worker
273
+ def jobs
274
+ Queues.jobs_by_worker[self.to_s]
275
+ end
276
+
277
+ # Clear all jobs for this worker
278
+ def clear
279
+ Queues.clear_for(queue, self.to_s)
280
+ end
281
+
282
+ # Drain and run all jobs for this worker
283
+ def drain
284
+ while jobs.any?
285
+ next_job = jobs.first
286
+ Queues.delete_for(next_job["jid"], next_job["queue"], self.to_s)
287
+ process_job(next_job)
288
+ end
289
+ end
290
+
291
+ # Pop out a single job and perform it
292
+ def perform_one
293
+ raise(EmptyQueueError, "perform_one called with empty job queue") if jobs.empty?
294
+ next_job = jobs.first
295
+ Queues.delete_for(next_job["jid"], queue, self.to_s)
296
+ process_job(next_job)
297
+ end
298
+
299
+ def process_job(job)
300
+ worker = new
301
+ worker.jid = job['jid']
302
+ worker.bid = job['bid'] if worker.respond_to?(:bid=)
303
+ #Faktory::Testing.server_middleware.invoke(worker, job, job['queue']) do
304
+ execute_job(worker, job['args'])
305
+ #end
306
+ end
307
+
308
+ def execute_job(worker, args)
309
+ worker.perform(*args)
310
+ end
311
+ end
312
+
313
+ class << self
314
+ def jobs # :nodoc:
315
+ Queues.jobs_by_queue.values.flatten
316
+ end
317
+
318
+ # Clear all queued jobs across all workers
319
+ def clear_all
320
+ Queues.clear_all
321
+ end
322
+
323
+ # Drain all queued jobs across all workers
324
+ def drain_all
325
+ while jobs.any?
326
+ worker_classes = jobs.map { |job| job["jobtype"] }.uniq
327
+
328
+ worker_classes.each do |worker_class|
329
+ Faktory::Testing.constantize(worker_class).drain
330
+ end
331
+ end
332
+ end
333
+ end
334
+ end
335
+
336
+ end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Faktory
3
- VERSION = "0.6.1"
3
+ VERSION = "0.7.0"
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: faktory_worker_ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Perham
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-11-13 00:00:00.000000000 Z
11
+ date: 2018-03-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: connection_pool
@@ -104,10 +104,11 @@ files:
104
104
  - lib/faktory/middleware/i18n.rb
105
105
  - lib/faktory/processor.rb
106
106
  - lib/faktory/rails.rb
107
+ - lib/faktory/testing.rb
107
108
  - lib/faktory/util.rb
108
109
  - lib/faktory/version.rb
109
110
  - lib/faktory_worker_ruby.rb
110
- homepage: http://github.com/contribsys/faktory_worker_ruby
111
+ homepage: https://github.com/contribsys/faktory_worker_ruby
111
112
  licenses:
112
113
  - LGPL-3.0
113
114
  metadata: {}
@@ -127,7 +128,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
127
128
  version: '0'
128
129
  requirements: []
129
130
  rubyforge_project:
130
- rubygems_version: 2.6.8
131
+ rubygems_version: 2.6.13
131
132
  signing_key:
132
133
  specification_version: 4
133
134
  summary: Ruby worker for Faktory