job_contracts 0.1.1 → 0.1.4

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
  SHA256:
3
- metadata.gz: 3409ea614e4698241974af1291cac6b6e2fc0cf75c45af5d7d5ea61cde545998
4
- data.tar.gz: 7a59f4f8408f9d0c7963578b411bc5425da56e7138cb35e7cd0ee938f342dc58
3
+ metadata.gz: 5bcafda35a29dbc6cb71004f638807b9634658e3eb50cd13f9ebd4274709f03c
4
+ data.tar.gz: 6a6a0e43df2db48f77525ac3ceae7bb26b36cd300becee484b65b14caec3c703
5
5
  SHA512:
6
- metadata.gz: 6e3329c2fb4ff9a4a898c44453504259b211389f8f2d57a6a6ba1a0722da414ad843030e8c1991e79ba28dfe71abedad9ef016f130ba469a5dc37c23c441572a
7
- data.tar.gz: 40be8734e8791531b7b03fde19f89697a10744ec0f77e688a091090df9a998bffd8aa3d70c95bd1e31aba024f10338a4aa7fdea712cef9f8eeaefb4beb2fbc45
6
+ metadata.gz: 799d3d7aa098fe1775a7ec0e36d210ae25713ccb96c2ab5bd8962298097b7754fb5805f4c99267111aabe19f20caf47e64671806410ffd2af5c11a757121e9a1
7
+ data.tar.gz: ecef09781a6b2833d30c6e8c1ee2dcf3d32c3a500df0f1b1cab60e4cf8490eeda1b294a77263ffdc9039adec10740b68396e7d292542e75e245a405f3ec59ea0
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
- [![Lines of Code](http://img.shields.io/badge/lines_of_code-240-brightgreen.svg?style=flat)](http://blog.codinghorror.com/the-best-code-is-no-code-at-all/)
1
+ [![Lines of Code](http://img.shields.io/badge/lines_of_code-232-brightgreen.svg?style=flat)](http://blog.codinghorror.com/the-best-code-is-no-code-at-all/)
2
2
  [![Code Quality](https://app.codacy.com/project/badge/Grade/f604d4bc6db0474c802ef51182732488)](https://www.codacy.com/gh/hopsoft/job_contracts/dashboard?utm_source=github.com&utm_medium=referral&utm_content=hopsoft/job_contracts&utm_campaign=Badge_Grade)
3
- ![tests](https://github.com/hopsoft/job_contracts/actions/workflows/test.yml/badge.svg)
3
+ ![Tests](https://github.com/hopsoft/job_contracts/actions/workflows/test.yml/badge.svg)
4
+ [![Gem Version](https://badge.fury.io/rb/job_contracts.svg)](https://badge.fury.io/rb/job_contracts)
5
+ [![Follow Hopsoft](https://img.shields.io/twitter/follow/hopsoft?style=social)](https://twitter.com/hopsoft)
4
6
 
5
7
  # Job Contracts
6
8
 
@@ -47,7 +49,6 @@ class ImportantJob < ApplicationJob
47
49
  include JobContracts::Contractable
48
50
 
49
51
  queue_as :default
50
-
51
52
  add_contract JobContracts::DurationContract.new(max: 5.seconds)
52
53
 
53
54
  def perform
@@ -97,7 +98,7 @@ Contracts support the following constructor arguments.
97
98
 
98
99
  ### Defining a Contract
99
100
 
100
- Here's a contrived but simple example that ensures the first argument to perform fits within a specific range of values.
101
+ Here's a contrived, but simple, example that ensures the first argument passed to perform fits within a specific range of values.
101
102
 
102
103
  ```ruby
103
104
  # app/contracts/argument_contract.rb
@@ -203,7 +204,6 @@ class ImportantJob < ApplicationJob
203
204
  include JobContracts::Contractable
204
205
 
205
206
  queue_as :default
206
-
207
207
  on_contract_breach :take_action
208
208
  add_contract JobContracts::DurationContract.new(max: 5.seconds)
209
209
 
@@ -222,10 +222,7 @@ class ImportantJob < ApplicationJob
222
222
  include JobContracts::Contractable
223
223
 
224
224
  queue_as :default
225
-
226
- on_contract_breach -> (contract) {
227
- # take action...
228
- }
225
+ on_contract_breach -> (contract) { # take action... }
229
226
 
230
227
  add_contract JobContracts::DurationContract.new(max: 5.seconds)
231
228
 
@@ -237,11 +234,26 @@ end
237
234
 
238
235
  ## Sidekiq
239
236
 
240
- Sidekiq jobs/workers are supported.
241
- Unfortunately this support comes with a performance penalty *(i.e. additional latency)* because executing
242
- Sidekiq jobs don't have access to their own metadata. To get around this, we wait to find job metadata in the active
243
- [`WorkSet`](https://github.com/hopsoft/job_contracts/blob/main/lib/job_contracts/concerns/sidekiq_contractable.rb#L23-L25)
244
- which is only updated [every 5 seconds](https://github.com/mperham/sidekiq/wiki/API#workers).
237
+ `Sidekiq::Job`s are also supported.
238
+
239
+ ```ruby
240
+ class ImportantJob
241
+ include Sidekiq::Job
242
+ include JobContracts::SidekiqContractable
243
+
244
+ sidekiq_options queue: :default
245
+ add_contract JobContracts::DurationContract.new(max: 1.second)
246
+
247
+ def perform
248
+ # logic...
249
+ end
250
+
251
+ # default callback that's invoked if the contract is breached
252
+ def contract_breached!(contract)
253
+ # handle breach...
254
+ end
255
+ end
256
+ ```
245
257
 
246
258
  ## Todo
247
259
 
@@ -75,11 +75,5 @@ module JobContracts
75
75
  def contract_breached!
76
76
  # noop / override in job subclasses
77
77
  end
78
-
79
- def on_contract_breach(contract)
80
- breached_contracts << contract
81
- method = self.class.on_contract_breach_callback
82
- method.is_a?(Proc) ? method.call(contract) : send(method.to_s, contract)
83
- end
84
78
  end
85
79
  end
@@ -8,54 +8,32 @@ module JobContracts
8
8
  extend ActiveSupport::Concern
9
9
  include Contractable
10
10
 
11
- class SidekiqJobMetadataNotFoundError < StandardError; end
12
-
13
11
  module ClassMethods
12
+ # Matches the ActiveJob API
14
13
  def queue_name
15
14
  sidekiq_options_hash["queue"]
16
15
  end
17
16
  end
18
17
 
19
- def sidekiq_job_metadata
20
- @sidekiq_job_metadata ||= begin
21
- hit = nil
22
- begin
23
- attempts ||= 1
24
- hit = Sidekiq::Workers.new.find do |_process_id, _thread_id, work|
25
- work.dig("payload", "jid") == jid
26
- end
27
- raise SidekiqJobMetadataNotFoundError if hit.blank?
28
- rescue SidekiqJobMetadataNotFoundError
29
- # The WorkSet only updates every 5 seconds
30
- # SEE: https://github.com/mperham/sidekiq/wiki/API#workers
31
- # Re-attempt up to 10 times with a simple backoff strategy (up to 5.5 seconds)
32
- # TODO: Is there a faster and more reliable way to fetch the job's metadata after perform has begun?
33
- # May need to query Redis directly if the data is still in there at this point
34
- attempts += 1
35
- if attempts <= 10
36
- sleep 0.1 * attempts
37
- retry
38
- end
39
- end
40
-
41
- hit&.last || {}
42
- end
18
+ # Metadata used to enqueue the job
19
+ def sidekiq_job_hash
20
+ @sidekiq_job_hash ||= {}
43
21
  end
44
22
 
45
23
  # Matches the ActiveJob API
46
24
  def queue_name
47
- sidekiq_job_metadata["queue"]
25
+ sidekiq_job_hash["queue"]
48
26
  end
49
27
 
50
28
  # Matches the ActiveJob API
51
29
  def enqueued_at
52
- seconds = sidekiq_job_metadata.dig("payload", "enqueued_at")
30
+ seconds = sidekiq_job_hash["enqueued_at"]
53
31
  (seconds ? Time.at(seconds) : nil)&.iso8601.to_s
54
32
  end
55
33
 
56
34
  # Matches the ActiveJob API
57
35
  def arguments
58
- sidekiq_job_metadata.dig("payload", "args") || []
36
+ sidekiq_job_hash["args"] || []
59
37
  end
60
38
  end
61
39
  end
@@ -1,11 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "observer"
4
-
5
3
  module JobContracts
6
4
  class Contract
7
- include Observable
8
-
9
5
  attr_reader :trigger, :queues
10
6
 
11
7
  def initialize(trigger: :after, halt: false, queues: [], expected: {})
@@ -32,11 +28,9 @@ module JobContracts
32
28
  # NOTE: subclasses should update `actual`, set `satisfied`, and call `super`
33
29
  def enforce!(contractable)
34
30
  return unless should_enforce?(contractable)
35
- add_observer contractable, :on_contract_breach
36
- changed if breached?
37
- notify_observers self
38
- ensure
39
- delete_observer contractable
31
+ return if satisfied?
32
+ contractable.breached_contracts << self
33
+ invoke_contract_breach_callback contractable
40
34
  end
41
35
 
42
36
  def satisfied?
@@ -73,5 +67,11 @@ module JobContracts
73
67
  protected
74
68
 
75
69
  attr_accessor :satisfied
70
+
71
+ def invoke_contract_breach_callback(contractable)
72
+ callback = contractable.class.on_contract_breach_callback
73
+ callback = contractable.method(callback.to_sym) unless callback.is_a?(Proc)
74
+ callback.call self
75
+ end
76
76
  end
77
77
  end
@@ -1,6 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "sidekiq"
4
+ require_relative "sidekiq_job_hash_middleware"
5
+
3
6
  module JobContracts
4
7
  class Railtie < ::Rails::Railtie
8
+ initializer "job_contracts.register_sidekiq_middleware" do
9
+ Sidekiq.server_middleware.add SidekiqJobHashMiddleware
10
+ end
5
11
  end
6
12
  end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JobContracts
4
+ class SidekiqJobHashMiddleware
5
+ def call(worker, job, _queue)
6
+ worker.instance_variable_set :@sidekiq_job_hash, job
7
+ yield
8
+ end
9
+ end
10
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JobContracts
4
- VERSION = "0.1.1"
4
+ VERSION = "0.1.4"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: job_contracts
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathan Hopkins
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-05-03 00:00:00.000000000 Z
11
+ date: 2022-05-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -16,42 +16,42 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 7.0.2.3
19
+ version: 6.1.5
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 7.0.2.3
26
+ version: 6.1.5
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: sidekiq
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 6.4.1
33
+ version: 6.4.2
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: 6.4.1
40
+ version: 6.4.2
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: standard
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: 1.10.0
47
+ version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: 1.10.0
54
+ version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: magic_frozen_string_literal
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -126,6 +126,7 @@ files:
126
126
  - lib/job_contracts/contracts/queue_name_contract.rb
127
127
  - lib/job_contracts/contracts/read_only_contract.rb
128
128
  - lib/job_contracts/railtie.rb
129
+ - lib/job_contracts/sidekiq_job_hash_middleware.rb
129
130
  - lib/job_contracts/version.rb
130
131
  homepage: https://github.com/hopsoft/job_contracts
131
132
  licenses: