acidic_job 1.0.0.rc2 → 1.0.0.rc4

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.
data/.rubocop.yml CHANGED
@@ -1,83 +1,11 @@
1
- require:
2
- - rubocop-minitest
3
- - rubocop-rake
4
-
5
1
  AllCops:
6
- Exclude:
7
- - inline.rb
8
- - test/simulation.rb
9
- - vendor/bundle/**/*
10
2
  TargetRubyVersion: 2.7
11
3
  NewCops: enable
4
+ DisabledByDefault: true
5
+ SuggestExtensions: false
12
6
 
13
- Style/StringLiterals:
14
- Enabled: true
15
- EnforcedStyle: double_quotes
16
-
17
- Style/StringLiteralsInInterpolation:
18
- Enabled: true
19
- EnforcedStyle: double_quotes
20
-
21
- Layout/LineLength:
22
- Max: 120
23
-
24
- Gemspec/DevelopmentDependencies:
25
- Enabled: false
26
-
27
- Style/Documentation:
28
- Enabled: false
29
-
30
- Metrics/ModuleLength:
31
- Enabled: false
32
-
33
- Metrics/AbcSize:
34
- Enabled: false
35
-
36
- Metrics/MethodLength:
37
- Enabled: false
38
-
39
- Metrics/BlockLength:
40
- Enabled: false
41
-
42
- Metrics/CyclomaticComplexity:
43
- Enabled: false
44
-
45
- Metrics/PerceivedComplexity:
46
- Enabled: false
47
-
48
- Metrics/ClassLength:
49
- Enabled: false
50
-
51
- Minitest/MultipleAssertions:
52
- Enabled: false
53
-
54
- Style/ModuleFunction:
55
- Enabled: false
56
-
57
- Style/RaiseArgs:
58
- EnforcedStyle: compact
59
-
60
- Style/NegatedIf:
61
- Enabled: false
62
-
63
- Style/ClassAndModuleChildren:
64
- Exclude:
65
- - test/**/*_test.rb
66
-
67
- Naming/VariableNumber:
68
- Exclude:
69
- - test/**/*_test.rb
70
-
71
- Style/SingleLineMethods:
72
- Exclude:
73
- - test/**/*_test.rb
7
+ inherit_from: "https://www.goodcop.style/base.yml"
74
8
 
75
9
  Lint/ConstantDefinitionInBlock:
76
10
  Exclude:
77
- - test/**/*_test.rb
78
-
79
- Minitest/AssertTruthy:
80
- Enabled: false
81
-
82
- Minitest/RefuteFalse:
83
- Enabled: false
11
+ - 'test/**/*'
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- acidic_job (1.0.0.rc2)
4
+ acidic_job (1.0.0.rc4)
5
5
  activejob (>= 7.1)
6
6
  activerecord (>= 7.1)
7
7
  activesupport (>= 7.1)
@@ -11,41 +11,41 @@ PATH
11
11
  GEM
12
12
  remote: https://rubygems.org/
13
13
  specs:
14
- actionmailer (7.2.1.1)
15
- actionpack (= 7.2.1.1)
16
- actionview (= 7.2.1.1)
17
- activejob (= 7.2.1.1)
18
- activesupport (= 7.2.1.1)
14
+ actionmailer (8.0.2)
15
+ actionpack (= 8.0.2)
16
+ actionview (= 8.0.2)
17
+ activejob (= 8.0.2)
18
+ activesupport (= 8.0.2)
19
19
  mail (>= 2.8.0)
20
20
  rails-dom-testing (~> 2.2)
21
- actionpack (7.2.1.1)
22
- actionview (= 7.2.1.1)
23
- activesupport (= 7.2.1.1)
21
+ actionpack (8.0.2)
22
+ actionview (= 8.0.2)
23
+ activesupport (= 8.0.2)
24
24
  nokogiri (>= 1.8.5)
25
- racc
26
- rack (>= 2.2.4, < 3.2)
25
+ rack (>= 2.2.4)
27
26
  rack-session (>= 1.0.1)
