acidic_job 1.0.0.pre9 → 1.0.0.pre10

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: 3971aa5af368952620c483730c9a8a28efd9e01bffecc38d015d322e165bdffe
4
- data.tar.gz: a6c7374d7e0f2121a0fc92b988c28cee29d488f32506dd84e9e84c15b0117eda
3
+ metadata.gz: 95b8fbdebe38a639f9b905df6ac1f5b853b09383d0810696c89c6c9e001efffe
4
+ data.tar.gz: b7e92f9b98461e2f39122405b6e3278fb7ed8a0edec65963026f7e2665bb5042
5
5
  SHA512:
6
- metadata.gz: 7a890818e15da5fb4f5990f9753545d2e33c2bfb6d6e2a9793a42efeed34fcb6dbef076b18111980b0bfe32fe58f490f5fad0d70640b8d6624c8ae0210b4dd8a
7
- data.tar.gz: b988408714e535184d553982a8220e32c83e7551ea129a7a5c001f1e83462d03d64f86e7df4112fed3af51f1a8a197f38c587fd01a9dc0b4d77ea8051a43aee0
6
+ metadata.gz: 13b5ea59163e833f5b698625d841fdcdc0a630b46880526df3274926e696b227b41946fd6622e121b9afd90ad3cc94589f6e864188e871d70f03c77168e3ad5e
7
+ data.tar.gz: 27e5b74147706f227875f4f4a61f084298c42e615149cb0a74670af841df8fcce9eb4f95d11935561912af61d6d8759c21c9ca9862e897f765b49c78b1e42ebd
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- acidic_job (1.0.0.pre9)
4
+ acidic_job (1.0.0.pre10)
5
5
  activerecord (>= 6.1.0)
6
6
  activesupport
7
7
 
@@ -7,18 +7,27 @@ module AcidicJob
7
7
  module ActionMailer
8
8
  extend ActiveSupport::Concern
9
9
 
10
- def deliver_acidicly(_options = {})
11
- job = ::ActionMailer::MailDeliveryJob
10
+ def deliver_acidicly(_options = {}, idempotency_key: nil, unique_by: nil)
11
+ job_class = ::ActionMailer::MailDeliveryJob
12
12
 
13
13
  job_args = [@mailer_class.name, @action.to_s, "deliver_now", @params, *@args]
14
14
  # for Sidekiq, this depends on the Sidekiq::Serialization extension
15
- serialized_job = job.new(job_args).serialize
15
+ serialized_job = job_class.new(job_args).serialize
16
+ acidic_identifier = job_class.respond_to?(:acidic_identifier) ? job_class.acidic_identifier : :job_id
17
+ # use either [1] provided key, [2] provided uniqueness constraint, or [3] computed key
18
+ key = if idempotency_key
19
+ idempotency_key
20
+ elsif unique_by
21
+ IdempotencyKey.generate(unique_by: unique_by, job_class: job_class.name)
22
+ else
23
+ IdempotencyKey.new(acidic_identifier).value_for(serialized_job)
24
+ end
16
25
 
17
26
  AcidicJob::Run.create!(
18
27
  staged: true,
19
- job_class: job.name,
28
+ job_class: job_class.name,
20
29
  serialized_job: serialized_job,
21
- idempotency_key: IdempotencyKey.value_for(serialized_job)
30
+ idempotency_key: key
22
31
  )
23
32
  end
24
33
  alias deliver_transactionally deliver_acidicly
@@ -24,12 +24,21 @@ module AcidicJob
24
24
  raise UnsupportedExtension unless defined?(::ActiveJob) && self < ::ActiveJob::Base
25
25
 
26
26
  serialized_job = serialize_with_arguments(*args, **kwargs)
27
+ # use either [1] provided key, [2] provided uniqueness constraint, or [3] computed key
28
+ key = if kwargs.key?(:idempotency_key) || kwargs.key?("idempotency_key")
29
+ kwargs[:idempotency_key] || kwargs["idempotency_key"]
30
+ elsif kwargs.key?(:unique_by) || kwargs.key?("unique_by")
31
+ unique_by = [kwargs[:unique_by], kwargs["unique_by"]].compact.first
32
+ IdempotencyKey.generate(unique_by: unique_by, job_class: name)
33
+ else
34
+ IdempotencyKey.new(acidic_identifier).value_for(serialized_job)
35
+ end
27
36
 
28
37
  AcidicJob::Run.create!(
29
38
  staged: true,
30
39
  job_class: name,
31
40
  serialized_job: serialized_job,
32
- idempotency_key: IdempotencyKey.value_for(serialized_job)
41
+ idempotency_key: key
33
42
  )
34
43
  end
35
44
  alias_method :perform_transactionally, :perform_acidicly
@@ -6,12 +6,12 @@ module AcidicJob
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  class_methods do
9
- def deliver_acidicly(recipients)
10
- new.deliver_acidicly(recipients)
9
+ def deliver_acidicly(recipients, idempotency_key: nil, unique_by: nil)
10
+ new.deliver_acidicly(recipients, idempotency_key: idempotency_key, unique_by: unique_by)
11
11
  end
12
12
  end
13
13
 
14
- def deliver_acidicly(recipients)
14
+ def deliver_acidicly(recipients, idempotency_key: nil, unique_by: nil)
15
15
  # THIS IS A HACK THAT COPIES AND PASTES KEY PARTS OF THE `Noticed::Base` CODE
16
16
  # IN ORDER TO ALLOW US TO TRANSACTIONALLY DELIVER NOTIFICATIONS
17
17
  # THIS IS THUS LIABLE TO BREAK WHENEVER THAT GEM IS UPDATED
@@ -36,12 +36,21 @@ module AcidicJob
36
36
  record: record
37
37
  }
38
38
  serialized_job = job_class.send(:job_or_instantiate, args).serialize
39
+ acidic_identifier = job_class.respond_to?(:acidic_identifier) ? job_class.acidic_identifier : :job_id
40
+ # use either [1] provided key, [2] provided uniqueness constraint, or [3] computed key
41
+ key = if idempotency_key
42
+ idempotency_key
43
+ elsif unique_by
44
+ IdempotencyKey.generate(unique_by: unique_by, job_class: job_class.name)
45
+ else
46
+ IdempotencyKey.new(acidic_identifier).value_for(serialized_job)
47
+ end
39
48
 
40
49
  AcidicJob::Run.create!(
41
50
  staged: true,
42
51
  job_class: job_class.name,
43
52
  serialized_job: serialized_job,
44
- idempotency_key: IdempotencyKey.value_for(serialized_job)
53
+ idempotency_key: key
45
54
  )
46
55
  end
47
56
  end
@@ -58,12 +58,21 @@ module AcidicJob
58
58
  class_methods do
59
59
  def perform_acidicly(*args, **kwargs)
60
60
  serialized_job = serialize_with_arguments(*args, **kwargs)
61
+ # use either [1] provided key, [2] provided uniqueness constraint, or [3] computed key
62
+ key = if kwargs.key?(:idempotency_key) || kwargs.key?("idempotency_key")
63
+ kwargs[:idempotency_key] || kwargs["idempotency_key"]
64
+ elsif kwargs.key?(:unique_by) || kwargs.key?("unique_by")
65
+ unique_by = [kwargs[:unique_by], kwargs["unique_by"]].compact.first
66
+ IdempotencyKey.generate(unique_by: unique_by, job_class: name)
67
+ else
68
+ IdempotencyKey.new(acidic_identifier).value_for(serialized_job)
69
+ end
61
70
 
62
71
  AcidicJob::Run.create!(
63
72
  staged: true,
64
73
  job_class: name,
65
74
  serialized_job: serialized_job,
66
- idempotency_key: IdempotencyKey.value_for(serialized_job)
75
+ idempotency_key: key
67
76
  )
68
77
  end
69
78
  alias_method :perform_transactionally, :perform_acidicly
@@ -2,15 +2,57 @@
2
2
 
3
3
  module AcidicJob
4
4
  class IdempotencyKey
5
- def self.value_for(hash_or_job, *args, **kwargs)
6
- return hash_or_job.job_id if hash_or_job.respond_to?(:job_id) && !hash_or_job.job_id.nil?
7
- return hash_or_job.jid if hash_or_job.respond_to?(:jid) && !hash_or_job.jid.nil?
5
+ def self.generate(unique_by:, job_class:)
6
+ new(:job_args).value_for({ "job_class" => job_class }, Marshal.dump(unique_by))
7
+ end
8
+
9
+ def initialize(identifier = :job_id)
10
+ @identifier = identifier
11
+ end
12
+
13
+ def value_for(hash_or_job, *args, **kwargs)
14
+ return value_from_job_args(hash_or_job, *args, **kwargs) if @identifier == :job_args
15
+
16
+ value = if hash_or_job.is_a?(Hash)
17
+ value_from_job_id_for_hash(hash_or_job)
18
+ else
19
+ value_from_job_id_for_obj(hash_or_job)
20
+ end
21
+
22
+ value || value_from_job_args(hash_or_job, *args, **kwargs)
23
+ end
24
+
25
+ private
8
26
 
9
- if hash_or_job.is_a?(Hash) && hash_or_job.key?("job_id") && !hash_or_job["job_id"].nil?
10
- return hash_or_job["job_id"]
27
+ def value_from_job_id_for_hash(hash)
28
+ if hash.key?("job_id")
29
+ return if hash["job_id"].nil?
30
+ return if hash["job_id"].empty?
31
+
32
+ hash["job_id"]
33
+ elsif hash.key?("jid")
34
+ return if hash["jid"].nil?
35
+ return if hash["jid"].empty?
36
+
37
+ hash["jid"]
38
+ end
39
+ end
40
+
41
+ def value_from_job_id_for_obj(obj)
42
+ if obj.respond_to?(:job_id)
43
+ return if obj.job_id.nil?
44
+ return if obj.job_id.empty?
45
+
46
+ obj.job_id
47
+ elsif obj.respond_to?(:jid)
48
+ return if obj.jid.nil?
49
+ return if obj.jid.empty?
50
+
51
+ obj.jid
11
52
  end
12
- return hash_or_job["jid"] if hash_or_job.is_a?(Hash) && hash_or_job.key?("jid") && !hash_or_job["jid"].nil?
53
+ end
13
54
 
55
+ def value_from_job_args(hash_or_job, *args, **kwargs)
14
56
  worker_class = case hash_or_job
15
57
  when Hash
16
58
  hash_or_job["worker"] || hash_or_job["job_class"]
@@ -26,5 +26,15 @@ module AcidicJob
26
26
 
27
27
  GlobalID::Locator.locate(staged_job_gid)
28
28
  end
29
+
30
+ def identifier
31
+ return jid if defined?(jid) && !jid.nil?
32
+ return job_id if defined?(job_id) && !job_id.nil?
33
+
34
+ # might be defined already in `with_acidity` method
35
+ @__acidic_job_idempotency_key ||= IdempotencyKey.value_for(self, @__acidic_job_args, @__acidic_job_kwargs)
36
+
37
+ @__acidic_job_idempotency_key
38
+ end
29
39
  end
30
40
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AcidicJob
4
- VERSION = "1.0.0.pre9"
4
+ VERSION = "1.0.0.pre10"
5
5
  end
data/lib/acidic_job.rb CHANGED
@@ -46,6 +46,10 @@ module AcidicJob
46
46
  end
47
47
 
48
48
  klass.set_callback :perform, :after, :delete_staged_job_record, if: :was_staged_job?
49
+
50
+ klass.instance_variable_set(:@acidic_identifier, :job_id)
51
+ klass.define_singleton_method(:acidic_by_job_id) { @acidic_identifier = :job_id }
52
+ klass.define_singleton_method(:acidic_by_job_args) { @acidic_identifier = :job_args }
49
53
  end
50
54
 
51
55
  included do
@@ -57,33 +61,34 @@ module AcidicJob
57
61
  AcidicJob.wire_everything_up(subclass)
58
62
  super
59
63
  end
64
+
65
+ def acidic_identifier
66
+ @acidic_identifier
67
+ end
60
68
  end
61
69
 
62
70
  def with_acidity(providing: {})
63
- # execute the block to gather the info on what steps are defined for this job workflow
71
+ # ensure this instance variable is always defined
64
72
  @__acidic_job_steps = []
65
- steps = yield || []
73
+ # execute the block to gather the info on what steps are defined for this job workflow
74
+ yield
66
75
 
67
76
  # check that the block actually defined at least one step
68
77
  # TODO: WRITE TESTS FOR FAULTY BLOCK VALUES
69
78
  raise NoDefinedSteps if @__acidic_job_steps.nil? || @__acidic_job_steps.empty?
70
79
 
71
80
  # convert the array of steps into a hash of recovery_points and next steps
72
- workflow = define_workflow(steps)
73
-
74
- # determine the idempotency key value for this job run (`job_id` or `jid`)
75
- # might be defined already in `identifier` method
76
- # TODO: allow idempotency to be defined by args OR job id
77
- @__acidic_job_idempotency_key ||= IdempotencyKey.value_for(self, @__acidic_job_args, @__acidic_job_kwargs)
81
+ workflow = define_workflow(@__acidic_job_steps)
78
82
 
79
- @run = ensure_run_record(@__acidic_job_idempotency_key, workflow, providing)
83
+ @run = ensure_run_record(workflow, providing)
80
84
 
81
85
  # begin the workflow
82
86
  process_run(@run)
83
87
  end
84
88
 
85
89
  # DEPRECATED
86
- def idempotently(with:, &blk)
90
+ def idempotently(with: {}, &blk)
91
+ ActiveSupport::Deprecation.new("1.0", "AcidicJob").deprecation_warning(:idempotently)
87
92
  with_acidity(providing: with, &blk)
88
93
  end
89
94
 
@@ -93,11 +98,16 @@ module AcidicJob
93
98
  FinishedPoint.new
94
99
  end
95
100
 
101
+ # TODO: allow idempotency to be defined by args OR job id
96
102
  # rubocop:disable Naming/MemoizedInstanceVariableName
97
103
  def idempotency_key
98
- return @__acidic_job_idempotency_key if defined? @__acidic_job_idempotency_key
104
+ if defined?(@__acidic_job_idempotency_key) && !@__acidic_job_idempotency_key.nil?
105
+ return @__acidic_job_idempotency_key
106
+ end
99
107
 
100
- @__acidic_job_idempotency_key ||= IdempotencyKey.value_for(self, @__acidic_job_args, @__acidic_job_kwargs)
108
+ acidic_identifier = self.class.acidic_identifier
109
+ @__acidic_job_idempotency_key ||= IdempotencyKey.new(acidic_identifier)
110
+ .value_for(self, @__acidic_job_args, @__acidic_job_kwargs)
101
111
  end
102
112
  # rubocop:enable Naming/MemoizedInstanceVariableName
103
113
 
@@ -171,7 +181,7 @@ module AcidicJob
171
181
  # { "step 1": { does: "step 1", awaits: [], then: "step 2" }, ... }
172
182
  end
173
183
 
174
- def ensure_run_record(key_val, workflow, accessors)
184
+ def ensure_run_record(workflow, accessors)
175
185
  isolation_level = case ActiveRecord::Base.connection.adapter_name.downcase.to_sym
176
186
  when :sqlite
177
187
  :read_uncommitted
@@ -180,7 +190,7 @@ module AcidicJob
180
190
  end
181
191
 
182
192
  ActiveRecord::Base.transaction(isolation: isolation_level) do
183
- run = Run.find_by(idempotency_key: key_val)
193
+ run = Run.find_by(idempotency_key: idempotency_key)
184
194
  serialized_job = serialize_job(@__acidic_job_args, @__acidic_job_kwargs)
185
195
 
186
196
  if run.present?
@@ -201,7 +211,7 @@ module AcidicJob
201
211
  else
202
212
  run = Run.create!(
203
213
  staged: false,
204
- idempotency_key: key_val,
214
+ idempotency_key: idempotency_key,
205
215
  job_class: self.class.name,
206
216
  locked_at: Time.current,
207
217
  last_run_at: Time.current,
@@ -246,14 +256,4 @@ module AcidicJob
246
256
 
247
257
  true
248
258
  end
249
-
250
- def identifier
251
- return jid if defined?(jid) && !jid.nil?
252
- return job_id if defined?(job_id) && !job_id.nil?
253
-
254
- # might be defined already in `with_acidity` method
255
- @__acidic_job_idempotency_key ||= IdempotencyKey.value_for(self, @__acidic_job_args, @__acidic_job_kwargs)
256
-
257
- @__acidic_job_idempotency_key
258
- end
259
259
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: acidic_job
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.pre9
4
+ version: 1.0.0.pre10
5
5
  platform: ruby
6
6
  authors:
7
7
  - fractaledmind
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-03-12 00:00:00.000000000 Z
11
+ date: 2022-03-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord