faktory_worker_ruby 0.6.1 → 0.7.0

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 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