28
27
  rack-test (>= 0.6.3)
29
28
  rails-dom-testing (~> 2.2)
30
29
  rails-html-sanitizer (~> 1.6)
31
30
  useragent (~> 0.16)
32
- actionview (7.2.1.1)
33
- activesupport (= 7.2.1.1)
31
+ actionview (8.0.2)
32
+ activesupport (= 8.0.2)
34
33
  builder (~> 3.1)
35
34
  erubi (~> 1.11)
36
35
  rails-dom-testing (~> 2.2)
37
36
  rails-html-sanitizer (~> 1.6)
38
- activejob (7.2.1.1)
39
- activesupport (= 7.2.1.1)
37
+ activejob (8.0.2)
38
+ activesupport (= 8.0.2)
40
39
  globalid (>= 0.3.6)
41
- activemodel (7.2.1.1)
42
- activesupport (= 7.2.1.1)
43
- activerecord (7.2.1.1)
44
- activemodel (= 7.2.1.1)
45
- activesupport (= 7.2.1.1)
40
+ activemodel (8.0.2)
41
+ activesupport (= 8.0.2)
42
+ activerecord (8.0.2)
43
+ activemodel (= 8.0.2)
44
+ activesupport (= 8.0.2)
46
45
  timeout (>= 0.4.0)
47
- activesupport (7.2.1.1)
46
+ activesupport (8.0.2)
48
47
  base64
48
+ benchmark (>= 0.3)
49
49
  bigdecimal
50
50
  concurrent-ruby (~> 1.0, >= 1.3.1)
51
51
  connection_pool (>= 2.2.5)
@@ -55,33 +55,38 @@ GEM
55
55
  minitest (>= 5.1)
56
56
  securerandom (>= 0.3)
57
57
  tzinfo (~> 2.0, >= 2.0.5)
58
- ast (2.4.2)
58
+ uri (>= 0.13.1)
59
+ ast (2.4.3)
59
60
  base64 (0.2.0)
60
- bigdecimal (3.1.8)
61
+ benchmark (0.4.0)
62
+ bigdecimal (3.1.9)
61
63
  builder (3.3.0)
62
64
  chaotic_job (0.3.0)
63
- combustion (1.3.7)
65
+ combustion (1.5.0)
64
66
  activesupport (>= 3.0.0)
65
67
  railties (>= 3.0.0)
66
68
  thor (>= 0.14.6)
67
- concurrent-ruby (1.3.4)
68
- connection_pool (2.4.0)
69
+ concurrent-ruby (1.3.5)
70
+ connection_pool (2.5.3)
69
71
  crass (1.0.6)
70
72
  date (3.4.1)
71
- docile (1.4.0)
73
+ docile (1.4.1)
72
74
  drb (2.2.1)
73
- erubi (1.13.0)
75
+ erubi (1.13.1)
74
76
  globalid (1.2.1)
75
77
  activesupport (>= 6.1)
76
- i18n (1.14.6)
78
+ i18n (1.14.7)
77
79
  concurrent-ruby (~> 1.0)
78
- io-console (0.7.2)
79
- irb (1.14.1)
80
+ io-console (0.8.0)
81
+ irb (1.15.2)
82
+ pp (>= 0.6.0)
80
83
  rdoc (>= 4.0.0)
81
84
  reline (>= 0.4.2)
82
- json (2.10.2)
83
- logger (1.6.1)
84
- loofah (2.22.0)
85
+ json (2.12.0)
86
+ language_server-protocol (3.17.0.5)
87
+ lint_roller (1.1.0)
88
+ logger (1.7.0)
89
+ loofah (2.24.1)
85
90
  crass (~> 1.0.2)
86
91
  nokogiri (>= 1.12.0)
87
92
  mail (2.8.1)
@@ -90,46 +95,52 @@ GEM
90
95
  net-pop
91
96
  net-smtp
92
97
  mini_mime (1.1.5)
93
- mini_portile2 (2.8.7)
94
- minitest (5.25.1)
95
- net-imap (0.5.1)
98
+ mini_portile2 (2.8.9)
99
+ minitest (5.25.5)
100
+ net-imap (0.5.8)
96
101
  date
97
102
  net-protocol
98
103
  net-pop (0.1.2)
99
104
  net-protocol
100
105
  net-protocol (0.2.2)
101
106
  timeout
102
- net-smtp (0.5.0)
107
+ net-smtp (0.5.1)
103
108
  net-protocol
104
- nokogiri (1.16.7)
109
+ nokogiri (1.18.8)
105
110
  mini_portile2 (~> 2.8.2)
106
111
  racc (~> 1.4)
107
- nokogiri (1.16.7-x86_64-darwin)
112
+ nokogiri (1.18.8-x86_64-darwin)
108
113
  racc (~> 1.4)
109
- parallel (1.22.1)
110
- parser (3.2.2.0)
114
+ parallel (1.27.0)
115
+ parser (3.3.8.0)
111
116
  ast (~> 2.4.1)
112
- psych (5.1.2)
117
+ racc
118
+ pp (0.6.2)
119
+ prettyprint
120
+ prettyprint (0.2.0)
121
+ prism (1.4.0)
122
+ psych (5.2.6)
123
+ date
113
124
  stringio
114
125
  racc (1.8.1)
115
- rack (3.1.8)
116
- rack-session (2.0.0)
126
+ rack (3.1.15)
127
+ rack-session (2.1.1)
128
+ base64 (>= 0.1.0)
117
129
  rack (>= 3.0.0)
118
- rack-test (2.1.0)
130
+ rack-test (2.2.0)
119
131
  rack (>= 1.3)
120
- rackup (2.1.0)
132
+ rackup (2.2.1)
121
133
  rack (>= 3)
122
- webrick (~> 1.8)
123
134
  rails-dom-testing (2.2.0)
124
135
  activesupport (>= 5.0.0)
125
136
  minitest
126
137
  nokogiri (>= 1.6)
127
- rails-html-sanitizer (1.6.0)
138
+ rails-html-sanitizer (1.6.2)
128
139
  loofah (~> 2.21)
129
- nokogiri (~> 1.14)
130
- railties (7.2.1.1)
131
- actionpack (= 7.2.1.1)
132
- activesupport (= 7.2.1.1)
140
+ nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
141
+ railties (8.0.2)
142
+ actionpack (= 8.0.2)
143
+ activesupport (= 8.0.2)
133
144
  irb (~> 1.13)
134
145
  rackup (>= 1.0.0)
135
146
  rake (>= 12.2)
@@ -137,48 +148,47 @@ GEM
137
148
  zeitwerk (~> 2.6)
138
149
  rainbow (3.1.1)
139
150
  rake (13.2.1)
140
- rdoc (6.7.0)
151
+ rdoc (6.13.1)
141
152
  psych (>= 4.0.0)
142
- regexp_parser (2.8.0)
143
- reline (0.5.10)
153
+ regexp_parser (2.10.0)
154
+ reline (0.6.1)
144
155
  io-console (~> 0.5)
145
- rexml (3.2.5)
146
- rubocop (1.50.2)
156
+ rubocop (1.75.6)
147
157
  json (~> 2.3)
158
+ language_server-protocol (~> 3.17.0.2)
159
+ lint_roller (~> 1.1.0)
148
160
  parallel (~> 1.10)
149
- parser (>= 3.2.0.0)
161
+ parser (>= 3.3.0.2)
150
162
  rainbow (>= 2.2.2, < 4.0)
151
- regexp_parser (>= 1.8, < 3.0)
152
- rexml (>= 3.2.5, < 4.0)
153
- rubocop-ast (>= 1.28.0, < 2.0)
163
+ regexp_parser (>= 2.9.3, < 3.0)
164
+ rubocop-ast (>= 1.44.0, < 2.0)
154
165
  ruby-progressbar (~> 1.7)
155
- unicode-display_width (>= 2.4.0, < 3.0)
156
- rubocop-ast (1.28.0)
157
- parser (>= 3.2.1.0)
158
- rubocop-minitest (0.30.0)
159
- rubocop (>= 1.39, < 2.0)
160
- rubocop-rake (0.6.0)
161
- rubocop (~> 1.0)
166
+ unicode-display_width (>= 2.4.0, < 4.0)
167
+ rubocop-ast (1.44.1)
168
+ parser (>= 3.3.7.2)
169
+ prism (~> 1.4)
162
170
  ruby-progressbar (1.13.0)
163
- securerandom (0.3.1)
171
+ securerandom (0.4.1)
164
172
  simplecov (0.22.0)
165
173
  docile (~> 1.1)
166
174
  simplecov-html (~> 0.11)
167
175
  simplecov_json_formatter (~> 0.1)
168
- simplecov-html (0.12.3)
176
+ simplecov-html (0.13.1)
169
177
  simplecov_json_formatter (0.1.4)
170
- sqlite3 (1.6.2)
178
+ sqlite3 (2.6.0)
171
179
  mini_portile2 (~> 2.8.0)
172
- sqlite3 (1.6.2-x86_64-darwin)
173
- stringio (3.1.1)
180
+ sqlite3 (2.6.0-x86_64-darwin)
181
+ stringio (3.1.7)
174
182
  thor (1.3.2)
175
- timeout (0.4.1)
183
+ timeout (0.4.3)
176
184
  tzinfo (2.0.6)
177
185
  concurrent-ruby (~> 1.0)
178
- unicode-display_width (2.4.2)
179
- useragent (0.16.10)
180
- webrick (1.8.2)
181
- zeitwerk (2.7.1)
186
+ unicode-display_width (3.1.4)
187
+ unicode-emoji (~> 4.0, >= 4.0.4)
188
+ unicode-emoji (4.0.4)
189
+ uri (1.0.3)
190
+ useragent (0.16.11)
191
+ zeitwerk (2.7.2)
182
192
 
183
193
  PLATFORMS
184
194
  ruby
@@ -192,8 +202,6 @@ DEPENDENCIES
192
202
  minitest
193
203
  rake
194
204
  rubocop
195
- rubocop-minitest
196
- rubocop-rake
197
205
  simplecov
198
206
  sqlite3
199
207
 
data/README.md CHANGED
@@ -24,7 +24,7 @@ With AcidicJob, you can write reliable and repeatable multi-step distributed ope
24
24
  Install the gem and add to the application's Gemfile by executing:
25
25
 
26
26
  ```sh
27
- bundle add acidic_job --version "1.0.0.rc1"
27
+ bundle add acidic_job --version "1.0.0.rc3"
28
28
  ```
29
29
 
30
30
  If `bundler` is not being used to manage dependencies, install the gem by executing:
@@ -109,7 +109,7 @@ The block passed to `execute_workflow` is where you define the steps of the work
109
109
  The `step` method is the only method available on the yielded workflow builder object, and it simply takes the name of a method available in the job.
110
110
 
111
111
  > [!IMPORTANT]
112
- > In order to craft resilient workflows, you need to ensure that each step method wraps a single unit of IO-bound work. You **must not** have a step method that performs multiple IO-bound operations, like writing to your database and calling an external API. Steps should be as granular and self-contained as possible. This allows your own logic to be more durable in case of failures in third-party APIs, network errors, and so on. So, the rule of thumb is to have only one _state mutation_ per step. And this rule of thumb graduates to a hard and fast rule for _foreign state mutations_. You **must** only have **one** foreign state mutation per step, where a foreign state mutation is any operation that writes to a system beyond your own boundaries. This might be creating a charge on Stripe, adding a DNS record, or sending an email.[^1]
112
+ > In order to craft resilient workflows, you need to ensure that each step method wraps a single unit of IO-bound work. You **should not** have a step method that performs multiple IO-bound operations, like writing to your database and calling an external API. Steps should be as granular and self-contained as possible. This allows your own logic to be more durable in case of failures in third-party APIs, network errors, and so on. So, the rule of thumb is to have only one _state mutation_ per step. And this rule of thumb graduates to a hard and fast rule for _foreign state mutations_. You **must** only have **one** foreign state mutation per step, where a foreign state mutation is any operation that writes to a system beyond your own boundaries. This might be creating a charge on Stripe, adding a DNS record, or sending an email.[^1]
113
113
 
114
114
  [^1]: I first learned this rule from [Brandur Leach](https://twitter.com/brandur) reminds in his post on [Implementing Stripe-like Idempotency Keys in Postgres](https://brandur.org/idempotency-keys).
115
115
 
@@ -188,7 +188,7 @@ class Job < ActiveJob::Base
188
188
 
189
189
  ### Orchestrating steps
190
190
 
191
- In addition to the workflow definition setup, `AcidicJob` also provides a couple of methods to precisely control the workflow step execution. From within any step method, you can call either `repeat_step!` or `halt_step!`.
191
+ In addition to the workflow definition setup, `AcidicJob` also provides a couple of methods to precisely control the workflow step execution. From within any step method, you can call either `repeat_step!` or `halt_workflow!`.
192
192
 
193
193
  `repeat_step!` will cause the current step to be re-executed on the next iteration of the workflow. This is useful when you need to traverse a collection of items and perform the same operation on each item. For example, if you need to send an email to each user in a collection, you could do something like this:
194
194
 
@@ -218,7 +218,7 @@ end
218
218
 
219
219
  This example demonstrates how you can leverage the basic building blocks provided by `AcidicJob` to orchestrate complex workflows. In this case, the `notify_users` step sends an email to each user in the collection, one at a time, and resiliently handles errors by storing a cursor in the `ctx` object to keep track of the current user being processed. If any error occurs while traversing the `@users` collection, the job will be retried, and the `notify_users` step will be re-executed from the last successful cursor position.
220
220
 
221
- The `halt_step!` method, on the other hand, stops not just the execution of the current step but the job as a whole. This is useful when you either need to conditionally stop the workflow based on some criteria or need to delay the job for some amount of time before being restarted. For example, if you need to send a follow-up email to a user 14 days after they sign up, you could do something like this:
221
+ The `halt_workflow!` method, on the other hand, stops not just the execution of the current step but the job as a whole. This is useful when you either need to conditionally stop the workflow based on some criteria or need to delay the job for some amount of time before being restarted. For example, if you need to send a follow-up email to a user 14 days after they sign up, you could do something like this:
222
222
 
223
223
  ```ruby
224
224
  class Job < ActiveJob::Base
@@ -240,7 +240,7 @@ class Job < ActiveJob::Base
240
240
  def send_welcome_email
241
241
  if ctx[:halt]
242
242
  ctx[:halt] = false
243
- halt_step!
243
+ halt_workflow!
244
244
  end
245
245
  UserMailer.with(user: @user).welcome_email.deliver_later
246
246
  end
@@ -252,7 +252,7 @@ In this example, the `delay` step creates a new instance of the job and enqueues
252
252
 
253
253
  ### Overview
254
254
 
255
- `AcidicJob` is a library that provides a small yet powerful set of tools to build cohesive and resilient workflows in your Active Jobs. All of the tools are made available by `include`ing the `AcidicJob::Workflow` module. The primary and most important tool is the `execute_workflow` method, which you call within your `perform` method. Then, if you need to store any contextual data, you use the `ctx` objects setters and getters. Finally, within any step methods, you can call `repeat_step!` or `halt_step!` to control the execution of the workflow. If you need, you can also access the `execution` Active Record object to get information about the current execution of the workflow. With these lightweight tools, you can build complex workflows that are resilient to failures and can handle a wide range of use cases.
255
+ `AcidicJob` is a library that provides a small yet powerful set of tools to build cohesive and resilient workflows in your Active Jobs. All of the tools are made available by `include`ing the `AcidicJob::Workflow` module. The primary and most important tool is the `execute_workflow` method, which you call within your `perform` method. Then, if you need to store any contextual data, you use the `ctx` objects setters and getters. Finally, within any step methods, you can call `repeat_step!` or `halt_workflow!` to control the execution of the workflow. If you need, you can also access the `execution` Active Record object to get information about the current execution of the workflow. With these lightweight tools, you can build complex workflows that are resilient to failures and can handle a wide range of use cases.
256
256
 
257
257
 
258
258
  ## Testing
data/acidic_job.gemspec CHANGED
@@ -41,8 +41,6 @@ Gem::Specification.new do |spec|
41
41
  spec.add_development_dependency "minitest"
42
42
  spec.add_development_dependency "rake"
43
43
  spec.add_development_dependency "rubocop"
44
- spec.add_development_dependency "rubocop-minitest"
45
- spec.add_development_dependency "rubocop-rake"
46
44
  spec.add_development_dependency "simplecov"
47
45
  spec.add_development_dependency "sqlite3"
48
46
 
@@ -4,16 +4,18 @@ module AcidicJob
4
4
  class Entry < Record
5
5
  belongs_to :execution, class_name: "AcidicJob::Execution"
6
6
 
7
- def started?
8
- action == "started"
9
- end
7
+ serialize :data, coder: AcidicJob::Serializer
8
+
9
+ scope :for_step, -> (step) { where(step: step) }
10
+ scope :for_action, -> (action) { where(action: action) }
11
+ scope :ordered, -> { order(timestamp: :asc) }
10
12
 
11
- def succeeded?
12
- action == "succeeded"
13
+ def self.most_recent
14
+ order(created_at: :desc).first
13
15
  end
14
16
 
15
- def errored?
16
- action == "errored"
17
+ def action?(check)
18
+ action == check
17
19
  end
18
20
  end
19
21
  end
@@ -2,25 +2,40 @@
2
2
 
3
3
  module AcidicJob
4
4
  class Execution < Record
5
- has_many :entries, class_name: "AcidicJob::Entry"
6
- has_many :values, class_name: "AcidicJob::Value"
5
+ has_many :entries, class_name: "AcidicJob::Entry", dependent: :destroy
6
+ has_many :values, class_name: "AcidicJob::Value", dependent: :destroy
7
+
8
+ serialize :definition, coder: AcidicJob::Serializer
7
9
 
8
10
  validates :idempotency_key, presence: true # uniqueness constraint is enforced at the database level
9
11
  validates :serialized_job, presence: true
10
12
 
11
- scope :finished, -> { where(recover_to: FINISHED_RECOVERY_POINT) }
12
- scope :outstanding, lambda {
13
- where.not(recover_to: FINISHED_RECOVERY_POINT).or(where(recover_to: [nil, ""]))
14
- }
13
+ scope :finished, -> {
14
+ where(recover_to: FINISHED_RECOVERY_POINT)
15
+ }
16
+ scope :outstanding, -> {
17
+ where.not(recover_to: FINISHED_RECOVERY_POINT).or(where(recover_to: [nil, ""]))
18
+ }
19
+ scope :clearable, -> (finished_before: AcidicJob.clear_finished_executions_after.ago) {
20
+ finished.where(last_run_at: ...finished_before)
21
+ }
15
22
 
16
- def record!(step:, action:, timestamp:, **kwargs)
23
+ def self.clear_finished_in_batches(batch_size: 500, finished_before: AcidicJob.clear_finished_executions_after.ago, sleep_between_batches: 0)
24
+ loop do
25
+ records_deleted = clearable(finished_before: finished_before).limit(batch_size).delete_all
26
+ sleep(sleep_between_batches) if sleep_between_batches > 0
27
+ break if records_deleted == 0
28
+ end
29
+ end
30
+
31
+ def record!(step:, action:, timestamp: Time.current, **kwargs)
17
32
  AcidicJob.instrument(:record_entry, step: step, action: action, timestamp: timestamp, data: kwargs) do
18
- entries.create!(
33
+ entries.insert!({
19
34
  step: step,
20
35
  action: action,
21
36
  timestamp: timestamp,
22
- data: kwargs.stringify_keys!
23
- )
37
+ data: kwargs.except(:ignored),
38
+ })
24
39
  end
25
40
  end
26
41
 
@@ -29,7 +44,26 @@ module AcidicJob
29
44
  end
30
45
 
31
46
  def finished?
32
- recover_to.to_s == FINISHED_RECOVERY_POINT
47
+ recover_to.to_s == FINISHED_RECOVERY_POINT ||
48
+ recover_to.to_s == "FINISHED" # old value pre-1.0, remove at v1.0
49
+ end
50
+
51
+ def defined?(step)
52
+ if definition.key?("steps")
53
+ definition["steps"].key?(step)
54
+ else
55
+ # TODO: add deprecation warning
56
+ definition.key?(step)
57
+ end
58
+ end
59
+
60
+ def definition_for(step)
61
+ if definition.key?("steps")
62
+ definition["steps"].fetch(step)
63
+ else
64
+ # TODO: add deprecation warning
65
+ definition.fetch(step)
66
+ end
33
67
  end
34
68
 
35
69
  def deserialized_job
@@ -3,5 +3,7 @@
3
3
  module AcidicJob
4
4
  class Value < Record
5
5
  belongs_to :execution, class_name: "AcidicJob::Execution"
6
+
7
+ serialize :value, coder: AcidicJob::Serializer
6
8
  end
7
9
  end
data/bin/console CHANGED
@@ -2,8 +2,6 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "bundler/setup"
5
- require "rails"
6
- require "acidic_job"
7
5
 
8
6
  # You can add fixtures and/or initialization code here to make experimenting
9
7
  # with your gem easier. You can also use a different console, if you like.
@@ -11,7 +9,9 @@ require "acidic_job"
11
9
  require "combustion"
12
10
  require "sqlite3"
13
11
  Combustion.path = "test/combustion"
14
- Combustion.initialize! :active_record
12
+ Combustion.initialize! :active_record, :active_job
13
+
14
+ require "acidic_job"
15
15
 
16
16
  # (If you use this, don't forget to add pry to your Gemfile!)
17
17
  # require "pry"
@@ -4,12 +4,21 @@ module AcidicJob
4
4
  class Builder
5
5
  attr_reader :steps
6
6
 
7
- def initialize
7
+ def initialize(plugins)
8
+ @plugins = plugins
8
9
  @steps = []
9
10
  end
10
11
 
11
- def step(method_name, transactional: false)
12
- @steps << { "does" => method_name.to_s, "transactional" => transactional }
12
+ def step(method_name, **kwargs)
13
+ step = { "does" => method_name.to_s }
14
+
15
+ @plugins.each do |plugin|
16
+ next unless kwargs.key?(plugin.keyword)
17
+
18
+ step[plugin.keyword.to_s] = plugin.validate(kwargs[plugin.keyword])
19
+ end
20
+
21
+ @steps << step
13
22
  @steps
14
23
  end
15
24
 
@@ -17,13 +26,20 @@ module AcidicJob
17
26
  # [ { does: "step 1", transactional: true }, { does: "step 2", transactional: false }, ... ]
18
27
  @steps << { "does" => FINISHED_RECOVERY_POINT }
19
28
 
20
- {}.tap do |workflow|
29
+ definition = {
30
+ "meta" => {
31
+ "version" => VERSION,
32
+ },
33
+ "steps" => {},
34
+ }
35
+
36
+ definition.tap do |workflow|
21
37
  @steps.each_cons(2).map do |enter_step, exit_step|
22
38
  enter_name = enter_step["does"]
23
- workflow[enter_name] = enter_step.merge("then" => exit_step["does"])
39
+ workflow["steps"][enter_name] = enter_step.merge("then" => exit_step["does"])
24
40
  end
25
41
  end
26
- # { "step 1": { does: "step 1", transactional: true, then: "step 2" }, ... }
42
+ # { meta: { ... }, steps: { "step 1": { does: "step 1", transactional: true, then: "step 2" }, ... } }
27
43
  end
28
44
  end
29
45
  end