job-workflow 0.3.0 → 0.5.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
  SHA256:
3
- metadata.gz: 2a4a82b69372293b7bf689c33125b60b4cbdbcdf4f2e1f468e205cf9bf0c1d85
4
- data.tar.gz: e2e6e91167d6127e6cee7e2af0339f79ffee244fe6e768d73e50ebb6c2e7b372
3
+ metadata.gz: 9474c65583cf21d485c3468675d4f23124e60e88f98c954bd38256b916d98ffe
4
+ data.tar.gz: 59d5e0b60a2b52e87ea4da4aab1834d02c63f88cba36f661acd8c74f89a4e6a9
5
5
  SHA512:
6
- metadata.gz: 84356b092c371e103d601576f9fc6ed5cdee9025340948b0d1fe8222b5b1f49f301342f8254a0d858fc89da58e8f828241d7d8ec706103349e63d6f68a426945
7
- data.tar.gz: b146df16edb3112fa10379437e03ebd6a7f74c46e3a83526bb195320e141e9493319caa96d601622cf1e2f5d8cf18b071cc56c93aac1ad39492b89cfa8e08392
6
+ metadata.gz: 4ac034e1b373b28aa52dcffd059bc5b64e5c45fc04d9b07460c24491c2514eabddf839b2dcb2c4a2cfbf36b578e2836e153e9bd6f34cd24f8af543cf06242ce9
7
+ data.tar.gz: 455b6913137103b6a5871562a6adc0ffca11e9e99543b94ec7e0bda9f2b78c6c76c5cee73baec30ca5b5b5e2fd388d6a4b119c08d7cf1039c20e115e255f58d7
data/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.5.0] - 2026-05-13
4
+
5
+ ### Added
6
+
7
+ - Add task-scoped cursor helpers on `Context` so tasks can persist progress, restore task-local continuation state, and delegate explicit checkpoints without exposing ActiveJob continuation internals
8
+
9
+ ### Fixed
10
+
11
+ - Initialize the SolidQueue adapter from a Railtie after Rails boot so the `ClaimedExecution` patch is applied reliably, rescheduled `dependency_wait` jobs are not marked finished early, and Rails apps no longer need a manual initializer
12
+
13
+ ## [0.4.0] - 2026-05-12
14
+
15
+ ### Fixed
16
+
17
+ - Fix `enqueue: true` consumer tasks that read `depends_on` outputs by persisting the parent context before `perform_all_later`, so sub-jobs load the latest dependency outputs instead of stale persisted state
18
+ - Fix sequential `each` task resumption to persist continuation cursor progress at the workflow step level, resume from `step.cursor`, and keep the failed iteration retrying from the same `each_index` without skipping previously collected outputs
19
+
20
+ ### Removed
21
+
22
+ - Remove the `namespace` DSL and `JobWorkflow::Namespace`, flatten task naming to a single task space, and delete the dedicated namespace guide/example coverage that existed for the removed feature
23
+
3
24
  ## [0.3.0] - 2026-03-13
4
25
 
5
26
  ### Added
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # JobWorkflow
2
2
 
3
- > ⚠️ **Early Stage (v0.3.0):** This library is in active development. APIs and features may change in breaking ways without notice. Use in production at your own risk and expect potential breaking changes in future releases.
3
+ > ⚠️ **Early Stage (v0.5.0):** This library is in active development. APIs and features may change in breaking ways without notice. Use in production at your own risk and expect potential breaking changes in future releases.
4
4
 
5
5
  ## Overview
6
6
 
@@ -84,6 +84,76 @@ end
84
84
 
85
85
  **Map Task Output**: When `each:` is specified, outputs are automatically collected as an array.
86
86
 
87
+ ### Task continuation helpers
88
+
89
+ Inside a task body, you can read the current task cursor, store a new cursor, and create interruption points through the task context.
90
+
91
+ #### Regular task example
92
+
93
+ ```ruby
94
+ task :sync_pages, output: { processed: "Integer" } do |ctx|
95
+ page = ctx.cursor || 1
96
+ result = ExternalAPI.fetch(page:)
97
+
98
+ ctx.set_cursor!(page + 1) if result.next_page?
99
+
100
+ { processed: result.items.size }
101
+ end
102
+ ```
103
+
104
+ - `ctx.cursor` returns the current task cursor, or `nil` when no cursor has been stored
105
+ - `ctx.set_cursor!(value)` validates that `value` is ActiveJob-serializable, stores it in the current continuation step, and checkpoints the job through Active Job continuation
106
+ - `ctx.checkpoint!` creates a checkpoint without changing the public cursor value
107
+ - Call `ctx.set_cursor!` when you want to change the public cursor value and create a checkpoint at the same time
108
+ - Call `ctx.checkpoint!` when you want the current task execution to become interruptible without changing the public cursor value
109
+ - Outside task execution, `ctx.cursor` returns `nil`, and `ctx.set_cursor!` / `ctx.checkpoint!` raise an error
110
+
111
+ For regular tasks, a cursor is only persisted when you call `ctx.set_cursor!(value)` explicitly.
112
+
113
+ #### Checkpoint without changing the cursor
114
+
115
+ ```ruby
116
+ task :publish_report do |ctx|
117
+ report = build_report
118
+ ctx.checkpoint!
119
+ deliver_report(report)
120
+ end
121
+ ```
122
+
123
+ #### Repeating work inside a task
124
+
125
+ ```ruby
126
+ task :sync_users do |ctx|
127
+ start_index = ctx.cursor || 0
128
+
129
+ ctx.arguments.user_ids.drop(start_index).each_with_index do |user_id, offset|
130
+ sync_user(user_id)
131
+ ctx.set_cursor!(start_index + offset + 1)
132
+ end
133
+ end
134
+ ```
135
+
136
+ This pattern is useful when a single task iterates over an Enumerable internally and you want to resume from the last completed item after an interruption.
137
+
138
+ #### `each:` task example
139
+
140
+ ```ruby
141
+ task :sync_users, each: ->(ctx) { ctx.arguments.user_ids } do |ctx|
142
+ next_cursor = ExternalAPI.sync_user(
143
+ user_id: ctx.each_value,
144
+ cursor: ctx.cursor
145
+ )
146
+
147
+ ctx.set_cursor!(next_cursor) unless next_cursor.nil?
148
+ end
149
+ ```
150
+
151
+ For `each:` tasks, JobWorkflow keeps the existing integer resume behavior for completed iterations.
152
+
153
+ - If an `each:` task is interrupted after calling `ctx.set_cursor!`, JobWorkflow resumes with both the current iteration index and the saved task cursor
154
+ - If an iteration completes normally, the resume state advances to the next integer index
155
+ - In other words, a custom cursor in an `each:` task is meant for resuming work inside the current iteration, not for replacing the completion index of finished iterations
156
+
87
157
  ### workflow_concurrency
88
158
 
89
159
  Configure job-level concurrency limits with workflow-aware context.
@@ -1,6 +1,6 @@
1
1
  # Production Deployment
2
2
 
3
- > ⚠️ **Early Stage (v0.3.0):** JobWorkflow is still in early development. While this section outlines potential deployment patterns, please thoroughly test in your specific environment and monitor for any issues before relying on JobWorkflow in critical production systems.
3
+ > ⚠️ **Early Stage (v0.5.0):** JobWorkflow is still in early development. While this section outlines potential deployment patterns, please thoroughly test in your specific environment and monitor for any issues before relying on JobWorkflow in critical production systems.
4
4
 
5
5
  This section covers suggested settings and patterns for running JobWorkflow in production-like environments.
6
6
 
data/guides/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # JobWorkflow Guides
2
2
 
3
- > ⚠️ **Early Stage (v0.3.0):** JobWorkflow is in active development. APIs and features may change. The following guides provide patterns and examples for building workflows, but be aware that implementations may need adjustment as the library evolves.
3
+ > ⚠️ **Early Stage (v0.5.0):** JobWorkflow is in active development. APIs and features may change. The following guides provide patterns and examples for building workflows, but be aware that implementations may need adjustment as the library evolves.
4
4
 
5
5
  Welcome to the JobWorkflow documentation! This directory contains comprehensive guides to help you build robust workflows with JobWorkflow.
6
6
 
@@ -67,11 +67,6 @@ Power features for complex workflows:
67
67
  - Configuration options (poll_timeout, poll_interval, reschedule_delay)
68
68
  - SolidQueue integration
69
69
 
70
- - **[NAMESPACES.md](NAMESPACES.md)** - Organizing large workflows
71
- - Basic namespace usage
72
- - Nested namespaces
73
- - Cross-namespace dependencies
74
-
75
70
  - **[THROTTLING.md](THROTTLING.md)** - Rate limiting and resource control
76
71
  - Task-level throttling
77
72
  - Runtime throttling
@@ -2,6 +2,8 @@
2
2
 
3
3
  module JobWorkflow
4
4
  class Context # rubocop:disable Metrics/ClassLength
5
+ EACH_TASK_CURSOR_MARKER = "__job_workflow_each_cursor__"
6
+
5
7
  attr_reader :workflow #: Workflow
6
8
  attr_reader :arguments #: Arguments
7
9
  attr_reader :output #: Output
@@ -52,7 +54,7 @@ module JobWorkflow
52
54
  # job_status: JobStatus,
53
55
  # ?job: DSL?
54
56
  # ) -> void
55
- def initialize(workflow:, arguments:, task_context:, output:, job_status:, job: nil) # rubocop:disable Metrics/ParameterLists
57
+ def initialize(workflow:, arguments:, task_context:, output:, job_status:, job: nil) # rubocop:disable Metrics/ParameterLists, Metrics/AbcSize, Metrics/MethodLength
56
58
  raise "job does not match the provided workflow" if job&.then { |j| j.class._workflow != workflow }
57
59
 
58
60
  self.job = job
@@ -64,6 +66,8 @@ module JobWorkflow
64
66
  self.enabled_with_each_value = false
65
67
  self.throttle_index = 0
66
68
  self.skip_in_dry_run_index = 0
69
+ self.current_step = nil
70
+ self.current_cursor = nil
67
71
  end
68
72
 
69
73
  #: () -> Hash[String, untyped]
@@ -77,6 +81,31 @@ module JobWorkflow
77
81
  self
78
82
  end
79
83
 
84
+ #: () -> untyped
85
+ def cursor
86
+ return if current_step.nil?
87
+
88
+ current_cursor
89
+ end
90
+
91
+ #: (untyped) -> void
92
+ def set_cursor!(value)
93
+ step = current_step || (raise "set_cursor! can be called only in task")
94
+
95
+ ActiveJob::Arguments.serialize([value])
96
+ self.current_cursor = value
97
+ step.set!(build_step_cursor(value))
98
+ end
99
+
100
+ #: () -> void
101
+ def checkpoint!
102
+ step = current_step || (raise "checkpoint! can be called only in task")
103
+
104
+ return step.checkpoint! unless each_task?
105
+
106
+ step.set!(build_step_cursor(current_cursor))
107
+ end
108
+
80
109
  #: (DSL) -> void
81
110
  def _job=(job)
82
111
  self.job = job
@@ -108,13 +137,13 @@ module JobWorkflow
108
137
  [task_context.parent_job_id, task.task_name].compact.join("/")
109
138
  end
110
139
 
111
- #: (Task) -> Enumerator[Context]
112
- def _with_each_value(task)
140
+ #: (Task, ?start_index: Integer?) -> Enumerator[Context]
141
+ def _with_each_value(task, start_index: nil)
113
142
  raise "Nested _with_each_value calls are not allowed" if enabled_with_each_value
114
143
 
115
144
  self.enabled_with_each_value = true
116
145
  Enumerator.new do |y|
117
- with_task_context(task, y)
146
+ with_task_context(task, y, start_index:)
118
147
  ensure
119
148
  self.enabled_with_each_value = false
120
149
  end
@@ -220,6 +249,18 @@ module JobWorkflow
220
249
  task_context
221
250
  end
222
251
 
252
+ #: (ActiveJob::Continuation::Step, ?cursor: untyped) { () -> void } -> void
253
+ def _with_current_step(step, cursor: nil)
254
+ previous_step = current_step
255
+ previous_cursor = current_cursor
256
+ self.current_step = step
257
+ self.current_cursor = cursor
258
+ yield
259
+ ensure
260
+ self.current_step = previous_step
261
+ self.current_cursor = previous_cursor
262
+ end
263
+
223
264
  #: (TaskOutput) -> void
224
265
  def _add_task_output(task_output)
225
266
  output.add_task_output(task_output)
@@ -245,12 +286,31 @@ module JobWorkflow
245
286
  attr_accessor :enabled_with_each_value #: bool
246
287
  attr_accessor :throttle_index #: Integer
247
288
  attr_accessor :skip_in_dry_run_index #: Integer
289
+ attr_accessor :current_step #: ActiveJob::Continuation::Step?
290
+ attr_accessor :current_cursor #: untyped
248
291
 
249
292
  #: () -> String
250
293
  def parent_job_id
251
294
  _task_context.parent_job_id || job_id
252
295
  end
253
296
 
297
+ #: () -> bool
298
+ def each_task?
299
+ task_context.task.each?
300
+ end
301
+
302
+ #: (untyped) -> untyped
303
+ def build_step_cursor(value)
304
+ return value unless each_task?
305
+ return task_context.index if value.nil?
306
+
307
+ {
308
+ EACH_TASK_CURSOR_MARKER => true,
309
+ "index" => task_context.index,
310
+ "cursor" => value
311
+ }
312
+ end
313
+
254
314
  #: () -> Hash[String, untyped]
255
315
  def serialize_for_job
256
316
  {
@@ -270,11 +330,11 @@ module JobWorkflow
270
330
  }
271
331
  end
272
332
 
273
- #: (Task, Enumerator::Yielder) -> void
274
- def with_task_context(task, yielder) # rubocop:disable Metrics/MethodLength
333
+ #: (Task, Enumerator::Yielder, ?start_index: Integer?) -> void
334
+ def with_task_context(task, yielder, start_index: nil) # rubocop:disable Metrics/MethodLength
275
335
  reset_task_context_if_task_changed(task)
276
336
 
277
- with_each_index_and_value(task) do |value, index|
337
+ with_each_index_and_value(task, start_index:) do |value, index|
278
338
  dry_run = calculate_dry_run(task)
279
339
  with_retry(task) do |retry_count|
280
340
  self.task_context = TaskContext.new(task:, parent_job_id:, index:, value:, retry_count:, dry_run:)
@@ -294,10 +354,12 @@ module JobWorkflow
294
354
  self.task_context = TaskContext.new if task_context.task&.task_name != task.task_name
295
355
  end
296
356
 
297
- #: (Task) { (untyped, Integer) -> void } -> void
298
- def with_each_index_and_value(task)
357
+ #: (Task, ?start_index: Integer?) { (untyped, Integer) -> void } -> void
358
+ def with_each_index_and_value(task, start_index: nil)
359
+ resume_index = start_index || task_context.index
360
+
299
361
  task.each.call(self).each.with_index do |value, index|
300
- next if index < task_context.index
362
+ next if index < resume_index
301
363
 
302
364
  yield value, index
303
365
 
@@ -108,15 +108,9 @@ module JobWorkflow
108
108
 
109
109
  #: (Symbol argument_name, String type, ?default: untyped) -> void
110
110
  def argument(argument_name, type, default: nil)
111
- validate_namespace!
112
111
  _workflow.add_argument(ArgumentDef.new(name: argument_name, type:, default:))
113
112
  end
114
113
 
115
- #: (Symbol) { () -> void } -> void
116
- def namespace(namespace_name, &)
117
- _workflow.add_namespace(Namespace.new(name: namespace_name), &)
118
- end
119
-
120
114
  # rubocop:disable Metrics/ParameterLists
121
115
  #
122
116
  #: (
@@ -134,7 +128,7 @@ module JobWorkflow
134
128
  # ) { (untyped) -> void } -> void
135
129
  def task(
136
130
  task_name,
137
- each: ->(_ctx) { [TaskContext::NULL_VALUE] },
131
+ each: Task::DEFAULT_EACH,
138
132
  enqueue: nil,
139
133
  retry: 0,
140
134
  output: {},
@@ -149,7 +143,6 @@ module JobWorkflow
149
143
  new_task = Task.new(
150
144
  job_name: name,
151
145
  name: task_name,
152
- namespace: _workflow.namespace,
153
146
  block: block,
154
147
  enqueue:,
155
148
  each:,
@@ -172,25 +165,21 @@ module JobWorkflow
172
165
 
173
166
  #: (*Symbol) { (Context) -> void } -> void
174
167
  def before(*task_names, &block)
175
- validate_namespace!
176
168
  _workflow.add_hook(:before, task_names:, block:)
177
169
  end
178
170
 
179
171
  #: (*Symbol) { (Context) -> void } -> void
180
172
  def after(*task_names, &block)
181
- validate_namespace!
182
173
  _workflow.add_hook(:after, task_names:, block:)
183
174
  end
184
175
 
185
176
  #: (*Symbol) { (Context, TaskCallable) -> void } -> void
186
177
  def around(*task_names, &block)
187
- validate_namespace!
188
178
  _workflow.add_hook(:around, task_names:, block:)
189
179
  end
190
180
 
191
181
  #: (*Symbol) { (Context, StandardError, Task) -> void } -> void
192
182
  def on_error(*task_names, &block)
193
- validate_namespace!
194
183
  _workflow.add_hook(:error, task_names:, block:)
195
184
  end
196
185
 
@@ -240,7 +229,6 @@ module JobWorkflow
240
229
 
241
230
  #: (?bool) ?{ (Context) -> bool } -> void
242
231
  def dry_run(value = nil, &block)
243
- validate_namespace!
244
232
  _workflow.dry_run_config = block || value
245
233
  end
246
234
 
@@ -254,7 +242,6 @@ module JobWorkflow
254
242
  # ?description: String?
255
243
  # ) -> void
256
244
  def schedule(expression, key: nil, queue: nil, priority: nil, args: {}, description: nil)
257
- validate_namespace!
258
245
  _workflow.add_schedule(
259
246
  Schedule.new(
260
247
  expression:,
@@ -268,13 +255,6 @@ module JobWorkflow
268
255
  )
269
256
  end
270
257
  # rubocop:enable Metrics/ParameterLists
271
-
272
- private
273
-
274
- #: () -> void
275
- def validate_namespace!
276
- raise "cannot be defined within a namespace." unless _workflow.namespace.default?
277
- end
278
258
  end
279
259
  end
280
260
  end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JobWorkflow
4
+ class Railtie < Rails::Railtie
5
+ config.after_initialize do
6
+ JobWorkflow::QueueAdapter.reset!
7
+ JobWorkflow::QueueAdapter.current.initialize_adapter!
8
+ end
9
+ end
10
+ end
@@ -14,13 +14,13 @@ module JobWorkflow
14
14
  def run
15
15
  task = context._task_context.task
16
16
  if !task.nil? && context.sub_job?
17
- run_task(task)
18
- QueueAdapter.current.persist_job_context(job)
17
+ job.step(task.task_name) { |step| run_task(task, step:) }
18
+ persist_current_job_context
19
19
  return
20
20
  end
21
21
 
22
22
  catch(:rescheduled) { run_workflow }
23
- QueueAdapter.current.persist_job_context(job)
23
+ persist_current_job_context
24
24
  end
25
25
 
26
26
  private
@@ -50,7 +50,7 @@ module JobWorkflow
50
50
 
51
51
  job.step(task.task_name) do |step|
52
52
  wait_for_dependent_tasks(task, step)
53
- task.enqueue.should_enqueue?(context) ? enqueue_task(task) : run_task(task)
53
+ task.enqueue.should_enqueue?(context) ? enqueue_task(task) : run_task(task, step:)
54
54
  end
55
55
  end
56
56
  end
@@ -63,24 +63,46 @@ module JobWorkflow
63
63
  result
64
64
  end
65
65
 
66
- #: (Task) -> void
67
- def run_task(task)
66
+ #: (Task, step: ActiveJob::Continuation::Step) -> void
67
+ def run_task(task, step:) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
68
68
  context._load_parent_task_output
69
- context._with_each_value(task).each do |ctx|
70
- run_each_task(task, ctx)
69
+ start_index, task_cursor = decode_task_cursor(task, step.cursor)
70
+
71
+ context._with_each_value(task, start_index:).each do |ctx|
72
+ iteration_cursor = task_cursor
73
+ iteration_cursor = nil if task.each? && (task_cursor.nil? || start_index != ctx._task_context.index)
74
+
75
+ run_each_task(task, ctx, step:, cursor: iteration_cursor)
76
+ step.set!(ctx._task_context.index + 1) if task.each?
71
77
  rescue StandardError => e
72
78
  run_error_hooks(task, ctx, e)
73
79
  raise
74
80
  end
75
81
  end
76
82
 
77
- #: (Task, Context) -> void
78
- def run_each_task(task, ctx)
83
+ #: (Task, untyped) -> [Integer?, untyped]
84
+ def decode_task_cursor(task, task_cursor)
85
+ return [nil, task_cursor] unless task.each?
86
+ return [task_cursor, nil] if task_cursor.is_a?(Integer)
87
+
88
+ if task_cursor.is_a?(Hash) && task_cursor[Context::EACH_TASK_CURSOR_MARKER]
89
+ return [task_cursor.fetch("index"), task_cursor.fetch("cursor")]
90
+ end
91
+
92
+ raise "invalid each task cursor: #{task_cursor.inspect}" unless task_cursor.nil?
93
+
94
+ [nil, nil]
95
+ end
96
+
97
+ #: (Task, Context, step: ActiveJob::Continuation::Step, ?cursor: untyped) -> void
98
+ def run_each_task(task, ctx, step:, cursor: nil)
79
99
  Instrumentation.instrument_task(job, task, ctx) do
80
- ctx._with_task_throttle do
81
- run_hooks(task, ctx) do
82
- data = task.block.call(ctx)
83
- add_task_output(ctx:, task:, each_index: ctx._task_context.index, data:)
100
+ ctx._with_current_step(step, cursor:) do
101
+ ctx._with_task_throttle do
102
+ run_hooks(task, ctx) do
103
+ data = task.block.call(ctx)
104
+ add_task_output(ctx:, task:, each_index: ctx._task_context.index, data:)
105
+ end
84
106
  end
85
107
  end
86
108
  end
@@ -112,6 +134,7 @@ module JobWorkflow
112
134
  #: (Task) -> void
113
135
  def enqueue_task(task)
114
136
  sub_jobs = context._with_each_value(task).map { |ctx| job.class.from_context(ctx) }
137
+ persist_current_job_context
115
138
  ActiveJob.perform_all_later(sub_jobs)
116
139
  context.job_status.update_task_job_statuses_from_jobs(task_name: task.task_name, jobs: sub_jobs)
117
140
  Instrumentation.notify_task_enqueue(job, task, sub_jobs.size)
@@ -175,5 +198,10 @@ module JobWorkflow
175
198
  context_data_list = QueueAdapter.current.fetch_job_contexts(finished_job_ids)
176
199
  context.output.update_task_outputs_from_contexts(context_data_list, context.workflow)
177
200
  end
201
+
202
+ #: () -> void
203
+ def persist_current_job_context
204
+ QueueAdapter.current.persist_job_context(job)
205
+ end
178
206
  end
179
207
  end
@@ -2,8 +2,9 @@
2
2
 
3
3
  module JobWorkflow
4
4
  class Task
5
+ DEFAULT_EACH = ->(_ctx) { [nil] }
6
+
5
7
  attr_reader :job_name #: String
6
- attr_reader :namespace #: Namespace
7
8
  attr_reader :block #: ^(untyped) -> void
8
9
  attr_reader :each #: ^(Context) -> untyped
9
10
  attr_reader :enqueue #: TaskEnqueue
@@ -16,11 +17,10 @@ module JobWorkflow
16
17
  attr_reader :dependency_wait #: TaskDependencyWait
17
18
  attr_reader :dry_run_config #: DryRunConfig
18
19
 
19
- # rubocop:disable Metrics/ParameterLists, Metrics/MethodLength, Metrics/AbcSize
20
+ # rubocop:disable Metrics/ParameterLists, Metrics/MethodLength
20
21
  #: (
21
22
  # job_name: String,
22
23
  # name: Symbol,
23
- # namespace: Namespace,
24
24
  # block: ^(untyped) -> void,
25
25
  # ?each: ^(Context) -> untyped,
26
26
  # ?enqueue: true | false | ^(Context) -> bool | Hash[Symbol, untyped],
@@ -36,7 +36,6 @@ module JobWorkflow
36
36
  def initialize(
37
37
  job_name:,
38
38
  name:,
39
- namespace:,
40
39
  block:,
41
40
  each: nil,
42
41
  enqueue: nil,
@@ -51,7 +50,6 @@ module JobWorkflow
51
50
  )
52
51
  @job_name = job_name
53
52
  @name = name
54
- @namespace = namespace #: Namespace
55
53
  @block = block
56
54
  @each = each
57
55
  @enqueue = TaskEnqueue.from_primitive_value(enqueue)
@@ -64,11 +62,11 @@ module JobWorkflow
64
62
  @dependency_wait = TaskDependencyWait.from_primitive_value(dependency_wait)
65
63
  @dry_run_config = DryRunConfig.from_primitive_value(dry_run)
66
64
  end
67
- # rubocop:enable Metrics/ParameterLists, Metrics/MethodLength, Metrics/AbcSize
65
+ # rubocop:enable Metrics/ParameterLists, Metrics/MethodLength
68
66
 
69
67
  #: () -> Symbol
70
68
  def task_name
71
- [namespace.full_name.to_s, name.to_s].reject(&:empty?).join(":").to_sym
69
+ name
72
70
  end
73
71
 
74
72
  #: () -> String
@@ -76,6 +74,11 @@ module JobWorkflow
76
74
  "#{job_name}:#{task_name}"
77
75
  end
78
76
 
77
+ #: () -> bool
78
+ def each?
79
+ !each.nil? && !each.equal?(DEFAULT_EACH)
80
+ end
81
+
79
82
  private
80
83
 
81
84
  attr_reader :name #: Symbol
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JobWorkflow
4
- VERSION = "0.3.0" # : String
4
+ VERSION = "0.5.0" # : String
5
5
  end
@@ -2,7 +2,6 @@
2
2
 
3
3
  module JobWorkflow
4
4
  class Workflow
5
- attr_reader :namespace #: Namespace
6
5
  attr_reader :dry_run_config #: DryRunConfig
7
6
 
8
7
  #: () -> void
@@ -10,7 +9,6 @@ module JobWorkflow
10
9
  @task_graph = TaskGraph.new
11
10
  @argument_defs = {} #: Hash[Symbol, ArgumentDef]
12
11
  @hook_registry = HookRegistry.new
13
- @namespace = Namespace.default #: Namespace
14
12
  @schedules = {} #: Hash[Symbol, Schedule]
15
13
  @dry_run_config = DryRunConfig.new
16
14
  end
@@ -20,15 +18,6 @@ module JobWorkflow
20
18
  @dry_run_config = DryRunConfig.from_primitive_value(value)
21
19
  end
22
20
 
23
- #: (Namespace) { () -> void } -> void
24
- def add_namespace(namespace)
25
- original_namespace = @namespace
26
- @namespace = namespace.update_parent(original_namespace)
27
- yield
28
- ensure
29
- @namespace = original_namespace
30
- end
31
-
32
21
  #: (Task) -> void
33
22
  def add_task(task)
34
23
  @task_graph.add(task)
data/lib/job_workflow.rb CHANGED
@@ -23,7 +23,6 @@ require_relative "job_workflow/task_throttle"
23
23
  require_relative "job_workflow/task_enqueue"
24
24
  require_relative "job_workflow/task_dependency_wait"
25
25
  require_relative "job_workflow/semaphore"
26
- require_relative "job_workflow/namespace"
27
26
  require_relative "job_workflow/hook"
28
27
  require_relative "job_workflow/error_hook"
29
28
  require_relative "job_workflow/hook_registry"
@@ -46,6 +45,9 @@ require_relative "job_workflow/task_output"
46
45
  require_relative "job_workflow/output"
47
46
  require_relative "job_workflow/queue"
48
47
  require_relative "job_workflow/auto_scaling"
48
+ # :nocov:
49
+ require_relative "job_workflow/railtie" if defined?(Rails::Railtie)
50
+ # :nocov:
49
51
 
50
52
  module JobWorkflow
51
53
  class Error < StandardError; end
@@ -54,6 +56,5 @@ module JobWorkflow
54
56
 
55
57
  Instrumentation::LogSubscriber.attach!
56
58
 
57
- ActiveSupport.on_load(:solid_queue) { QueueAdapter.current.initialize_adapter! }
58
59
  QueueAdapter.current.initialize_adapter!
59
60
  end
@@ -6,7 +6,7 @@ gems:
6
6
  source:
7
7
  type: git
8
8
  name: ruby/gem_rbs_collection
9
- revision: 9bf2eebb1c54b5d6f23f2acb65d4c36f195b4783
9
+ revision: 81fa8bd0617286078617a62b6a3229cebfd4af23
10
10
  remote: https://github.com/ruby/gem_rbs_collection.git
11
11
  repo_dir: gems
12
12
  - name: activerecord
@@ -14,7 +14,7 @@ gems:
14
14
  source:
15
15
  type: git
16
16
  name: ruby/gem_rbs_collection
17
- revision: 9bf2eebb1c54b5d6f23f2acb65d4c36f195b4783
17
+ revision: 81fa8bd0617286078617a62b6a3229cebfd4af23
18
18
  remote: https://github.com/ruby/gem_rbs_collection.git
19
19
  repo_dir: gems
20
20
  - name: activesupport
@@ -22,7 +22,7 @@ gems:
22
22
  source:
23
23
  type: git
24
24
  name: ruby/gem_rbs_collection
25
- revision: 9bf2eebb1c54b5d6f23f2acb65d4c36f195b4783
25
+ revision: 81fa8bd0617286078617a62b6a3229cebfd4af23
26
26
  remote: https://github.com/ruby/gem_rbs_collection.git
27
27
  repo_dir: gems
28
28
  - name: base64
@@ -34,7 +34,7 @@ gems:
34
34
  source:
35
35
  type: git
36
36
  name: ruby/gem_rbs_collection
37
- revision: 9bf2eebb1c54b5d6f23f2acb65d4c36f195b4783
37
+ revision: 81fa8bd0617286078617a62b6a3229cebfd4af23
38
38
  remote: https://github.com/ruby/gem_rbs_collection.git
39
39
  repo_dir: gems
40
40
  - name: concurrent-ruby
@@ -42,7 +42,7 @@ gems:
42
42
  source:
43
43
  type: git
44
44
  name: ruby/gem_rbs_collection
45
- revision: 9bf2eebb1c54b5d6f23f2acb65d4c36f195b4783
45
+ revision: 81fa8bd0617286078617a62b6a3229cebfd4af23
46
46
  remote: https://github.com/ruby/gem_rbs_collection.git
47
47
  repo_dir: gems
48
48
  - name: connection_pool
@@ -50,7 +50,7 @@ gems:
50
50
  source:
51
51
  type: git
52
52
  name: ruby/gem_rbs_collection
53
- revision: 9bf2eebb1c54b5d6f23f2acb65d4c36f195b4783
53
+ revision: 81fa8bd0617286078617a62b6a3229cebfd4af23
54
54
  remote: https://github.com/ruby/gem_rbs_collection.git
55
55
  repo_dir: gems
56
56
  - name: date
@@ -74,7 +74,7 @@ gems:
74
74
  source:
75
75
  type: git
76
76
  name: ruby/gem_rbs_collection
77
- revision: 9bf2eebb1c54b5d6f23f2acb65d4c36f195b4783
77
+ revision: 81fa8bd0617286078617a62b6a3229cebfd4af23
78
78
  remote: https://github.com/ruby/gem_rbs_collection.git
79
79
  repo_dir: gems
80
80
  - name: i18n
@@ -82,7 +82,7 @@ gems:
82
82
  source:
83
83
  type: git
84
84
  name: ruby/gem_rbs_collection
85
- revision: 9bf2eebb1c54b5d6f23f2acb65d4c36f195b4783
85
+ revision: 81fa8bd0617286078617a62b6a3229cebfd4af23
86
86
  remote: https://github.com/ruby/gem_rbs_collection.git
87
87
  repo_dir: gems
88
88
  - name: json
@@ -98,7 +98,7 @@ gems:
98
98
  source:
99
99
  type: git
100
100
  name: ruby/gem_rbs_collection
101
- revision: 9bf2eebb1c54b5d6f23f2acb65d4c36f195b4783
101
+ revision: 81fa8bd0617286078617a62b6a3229cebfd4af23
102
102
  remote: https://github.com/ruby/gem_rbs_collection.git
103
103
  repo_dir: gems
104
104
  - name: monitor
@@ -138,7 +138,7 @@ gems:
138
138
  source:
139
139
  type: git
140
140
  name: ruby/gem_rbs_collection
141
- revision: 9bf2eebb1c54b5d6f23f2acb65d4c36f195b4783
141
+ revision: 81fa8bd0617286078617a62b6a3229cebfd4af23
142
142
  remote: https://github.com/ruby/gem_rbs_collection.git
143
143
  repo_dir: gems
144
144
  - name: stringio
@@ -162,7 +162,7 @@ gems:
162
162
  source:
163
163
  type: git
164
164
  name: ruby/gem_rbs_collection
165
- revision: 9bf2eebb1c54b5d6f23f2acb65d4c36f195b4783
165
+ revision: 81fa8bd0617286078617a62b6a3229cebfd4af23
166
166
  remote: https://github.com/ruby/gem_rbs_collection.git
167
167
  repo_dir: gems
168
168
  - name: uri
@@ -3,6 +3,8 @@
3
3
  module JobWorkflow
4
4
  class Context
5
5
  # rubocop:disable Metrics/ClassLength
6
+ EACH_TASK_CURSOR_MARKER: ::String
7
+
6
8
  attr_reader workflow: Workflow
7
9
 
8
10
  attr_reader arguments: Arguments
@@ -33,6 +35,15 @@ module JobWorkflow
33
35
  # : (Hash[Symbol, untyped]) -> Context
34
36
  def _update_arguments: (Hash[Symbol, untyped]) -> Context
35
37
 
38
+ # : () -> untyped
39
+ def cursor: () -> untyped
40
+
41
+ # : (untyped) -> void
42
+ def set_cursor!: (untyped) -> void
43
+
44
+ # : () -> void
45
+ def checkpoint!: () -> void
46
+
36
47
  # : (DSL) -> void
37
48
  def _job=: (DSL) -> void
38
49
 
@@ -48,8 +59,8 @@ module JobWorkflow
48
59
  # : () -> String?
49
60
  def concurrency_key: () -> String?
50
61
 
51
- # : (Task) -> Enumerator[Context]
52
- def _with_each_value: (Task) -> Enumerator[Context]
62
+ # : (Task, ?start_index: Integer?) -> Enumerator[Context]
63
+ def _with_each_value: (Task, ?start_index: Integer?) -> Enumerator[Context]
53
64
 
54
65
  # : () { () -> void } -> void
55
66
  def _with_task_throttle: () { () -> void } -> void
@@ -93,6 +104,9 @@ module JobWorkflow
93
104
  # : () -> TaskContext
94
105
  def _task_context: () -> TaskContext
95
106
 
107
+ # : (ActiveJob::Continuation::Step, ?cursor: untyped) { () -> void } -> void
108
+ def _with_current_step: (ActiveJob::Continuation::Step, ?cursor: untyped) { () -> void } -> void
109
+
96
110
  # : (TaskOutput) -> void
97
111
  def _add_task_output: (TaskOutput) -> void
98
112
 
@@ -119,23 +133,33 @@ module JobWorkflow
119
133
 
120
134
  attr_accessor skip_in_dry_run_index: Integer
121
135
 
136
+ attr_accessor current_step: ActiveJob::Continuation::Step?
137
+
138
+ attr_accessor current_cursor: untyped
139
+
122
140
  # : () -> String
123
141
  def parent_job_id: () -> String
124
142
 
143
+ # : () -> bool
144
+ def each_task?: () -> bool
145
+
146
+ # : (untyped) -> untyped
147
+ def build_step_cursor: (untyped) -> untyped
148
+
125
149
  # : () -> Hash[String, untyped]
126
150
  def serialize_for_job: () -> Hash[String, untyped]
127
151
 
128
152
  # : () -> Hash[String, untyped]
129
153
  def serialize_for_sub_job: () -> Hash[String, untyped]
130
154
 
131
- # : (Task, Enumerator::Yielder) -> void
132
- def with_task_context: (Task, Enumerator::Yielder) -> void
155
+ # : (Task, Enumerator::Yielder, ?start_index: Integer?) -> void
156
+ def with_task_context: (Task, Enumerator::Yielder, ?start_index: Integer?) -> void
133
157
 
134
158
  # : (Task) -> void
135
159
  def reset_task_context_if_task_changed: (Task) -> void
136
160
 
137
- # : (Task) { (untyped, Integer) -> void } -> void
138
- def with_each_index_and_value: (Task) { (untyped, Integer) -> void } -> void
161
+ # : (Task, ?start_index: Integer?) { (untyped, Integer) -> void } -> void
162
+ def with_each_index_and_value: (Task, ?start_index: Integer?) { (untyped, Integer) -> void } -> void
139
163
 
140
164
  # : () -> void
141
165
  def clear_after_each_index_and_value: () -> void
@@ -62,9 +62,6 @@ module JobWorkflow
62
62
  # : (Symbol argument_name, String type, ?default: untyped) -> void
63
63
  def argument: (Symbol argument_name, String type, ?default: untyped) -> void
64
64
 
65
- # : (Symbol) { () -> void } -> void
66
- def namespace: (Symbol) { () -> void } -> void
67
-
68
65
  # rubocop:disable Metrics/ParameterLists
69
66
  #
70
67
  # : (
@@ -139,11 +136,6 @@ module JobWorkflow
139
136
  # ?description: String?
140
137
  # ) -> void
141
138
  def schedule: (String expression, ?key: (String | Symbol)?, ?queue: String?, ?priority: Integer?, ?args: Hash[Symbol, untyped], ?description: String?) -> void
142
-
143
- private
144
-
145
- # : () -> void
146
- def validate_namespace!: () -> void
147
139
  end
148
140
  end
149
141
  end
@@ -0,0 +1,6 @@
1
+ # Generated from lib/job_workflow/railtie.rb with RBS::Inline
2
+
3
+ module JobWorkflow
4
+ class Railtie < Rails::Railtie
5
+ end
6
+ end
@@ -30,11 +30,14 @@ module JobWorkflow
30
30
  # : (Task) -> bool
31
31
  def skip_task?: (Task) -> bool
32
32
 
33
- # : (Task) -> void
34
- def run_task: (Task) -> void
33
+ # : (Task, step: ActiveJob::Continuation::Step) -> void
34
+ def run_task: (Task, step: ActiveJob::Continuation::Step) -> void
35
+
36
+ # : (Task, untyped) -> [Integer?, untyped]
37
+ def decode_task_cursor: (Task, untyped) -> [ Integer?, untyped ]
35
38
 
36
- # : (Task, Context) -> void
37
- def run_each_task: (Task, Context) -> void
39
+ # : (Task, Context, step: ActiveJob::Continuation::Step, ?cursor: untyped) -> void
40
+ def run_each_task: (Task, Context, step: ActiveJob::Continuation::Step, ?cursor: untyped) -> void
38
41
 
39
42
  # : (Task, Context) { () -> void } -> void
40
43
  def run_hooks: (Task, Context) { () -> void } -> void
@@ -62,5 +65,8 @@ module JobWorkflow
62
65
 
63
66
  # : (Task) -> void
64
67
  def update_task_outputs: (Task) -> void
68
+
69
+ # : () -> void
70
+ def persist_current_job_context: () -> void
65
71
  end
66
72
  end
@@ -2,9 +2,9 @@
2
2
 
3
3
  module JobWorkflow
4
4
  class Task
5
- attr_reader job_name: String
5
+ DEFAULT_EACH: untyped
6
6
 
7
- attr_reader namespace: Namespace
7
+ attr_reader job_name: String
8
8
 
9
9
  attr_reader block: ^(untyped) -> void
10
10
 
@@ -28,11 +28,10 @@ module JobWorkflow
28
28
 
29
29
  attr_reader dry_run_config: DryRunConfig
30
30
 
31
- # rubocop:disable Metrics/ParameterLists, Metrics/MethodLength, Metrics/AbcSize
31
+ # rubocop:disable Metrics/ParameterLists, Metrics/MethodLength
32
32
  # : (
33
33
  # job_name: String,
34
34
  # name: Symbol,
35
- # namespace: Namespace,
36
35
  # block: ^(untyped) -> void,
37
36
  # ?each: ^(Context) -> untyped,
38
37
  # ?enqueue: true | false | ^(Context) -> bool | Hash[Symbol, untyped],
@@ -45,7 +44,7 @@ module JobWorkflow
45
44
  # ?dependency_wait: Hash[Symbol, untyped],
46
45
  # ?dry_run: bool | ^(Context) -> bool
47
46
  # ) -> void
48
- def initialize: (job_name: String, name: Symbol, namespace: Namespace, block: ^(untyped) -> void, condition: ^(Context) -> bool, ?each: ^(Context) -> untyped, ?enqueue: true | false | ^(Context) -> bool | Hash[Symbol, untyped], ?output: Hash[Symbol, String], ?depends_on: Array[Symbol], ?task_retry: Integer | Hash[Symbol, untyped], ?throttle: Integer | Hash[Symbol, untyped], ?timeout: Numeric?, ?dependency_wait: Hash[Symbol, untyped], ?dry_run: bool | ^(Context) -> bool) -> void
47
+ def initialize: (job_name: String, name: Symbol, block: ^(untyped) -> void, condition: ^(Context) -> bool, ?each: ^(Context) -> untyped, ?enqueue: true | false | ^(Context) -> bool | Hash[Symbol, untyped], ?output: Hash[Symbol, String], ?depends_on: Array[Symbol], ?task_retry: Integer | Hash[Symbol, untyped], ?throttle: Integer | Hash[Symbol, untyped], ?timeout: Numeric?, ?dependency_wait: Hash[Symbol, untyped], ?dry_run: bool | ^(Context) -> bool) -> void
49
48
 
50
49
  # : () -> Symbol
51
50
  def task_name: () -> Symbol
@@ -53,6 +52,9 @@ module JobWorkflow
53
52
  # : () -> String
54
53
  def throttle_prefix_key: () -> String
55
54
 
55
+ # : () -> bool
56
+ def each?: () -> bool
57
+
56
58
  private
57
59
 
58
60
  attr_reader name: Symbol
@@ -2,8 +2,6 @@
2
2
 
3
3
  module JobWorkflow
4
4
  class Workflow
5
- attr_reader namespace: Namespace
6
-
7
5
  attr_reader dry_run_config: DryRunConfig
8
6
 
9
7
  # : () -> void
@@ -12,9 +10,6 @@ module JobWorkflow
12
10
  # : (bool | ^(Context) -> bool) -> void
13
11
  def dry_run_config=: (bool | ^(Context) -> bool) -> void
14
12
 
15
- # : (Namespace) { () -> void } -> void
16
- def add_namespace: (Namespace) { () -> void } -> void
17
-
18
13
  # : (Task) -> void
19
14
  def add_task: (Task) -> void
20
15
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: job-workflow
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - shoma07
@@ -49,7 +49,6 @@ files:
49
49
  - guides/GETTING_STARTED.md
50
50
  - guides/INSTRUMENTATION.md
51
51
  - guides/LIFECYCLE_HOOKS.md
52
- - guides/NAMESPACES.md
53
52
  - guides/OPENTELEMETRY_INTEGRATION.md
54
53
  - guides/PARALLEL_PROCESSING.md
55
54
  - guides/PRODUCTION_DEPLOYMENT.md
@@ -84,7 +83,6 @@ files:
84
83
  - lib/job_workflow/instrumentation/opentelemetry_subscriber.rb
85
84
  - lib/job_workflow/job_status.rb
86
85
  - lib/job_workflow/logger.rb
87
- - lib/job_workflow/namespace.rb
88
86
  - lib/job_workflow/output.rb
89
87
  - lib/job_workflow/output_def.rb
90
88
  - lib/job_workflow/queue.rb
@@ -92,6 +90,7 @@ files:
92
90
  - lib/job_workflow/queue_adapters/abstract.rb
93
91
  - lib/job_workflow/queue_adapters/null_adapter.rb
94
92
  - lib/job_workflow/queue_adapters/solid_queue_adapter.rb
93
+ - lib/job_workflow/railtie.rb
95
94
  - lib/job_workflow/runner.rb
96
95
  - lib/job_workflow/schedule.rb
97
96
  - lib/job_workflow/semaphore.rb
@@ -136,7 +135,6 @@ files:
136
135
  - sig/generated/job_workflow/instrumentation/opentelemetry_subscriber.rbs
137
136
  - sig/generated/job_workflow/job_status.rbs
138
137
  - sig/generated/job_workflow/logger.rbs
139
- - sig/generated/job_workflow/namespace.rbs
140
138
  - sig/generated/job_workflow/output.rbs
141
139
  - sig/generated/job_workflow/output_def.rbs
142
140
  - sig/generated/job_workflow/queue.rbs
@@ -144,6 +142,7 @@ files:
144
142
  - sig/generated/job_workflow/queue_adapters/abstract.rbs
145
143
  - sig/generated/job_workflow/queue_adapters/null_adapter.rbs
146
144
  - sig/generated/job_workflow/queue_adapters/solid_queue_adapter.rbs
145
+ - sig/generated/job_workflow/railtie.rbs
147
146
  - sig/generated/job_workflow/runner.rbs
148
147
  - sig/generated/job_workflow/schedule.rbs
149
148
  - sig/generated/job_workflow/semaphore.rbs
data/guides/NAMESPACES.md DELETED
@@ -1,75 +0,0 @@
1
- # Namespaces
2
-
3
- Logically grouping tasks improves readability and maintainability of complex workflows. JobWorkflow provides namespace functionality.
4
-
5
- ## Basic Namespaces
6
-
7
- ### namespace DSL
8
-
9
- Group related tasks.
10
-
11
- ```ruby
12
- class ECommerceOrderJob < ApplicationJob
13
- include JobWorkflow::DSL
14
-
15
- argument :order, "Order"
16
-
17
- # Payment-related tasks
18
- namespace :payment do
19
- task :validate do |ctx|
20
- order = ctx.arguments.order
21
- PaymentValidator.validate(order)
22
- end
23
-
24
- task :charge, depends_on: [:"payment:validate"], output: { payment_result: "Hash" } do |ctx|
25
- order = ctx.arguments.order
26
- { payment_result: PaymentProcessor.charge(order) }
27
- end
28
-
29
- task :send_receipt, depends_on: [:"payment:charge"] do |ctx|
30
- order = ctx.arguments.order
31
- payment_result = ctx.output[:"payment:charge"].first.payment_result
32
- ReceiptMailer.send(order, payment_result)
33
- end
34
- end
35
-
36
- # Inventory-related tasks
37
- namespace :inventory do
38
- task :check_availability do |ctx|
39
- order = ctx.arguments.order
40
- InventoryService.check(order.items)
41
- end
42
-
43
- task :reserve, depends_on: [:"inventory:check_availability"], output: { reserved: "bool" } do |ctx|
44
- order = ctx.arguments.order
45
- { reserved: InventoryService.reserve(order.items) }
46
- end
47
- end
48
-
49
- # Shipping-related tasks
50
- namespace :shipping do
51
- task :calculate_cost, output: { shipping_cost: "Float" } do |ctx|
52
- order = ctx.arguments.order
53
- { shipping_cost: ShippingCalculator.calculate(order) }
54
- end
55
-
56
- task :create_label, depends_on: [:"shipping:calculate_cost"], output: { shipping_label: "String" } do |ctx|
57
- order = ctx.arguments.order
58
- { shipping_label: ShippingService.create_label(order) }
59
- end
60
- end
61
- end
62
- ```
63
-
64
- Tasks in namespaces are identified as `:namespace:task_name` at runtime:
65
-
66
- ```ruby
67
- # Executed tasks:
68
- # - :payment:validate
69
- # - :payment:charge
70
- # - :payment:send_receipt
71
- # - :inventory:check_availability
72
- # - :inventory:reserve
73
- # - :shipping:calculate_cost
74
- # - :shipping:create_label
75
- ```
@@ -1,36 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JobWorkflow
4
- class Namespace
5
- attr_reader :name #: Symbol
6
- attr_reader :parent #: Namespace?
7
-
8
- class << self
9
- #: () -> Namespace
10
- def default
11
- new(name: :"")
12
- end
13
- end
14
-
15
- #: (name: Symbol, ?parent: Namespace?) -> void
16
- def initialize(name:, parent: nil)
17
- @name = name #: Symbol
18
- @parent = parent #: Namespace?
19
- end
20
-
21
- #: () -> bool
22
- def default?
23
- name.empty?
24
- end
25
-
26
- #: (Namespace) -> Namespace
27
- def update_parent(parent)
28
- self.class.new(name:, parent:)
29
- end
30
-
31
- #: () -> Symbol
32
- def full_name
33
- [parent&.full_name, name.to_s].compact.reject(&:empty?).join(":").to_sym
34
- end
35
- end
36
- end
@@ -1,24 +0,0 @@
1
- # Generated from lib/job_workflow/namespace.rb with RBS::Inline
2
-
3
- module JobWorkflow
4
- class Namespace
5
- attr_reader name: Symbol
6
-
7
- attr_reader parent: Namespace?
8
-
9
- # : () -> Namespace
10
- def self.default: () -> Namespace
11
-
12
- # : (name: Symbol, ?parent: Namespace?) -> void
13
- def initialize: (name: Symbol, ?parent: Namespace?) -> void
14
-
15
- # : () -> bool
16
- def default?: () -> bool
17
-
18
- # : (Namespace) -> Namespace
19
- def update_parent: (Namespace) -> Namespace
20
-
21
- # : () -> Symbol
22
- def full_name: () -> Symbol
23
- end
24
- end