cmdx 1.21.0 → 2.0.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 +4 -4
- data/CHANGELOG.md +118 -1
- data/README.md +37 -24
- data/lib/cmdx/.DS_Store +0 -0
- data/lib/cmdx/callbacks.rb +179 -0
- data/lib/cmdx/chain.rb +78 -175
- data/lib/cmdx/coercions/array.rb +19 -33
- data/lib/cmdx/coercions/big_decimal.rb +12 -29
- data/lib/cmdx/coercions/boolean.rb +25 -45
- data/lib/cmdx/coercions/coerce.rb +32 -0
- data/lib/cmdx/coercions/complex.rb +12 -27
- data/lib/cmdx/coercions/date.rb +29 -33
- data/lib/cmdx/coercions/date_time.rb +29 -33
- data/lib/cmdx/coercions/float.rb +8 -29
- data/lib/cmdx/coercions/hash.rb +17 -43
- data/lib/cmdx/coercions/integer.rb +8 -32
- data/lib/cmdx/coercions/rational.rb +12 -33
- data/lib/cmdx/coercions/string.rb +6 -24
- data/lib/cmdx/coercions/symbol.rb +12 -26
- data/lib/cmdx/coercions/time.rb +31 -35
- data/lib/cmdx/coercions.rb +174 -0
- data/lib/cmdx/configuration.rb +45 -237
- data/lib/cmdx/context.rb +264 -243
- data/lib/cmdx/deprecation.rb +67 -0
- data/lib/cmdx/deprecators/error.rb +22 -0
- data/lib/cmdx/deprecators/log.rb +22 -0
- data/lib/cmdx/deprecators/warn.rb +21 -0
- data/lib/cmdx/deprecators.rb +101 -0
- data/lib/cmdx/errors.rb +145 -79
- data/lib/cmdx/executors/fiber.rb +42 -0
- data/lib/cmdx/executors/thread.rb +36 -0
- data/lib/cmdx/executors.rb +95 -0
- data/lib/cmdx/fault.rb +85 -78
- data/lib/cmdx/i18n_proxy.rb +104 -0
- data/lib/cmdx/input.rb +294 -0
- data/lib/cmdx/inputs.rb +218 -0
- data/lib/cmdx/log_formatters/json.rb +9 -20
- data/lib/cmdx/log_formatters/key_value.rb +10 -21
- data/lib/cmdx/log_formatters/line.rb +7 -19
- data/lib/cmdx/log_formatters/logstash.rb +8 -21
- data/lib/cmdx/log_formatters/raw.rb +8 -20
- data/lib/cmdx/logger_proxy.rb +30 -0
- data/lib/cmdx/mergers/deep_merge.rb +23 -0
- data/lib/cmdx/mergers/last_write_wins.rb +23 -0
- data/lib/cmdx/mergers/no_merge.rb +20 -0
- data/lib/cmdx/mergers.rb +95 -0
- data/lib/cmdx/middlewares.rb +128 -0
- data/lib/cmdx/output.rb +115 -0
- data/lib/cmdx/outputs.rb +66 -0
- data/lib/cmdx/pipeline.rb +144 -131
- data/lib/cmdx/railtie.rb +10 -36
- data/lib/cmdx/result.rb +247 -524
- data/lib/cmdx/retriers/bounded_random.rb +24 -0
- data/lib/cmdx/retriers/decorrelated_jitter.rb +28 -0
- data/lib/cmdx/retriers/exponential.rb +23 -0
- data/lib/cmdx/retriers/fibonacci.rb +39 -0
- data/lib/cmdx/retriers/full_random.rb +23 -0
- data/lib/cmdx/retriers/half_random.rb +24 -0
- data/lib/cmdx/retriers/linear.rb +23 -0
- data/lib/cmdx/retriers.rb +106 -0
- data/lib/cmdx/retry.rb +117 -138
- data/lib/cmdx/runtime.rb +251 -0
- data/lib/cmdx/settings.rb +68 -200
- data/lib/cmdx/signal.rb +165 -0
- data/lib/cmdx/task.rb +443 -343
- data/lib/cmdx/telemetry.rb +108 -0
- data/lib/cmdx/util.rb +73 -0
- data/lib/cmdx/validators/absence.rb +10 -39
- data/lib/cmdx/validators/exclusion.rb +33 -52
- data/lib/cmdx/validators/format.rb +19 -49
- data/lib/cmdx/validators/inclusion.rb +33 -54
- data/lib/cmdx/validators/length.rb +125 -127
- data/lib/cmdx/validators/numeric.rb +123 -123
- data/lib/cmdx/validators/presence.rb +10 -39
- data/lib/cmdx/validators/validate.rb +31 -0
- data/lib/cmdx/validators.rb +161 -0
- data/lib/cmdx/version.rb +2 -4
- data/lib/cmdx/workflow.rb +71 -96
- data/lib/cmdx.rb +111 -42
- data/lib/generators/cmdx/install_generator.rb +7 -17
- data/lib/generators/cmdx/task_generator.rb +12 -29
- data/lib/generators/cmdx/templates/install.rb +120 -48
- data/lib/generators/cmdx/templates/task.rb.tt +1 -1
- data/lib/generators/cmdx/templates/workflow.rb.tt +1 -2
- data/lib/generators/cmdx/workflow_generator.rb +12 -29
- data/lib/locales/en.yml +8 -7
- data/mkdocs.yml +25 -23
- metadata +39 -138
- data/lib/cmdx/attribute.rb +0 -440
- data/lib/cmdx/attribute_registry.rb +0 -185
- data/lib/cmdx/attribute_value.rb +0 -252
- data/lib/cmdx/callback_registry.rb +0 -169
- data/lib/cmdx/coercion_registry.rb +0 -138
- data/lib/cmdx/deprecator.rb +0 -77
- data/lib/cmdx/exception.rb +0 -46
- data/lib/cmdx/executor.rb +0 -378
- data/lib/cmdx/identifier.rb +0 -30
- data/lib/cmdx/locale.rb +0 -78
- data/lib/cmdx/middleware_registry.rb +0 -148
- data/lib/cmdx/middlewares/correlate.rb +0 -140
- data/lib/cmdx/middlewares/runtime.rb +0 -77
- data/lib/cmdx/middlewares/timeout.rb +0 -78
- data/lib/cmdx/parallelizer.rb +0 -100
- data/lib/cmdx/utils/call.rb +0 -53
- data/lib/cmdx/utils/condition.rb +0 -71
- data/lib/cmdx/utils/format.rb +0 -82
- data/lib/cmdx/utils/normalize.rb +0 -52
- data/lib/cmdx/utils/wrap.rb +0 -38
- data/lib/cmdx/validator_registry.rb +0 -143
- data/lib/generators/cmdx/locale_generator.rb +0 -39
- data/lib/locales/af.yml +0 -55
- data/lib/locales/ar.yml +0 -55
- data/lib/locales/az.yml +0 -55
- data/lib/locales/be.yml +0 -55
- data/lib/locales/bg.yml +0 -55
- data/lib/locales/bn.yml +0 -55
- data/lib/locales/bs.yml +0 -55
- data/lib/locales/ca.yml +0 -55
- data/lib/locales/cnr.yml +0 -55
- data/lib/locales/cs.yml +0 -55
- data/lib/locales/cy.yml +0 -55
- data/lib/locales/da.yml +0 -55
- data/lib/locales/de.yml +0 -55
- data/lib/locales/dz.yml +0 -55
- data/lib/locales/el.yml +0 -55
- data/lib/locales/eo.yml +0 -55
- data/lib/locales/es.yml +0 -55
- data/lib/locales/et.yml +0 -55
- data/lib/locales/eu.yml +0 -55
- data/lib/locales/fa.yml +0 -55
- data/lib/locales/fi.yml +0 -55
- data/lib/locales/fr.yml +0 -55
- data/lib/locales/fy.yml +0 -55
- data/lib/locales/gd.yml +0 -55
- data/lib/locales/gl.yml +0 -55
- data/lib/locales/he.yml +0 -55
- data/lib/locales/hi.yml +0 -55
- data/lib/locales/hr.yml +0 -55
- data/lib/locales/hu.yml +0 -55
- data/lib/locales/hy.yml +0 -55
- data/lib/locales/id.yml +0 -55
- data/lib/locales/is.yml +0 -55
- data/lib/locales/it.yml +0 -55
- data/lib/locales/ja.yml +0 -55
- data/lib/locales/ka.yml +0 -55
- data/lib/locales/kk.yml +0 -55
- data/lib/locales/km.yml +0 -55
- data/lib/locales/kn.yml +0 -55
- data/lib/locales/ko.yml +0 -55
- data/lib/locales/lb.yml +0 -55
- data/lib/locales/lo.yml +0 -55
- data/lib/locales/lt.yml +0 -55
- data/lib/locales/lv.yml +0 -55
- data/lib/locales/mg.yml +0 -55
- data/lib/locales/mk.yml +0 -55
- data/lib/locales/ml.yml +0 -55
- data/lib/locales/mn.yml +0 -55
- data/lib/locales/mr-IN.yml +0 -55
- data/lib/locales/ms.yml +0 -55
- data/lib/locales/nb.yml +0 -55
- data/lib/locales/ne.yml +0 -55
- data/lib/locales/nl.yml +0 -55
- data/lib/locales/nn.yml +0 -55
- data/lib/locales/oc.yml +0 -55
- data/lib/locales/or.yml +0 -55
- data/lib/locales/pa.yml +0 -55
- data/lib/locales/pl.yml +0 -55
- data/lib/locales/pt.yml +0 -55
- data/lib/locales/rm.yml +0 -55
- data/lib/locales/ro.yml +0 -55
- data/lib/locales/ru.yml +0 -55
- data/lib/locales/sc.yml +0 -55
- data/lib/locales/sk.yml +0 -55
- data/lib/locales/sl.yml +0 -55
- data/lib/locales/sq.yml +0 -55
- data/lib/locales/sr.yml +0 -55
- data/lib/locales/st.yml +0 -55
- data/lib/locales/sv.yml +0 -55
- data/lib/locales/sw.yml +0 -55
- data/lib/locales/ta.yml +0 -55
- data/lib/locales/te.yml +0 -55
- data/lib/locales/th.yml +0 -55
- data/lib/locales/tl.yml +0 -55
- data/lib/locales/tr.yml +0 -55
- data/lib/locales/tt.yml +0 -55
- data/lib/locales/ug.yml +0 -55
- data/lib/locales/uk.yml +0 -55
- data/lib/locales/ur.yml +0 -55
- data/lib/locales/uz.yml +0 -55
- data/lib/locales/vi.yml +0 -55
- data/lib/locales/wo.yml +0 -55
- data/lib/locales/zh-CN.yml +0 -55
- data/lib/locales/zh-HK.yml +0 -55
- data/lib/locales/zh-TW.yml +0 -55
- data/lib/locales/zh-YUE.yml +0 -55
|
@@ -1,32 +1,22 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Cmdx
|
|
4
|
-
#
|
|
4
|
+
# Rails generator that scaffolds the CMDx initializer at
|
|
5
|
+
# `config/initializers/cmdx.rb`. The initializer template seeds global
|
|
6
|
+
# {CMDx.configuration} defaults (middlewares, callbacks, coercions,
|
|
7
|
+
# validators, telemetry) that all tasks inherit from.
|
|
5
8
|
#
|
|
6
|
-
#
|
|
7
|
-
# CMDx settings for the Rails application. It copies a pre-configured
|
|
8
|
-
# initializer template to the standard Rails initializers directory.
|
|
9
|
+
# Invoked via `rails generate cmdx:install`.
|
|
9
10
|
class InstallGenerator < Rails::Generators::Base
|
|
10
11
|
|
|
11
12
|
source_root File.expand_path("templates", __dir__)
|
|
12
13
|
|
|
13
14
|
desc "Creates CMDx initializer with global configuration settings"
|
|
14
15
|
|
|
15
|
-
# Copies the
|
|
16
|
-
#
|
|
17
|
-
# Creates a new initializer file at `config/initializers/cmdx.rb` containing
|
|
18
|
-
# the default CMDx configuration settings. This allows applications to
|
|
19
|
-
# customize global CMDx behavior through the standard Rails configuration
|
|
20
|
-
# pattern.
|
|
16
|
+
# Copies the initializer template into the host application's
|
|
17
|
+
# `config/initializers` directory.
|
|
21
18
|
#
|
|
22
19
|
# @return [void]
|
|
23
|
-
#
|
|
24
|
-
# @example Basic usage
|
|
25
|
-
# rails generate cmdx:install
|
|
26
|
-
#
|
|
27
|
-
# @example Custom initializer location
|
|
28
|
-
# generator.copy_initializer_file
|
|
29
|
-
# # => Creates config/initializers/cmdx.rb
|
|
30
20
|
def copy_initializer_file
|
|
31
21
|
copy_file("install.rb", "config/initializers/cmdx.rb")
|
|
32
22
|
end
|
|
@@ -1,32 +1,23 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Cmdx
|
|
4
|
-
#
|
|
4
|
+
# Rails generator that scaffolds a new {CMDx::Task} subclass under
|
|
5
|
+
# `app/tasks`, honoring nested module paths supplied through the NAME
|
|
6
|
+
# argument (e.g. `Billing::ChargeCard` writes to
|
|
7
|
+
# `app/tasks/billing/charge_card.rb`).
|
|
5
8
|
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
+
# Invoked via `rails generate cmdx:task NAME`.
|
|
10
|
+
#
|
|
11
|
+
# @see CMDx::Task
|
|
9
12
|
class TaskGenerator < Rails::Generators::NamedBase
|
|
10
13
|
|
|
11
14
|
source_root File.expand_path("templates", __dir__)
|
|
12
15
|
|
|
13
16
|
desc "Creates a task with the given NAME"
|
|
14
17
|
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
# Creates a new task file at `app/tasks/[class_path]/[file_name].rb` using
|
|
18
|
-
# the task template. The file is placed in the standard Rails tasks directory
|
|
19
|
-
# structure, maintaining proper namespacing if the task is nested.
|
|
18
|
+
# Renders `task.rb.tt` into `app/tasks/<class_path>/<file_name>.rb`.
|
|
20
19
|
#
|
|
21
20
|
# @return [void]
|
|
22
|
-
#
|
|
23
|
-
# @example Basic usage
|
|
24
|
-
# rails generate cmdx:task UserRegistration
|
|
25
|
-
# # => Creates app/tasks/user_registration.rb
|
|
26
|
-
#
|
|
27
|
-
# @example Nested task
|
|
28
|
-
# rails generate cmdx:task Admin::UserManagement
|
|
29
|
-
# # => Creates app/tasks/admin/user_management.rb
|
|
30
21
|
def copy_files
|
|
31
22
|
path = File.join("app/tasks", class_path, "#{file_name}.rb")
|
|
32
23
|
template("task.rb.tt", path)
|
|
@@ -34,19 +25,11 @@ module Cmdx
|
|
|
34
25
|
|
|
35
26
|
private
|
|
36
27
|
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
# falls back to CMDx::Task. This allows applications to define their own
|
|
41
|
-
# base task class while maintaining compatibility.
|
|
42
|
-
#
|
|
43
|
-
# @return [Class] The parent class for the generated task
|
|
44
|
-
#
|
|
45
|
-
# @example
|
|
46
|
-
# parent_class_name # => ApplicationTask
|
|
28
|
+
# Selects the parent class for the generated task: prefers the host
|
|
29
|
+
# application's `ApplicationTask` when defined, falling back to
|
|
30
|
+
# {CMDx::Task} otherwise. Consumed by the ERB template via `<%= %>`.
|
|
47
31
|
#
|
|
48
|
-
# @
|
|
49
|
-
# parent_class_name # => CMDx::Task
|
|
32
|
+
# @return [Class] either `ApplicationTask` or {CMDx::Task}
|
|
50
33
|
def parent_class_name
|
|
51
34
|
ApplicationTask
|
|
52
35
|
rescue NameError
|
|
@@ -1,65 +1,137 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
CMDx.configure do |config|
|
|
4
|
-
#
|
|
5
|
-
#
|
|
4
|
+
# ===========================================================================
|
|
5
|
+
# Locale
|
|
6
|
+
# ===========================================================================
|
|
7
|
+
# Fallback locale for built-in messages (validation, coercion, etc.) when
|
|
8
|
+
# the I18n gem is not present. With I18n loaded, CMDx follows `I18n.locale`.
|
|
6
9
|
#
|
|
7
|
-
#
|
|
8
|
-
# If set to an empty array, task will never halt
|
|
9
|
-
config.task_breakpoints = %w[failed]
|
|
10
|
+
# config.default_locale = "en"
|
|
10
11
|
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
12
|
+
# ===========================================================================
|
|
13
|
+
# Strict context
|
|
14
|
+
# ===========================================================================
|
|
15
|
+
# When true, dynamic reads on `context` raise `NoMethodError` for unknown
|
|
16
|
+
# keys instead of returning `nil` (`[]`, `fetch`, `dig`, and `?` predicates
|
|
17
|
+
# stay lenient). Override per-task via `settings(strict_context: true)`.
|
|
14
18
|
#
|
|
15
|
-
#
|
|
16
|
-
# If set to an empty array, workflow will never halt
|
|
17
|
-
config.workflow_breakpoints = %w[failed]
|
|
19
|
+
# config.strict_context = true
|
|
18
20
|
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
+
# ===========================================================================
|
|
22
|
+
# Correlation ID (xid)
|
|
23
|
+
# ===========================================================================
|
|
24
|
+
# Resolves an external correlation id (e.g. Rails `request_id`) once per
|
|
25
|
+
# root execution. The value is stored on the Chain and surfaces on every
|
|
26
|
+
# Result (`result.xid`, `result.to_h[:xid]`) and Telemetry::Event (`event.xid`),
|
|
27
|
+
# so all tasks within the same request can be filtered together in logs.
|
|
21
28
|
#
|
|
22
|
-
#
|
|
23
|
-
# - CMDx::LogFormatters::Json
|
|
24
|
-
# - CMDx::LogFormatters::KeyValue
|
|
25
|
-
# - CMDx::LogFormatters::Line
|
|
26
|
-
# - CMDx::LogFormatters::Logstash
|
|
27
|
-
# - CMDx::LogFormatters::Raw
|
|
28
|
-
config.logger = Logger.new(
|
|
29
|
-
$stdout,
|
|
30
|
-
progname: "cmdx",
|
|
31
|
-
formatter: CMDx::LogFormatters::Line.new,
|
|
32
|
-
level: Logger::INFO
|
|
33
|
-
)
|
|
29
|
+
# config.correlation_id = -> { Current.request_id }
|
|
34
30
|
|
|
35
|
-
#
|
|
36
|
-
#
|
|
31
|
+
# ===========================================================================
|
|
32
|
+
# Logging
|
|
33
|
+
# ===========================================================================
|
|
34
|
+
# In Rails, the Railtie already wires `config.logger = Rails.logger` and a
|
|
35
|
+
# backtrace cleaner — override here only if you need something different.
|
|
36
|
+
#
|
|
37
|
+
# Formatters: Line (default), Json, KeyValue, Logstash, Raw
|
|
37
38
|
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
config.
|
|
39
|
+
# config.backtrace_cleaner = ->(bt) { Rails.backtrace_cleaner.clean(bt) }
|
|
40
|
+
# config.log_exclusions = [:context]
|
|
41
|
+
# config.log_formatter = CMDx::LogFormatters::Line.new
|
|
42
|
+
# config.log_level = Logger::INFO
|
|
43
|
+
# config.logger = Logger.new($stdout, progname: "cmdx")
|
|
41
44
|
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
+
# ===========================================================================
|
|
46
|
+
# Middlewares
|
|
47
|
+
# ===========================================================================
|
|
48
|
+
# Wrap every task's execution. Must respond to `call(task) { ... }`.
|
|
49
|
+
#
|
|
50
|
+
# Example — run each task under the current user's locale:
|
|
51
|
+
#
|
|
52
|
+
# config.middlewares.register(proc do |task, &next_link|
|
|
53
|
+
# locale = Current.user.locale || I18n.default_locale
|
|
54
|
+
# I18n.with_locale(locale) do
|
|
55
|
+
# task.metadata[:locale] = locale
|
|
56
|
+
# next_link.call
|
|
57
|
+
# end
|
|
58
|
+
# end)
|
|
45
59
|
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
#
|
|
60
|
+
# ===========================================================================
|
|
61
|
+
# Callbacks
|
|
62
|
+
# ===========================================================================
|
|
63
|
+
# Events:
|
|
64
|
+
# :before_validation, :before_execution,
|
|
65
|
+
# :on_complete, :on_interrupted,
|
|
66
|
+
# :on_success, :on_skipped, :on_failed,
|
|
67
|
+
# :on_ok, :on_ko
|
|
68
|
+
#
|
|
69
|
+
# config.callbacks.register(:on_failed, proc do |task|
|
|
70
|
+
# Rails.logger.error("[cmdx] #{task.class.name} failed: #{task.metadata[:reason]}")
|
|
71
|
+
# end)
|
|
50
72
|
|
|
51
|
-
#
|
|
52
|
-
#
|
|
53
|
-
#
|
|
73
|
+
# ===========================================================================
|
|
74
|
+
# Telemetry
|
|
75
|
+
# ===========================================================================
|
|
76
|
+
# Events and payloads:
|
|
77
|
+
# :task_started payload: {}
|
|
78
|
+
# :task_deprecated payload: {}
|
|
79
|
+
# :task_retried payload: { attempt: Integer }
|
|
80
|
+
# :task_rolled_back payload: {}
|
|
81
|
+
# :task_executed payload: { result: CMDx::Result }
|
|
82
|
+
#
|
|
83
|
+
# Every event also carries: event.cid, event.xid, event.tid, event.task,
|
|
84
|
+
# event.type, event.root, event.timestamp.
|
|
85
|
+
#
|
|
86
|
+
# config.telemetry.subscribe(:task_executed, proc do |event|
|
|
87
|
+
# StatsD.timing("cmdx.task", event.payload[:result].duration)
|
|
88
|
+
# end)
|
|
89
|
+
|
|
90
|
+
# ===========================================================================
|
|
91
|
+
# Coercions
|
|
92
|
+
# ===========================================================================
|
|
93
|
+
# Register custom type coercions. Callable receives `(value, **options)`.
|
|
94
|
+
#
|
|
95
|
+
# config.coercions.register(:currency, proc do |value, **|
|
|
96
|
+
# BigDecimal(value.to_s.gsub(/[^\d.-]/, ""))
|
|
97
|
+
# end)
|
|
54
98
|
|
|
55
|
-
#
|
|
56
|
-
#
|
|
57
|
-
#
|
|
99
|
+
# ===========================================================================
|
|
100
|
+
# Validators
|
|
101
|
+
# ===========================================================================
|
|
102
|
+
# Register custom validators. Callable receives `(value, options)` and
|
|
103
|
+
# returns a `CMDx::Validators::Failure.new(message)` on failure.
|
|
104
|
+
#
|
|
105
|
+
# config.validators.register(:uuid, proc do |value, _options|
|
|
106
|
+
# unless value.to_s.match?(/\A[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}\z/i)
|
|
107
|
+
# CMDx::Validators::Failure.new("is not a valid UUID")
|
|
108
|
+
# end
|
|
109
|
+
# end)
|
|
110
|
+
|
|
111
|
+
# ===========================================================================
|
|
112
|
+
# Executors
|
|
113
|
+
# ===========================================================================
|
|
114
|
+
# Registered executors drive `:parallel` workflow groups. Built-ins:
|
|
115
|
+
# `:threads` (default), `:fibers`. A callable receives
|
|
116
|
+
# `call(jobs:, concurrency:, on_job:)` and must invoke `on_job.call(job)`
|
|
117
|
+
# for each job, blocking until every job is done.
|
|
118
|
+
#
|
|
119
|
+
# config.executors.register(:ractors, proc do |jobs:, concurrency:, on_job:|
|
|
120
|
+
# jobs.each_slice(concurrency) do |slice|
|
|
121
|
+
# slice.map { |job| Ractor.new(job) { |j| on_job.call(j) } }.each(&:take)
|
|
122
|
+
# end
|
|
123
|
+
# end)
|
|
58
124
|
|
|
59
|
-
#
|
|
125
|
+
# ===========================================================================
|
|
126
|
+
# Mergers
|
|
127
|
+
# ===========================================================================
|
|
128
|
+
# Merge strategies fold successful parallel task contexts back into the
|
|
129
|
+
# workflow context. Built-ins: `:last_write_wins` (default), `:deep_merge`,
|
|
130
|
+
# `:no_merge`. A callable receives `call(workflow_context, result)`.
|
|
60
131
|
#
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
#
|
|
132
|
+
# config.mergers.register(:whitelist, proc do |workflow_context, result|
|
|
133
|
+
# result.context.to_h.slice(:order_id, :total).each do |key, value|
|
|
134
|
+
# workflow_context[key] = value
|
|
135
|
+
# end
|
|
136
|
+
# end)
|
|
65
137
|
end
|
|
@@ -1,32 +1,23 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Cmdx
|
|
4
|
-
#
|
|
4
|
+
# Rails generator that scaffolds a new {CMDx::Workflow} subclass under
|
|
5
|
+
# `app/tasks`, honoring nested module paths supplied through the NAME
|
|
6
|
+
# argument (e.g. `Billing::Checkout` writes to
|
|
7
|
+
# `app/tasks/billing/checkout.rb`).
|
|
5
8
|
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
+
# Invoked via `rails generate cmdx:workflow NAME`.
|
|
10
|
+
#
|
|
11
|
+
# @see CMDx::Workflow
|
|
9
12
|
class WorkflowGenerator < Rails::Generators::NamedBase
|
|
10
13
|
|
|
11
14
|
source_root File.expand_path("templates", __dir__)
|
|
12
15
|
|
|
13
16
|
desc "Creates a workflow with the given NAME"
|
|
14
17
|
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
# Creates a new task file at `app/tasks/[class_path]/[file_name].rb` using
|
|
18
|
-
# the task template. The file is placed in the standard Rails tasks directory
|
|
19
|
-
# structure, maintaining proper namespacing if the task is nested.
|
|
18
|
+
# Renders `workflow.rb.tt` into `app/tasks/<class_path>/<file_name>.rb`.
|
|
20
19
|
#
|
|
21
20
|
# @return [void]
|
|
22
|
-
#
|
|
23
|
-
# @example Basic usage
|
|
24
|
-
# rails generate cmdx:workflow SendNotifications
|
|
25
|
-
# # => Creates app/tasks/send_notifications.rb
|
|
26
|
-
#
|
|
27
|
-
# @example Nested task
|
|
28
|
-
# rails generate cmdx:workflow Admin::SendNotifications
|
|
29
|
-
# # => Creates app/tasks/admin/send_notifications.rb
|
|
30
21
|
def copy_files
|
|
31
22
|
path = File.join("app/tasks", class_path, "#{file_name}.rb")
|
|
32
23
|
template("workflow.rb.tt", path)
|
|
@@ -34,19 +25,11 @@ module Cmdx
|
|
|
34
25
|
|
|
35
26
|
private
|
|
36
27
|
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
# falls back to CMDx::Task. This allows applications to define their own
|
|
41
|
-
# base task class while maintaining compatibility.
|
|
42
|
-
#
|
|
43
|
-
# @return [Class] The parent class for the generated task
|
|
44
|
-
#
|
|
45
|
-
# @example
|
|
46
|
-
# parent_class_name # => ApplicationTask
|
|
28
|
+
# Selects the parent class for the generated workflow: prefers the host
|
|
29
|
+
# application's `ApplicationTask` when defined, falling back to
|
|
30
|
+
# {CMDx::Task} otherwise. Consumed by the ERB template via `<%= %>`.
|
|
47
31
|
#
|
|
48
|
-
# @
|
|
49
|
-
# parent_class_name # => CMDx::Task
|
|
32
|
+
# @return [Class] either `ApplicationTask` or {CMDx::Task}
|
|
50
33
|
def parent_class_name
|
|
51
34
|
ApplicationTask
|
|
52
35
|
rescue NameError
|
data/lib/locales/en.yml
CHANGED
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
en:
|
|
2
2
|
cmdx:
|
|
3
3
|
attributes:
|
|
4
|
-
required: "
|
|
5
|
-
undefined: "delegates to undefined method %{method}"
|
|
4
|
+
required: "is required"
|
|
6
5
|
coercions:
|
|
7
6
|
into_a: "could not coerce into a %{type}"
|
|
8
7
|
into_an: "could not coerce into an %{type}"
|
|
9
8
|
into_any: "could not coerce into one of: %{types}"
|
|
10
|
-
unknown: "unknown %{type} coercion type"
|
|
11
|
-
faults:
|
|
12
|
-
invalid: "Invalid"
|
|
13
|
-
unspecified: "Unspecified"
|
|
14
9
|
reasons:
|
|
15
10
|
unspecified: "Unspecified"
|
|
16
|
-
|
|
11
|
+
outputs:
|
|
17
12
|
missing: "must be set in the context"
|
|
18
13
|
types:
|
|
19
14
|
array: "array"
|
|
@@ -43,6 +38,9 @@ en:
|
|
|
43
38
|
is_not: "length must not be %{is_not}"
|
|
44
39
|
min: "length must be at least %{min}"
|
|
45
40
|
max: "length must be at most %{max}"
|
|
41
|
+
gt: "length must be greater than %{gt}"
|
|
42
|
+
lt: "length must be less than %{lt}"
|
|
43
|
+
nil_value: "must have a length"
|
|
46
44
|
not_within: "length must not be within %{min} and %{max}"
|
|
47
45
|
within: "length must be within %{min} and %{max}"
|
|
48
46
|
numeric:
|
|
@@ -50,6 +48,9 @@ en:
|
|
|
50
48
|
is_not: "must not be %{is_not}"
|
|
51
49
|
min: "must be at least %{min}"
|
|
52
50
|
max: "must be at most %{max}"
|
|
51
|
+
gt: "must be greater than %{gt}"
|
|
52
|
+
lt: "must be less than %{lt}"
|
|
53
|
+
nil_value: "must be numeric"
|
|
53
54
|
not_within: "must not be within %{min} and %{max}"
|
|
54
55
|
within: "must be within %{min} and %{max}"
|
|
55
56
|
presence: "cannot be empty"
|
data/mkdocs.yml
CHANGED
|
@@ -17,8 +17,8 @@ theme:
|
|
|
17
17
|
icon:
|
|
18
18
|
repo: fontawesome/brands/github
|
|
19
19
|
font:
|
|
20
|
-
text:
|
|
21
|
-
code:
|
|
20
|
+
text: DM Sans
|
|
21
|
+
code: DM Mono
|
|
22
22
|
palette:
|
|
23
23
|
# Palette toggle for automatic mode
|
|
24
24
|
- media: "(prefers-color-scheme)"
|
|
@@ -120,20 +120,21 @@ plugins:
|
|
|
120
120
|
- basics/context.md: Context object for data sharing, input/output management, and attribute access
|
|
121
121
|
- basics/chain.md: Execution chain tracking for related tasks within threads
|
|
122
122
|
Interruptions:
|
|
123
|
-
- interruptions/
|
|
124
|
-
- interruptions/faults.md: Fault
|
|
125
|
-
- interruptions/exceptions.md:
|
|
123
|
+
- interruptions/signals.md: Intentional task interruption using skip! and fail! methods
|
|
124
|
+
- interruptions/faults.md: CMDx::Fault raised by execute! with task class, signal, and cleaned backtrace
|
|
125
|
+
- interruptions/exceptions.md: Complete exception hierarchy (Error, DeprecationError, ImplementationError, MiddlewareError, Fault) and execute vs execute! semantics
|
|
126
126
|
Outcomes:
|
|
127
127
|
- outcomes/result.md: Result objects exposing execution state, status, context, and metadata
|
|
128
128
|
- outcomes/states.md: Execution lifecycle states (initialized, executing, complete, interrupted)
|
|
129
129
|
- outcomes/statuses.md: Business outcome statuses (success, skipped, failed) and transitions
|
|
130
|
+
- outcomes/errors.md: Structured per-attribute error container accumulated during input/output verification and exposed on result.errors
|
|
130
131
|
Attributes:
|
|
131
|
-
-
|
|
132
|
-
-
|
|
133
|
-
-
|
|
134
|
-
-
|
|
135
|
-
-
|
|
136
|
-
-
|
|
132
|
+
- inputs/definitions.md: Attribute declarations (required/optional), validation, and type coercion
|
|
133
|
+
- inputs/naming.md: Customizing accessor method names with prefixes and suffixes
|
|
134
|
+
- inputs/coercions.md: Automatic type conversion for inputs (string to integer, date parsing, etc.)
|
|
135
|
+
- inputs/validations.md: Input validation rules (presence, length, format, numeric, inclusion, exclusion)
|
|
136
|
+
- inputs/defaults.md: Default values for optional inputs (static, dynamic, callable)
|
|
137
|
+
- inputs/transformations.md: Value transformation after coercion but before validation
|
|
137
138
|
Features:
|
|
138
139
|
- callbacks.md: Execution lifecycle callbacks (before_execution, on_success, on_failure, etc.)
|
|
139
140
|
- middlewares.md: Cross-cutting concerns wrapping task execution (authentication, caching, timeouts)
|
|
@@ -141,13 +142,13 @@ plugins:
|
|
|
141
142
|
- internationalization.md: Multi-language support for error messages and validations (90+ locales)
|
|
142
143
|
- retries.md: Automatic retry functionality for transient failures with jitter and selective retries
|
|
143
144
|
- deprecation.md: Managing deprecated tasks with logging, warnings, or execution prevention
|
|
144
|
-
-
|
|
145
|
+
- outputs.md: Declaring expected context outputs that must be set after task execution
|
|
145
146
|
- workflows.md: Composing multiple tasks into sequential pipelines with conditional execution
|
|
146
147
|
More:
|
|
147
148
|
- testing.md: Best practices for testing CMDx tasks and workflows with RSpec
|
|
148
|
-
- exceptions.md: Complete exception hierarchy and error type reference
|
|
149
149
|
- tips_and_tricks.md: Best practices, patterns, and techniques for maintainable CMDx applications
|
|
150
150
|
- comparison.md: Comparison with other command/service object frameworks
|
|
151
|
+
- v2-migration.md: Migration guide from CMDx 1.x to 2.0
|
|
151
152
|
nav:
|
|
152
153
|
- Home: index.md
|
|
153
154
|
- Documentation:
|
|
@@ -159,20 +160,21 @@ nav:
|
|
|
159
160
|
- Context: basics/context.md
|
|
160
161
|
- Chain: basics/chain.md
|
|
161
162
|
- Interruptions:
|
|
162
|
-
-
|
|
163
|
+
- Signals: interruptions/signals.md
|
|
163
164
|
- Faults: interruptions/faults.md
|
|
164
165
|
- Exceptions: interruptions/exceptions.md
|
|
165
166
|
- Outcomes:
|
|
166
167
|
- Result: outcomes/result.md
|
|
167
168
|
- States: outcomes/states.md
|
|
168
169
|
- Statuses: outcomes/statuses.md
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
-
|
|
172
|
-
-
|
|
173
|
-
-
|
|
174
|
-
-
|
|
175
|
-
-
|
|
170
|
+
- Errors: outcomes/errors.md
|
|
171
|
+
- Inputs:
|
|
172
|
+
- Definitions: inputs/definitions.md
|
|
173
|
+
- Naming: inputs/naming.md
|
|
174
|
+
- Coercions: inputs/coercions.md
|
|
175
|
+
- Validations: inputs/validations.md
|
|
176
|
+
- Defaults: inputs/defaults.md
|
|
177
|
+
- Transformations: inputs/transformations.md
|
|
176
178
|
- Features:
|
|
177
179
|
- Callbacks: callbacks.md
|
|
178
180
|
- Middlewares: middlewares.md
|
|
@@ -180,13 +182,13 @@ nav:
|
|
|
180
182
|
- Internationalization: internationalization.md
|
|
181
183
|
- Retries: retries.md
|
|
182
184
|
- Deprecation: deprecation.md
|
|
183
|
-
-
|
|
185
|
+
- Outputs: outputs.md
|
|
184
186
|
- Workflows: workflows.md
|
|
185
187
|
- More:
|
|
186
188
|
- Testing: testing.md
|
|
187
|
-
- Exceptions: exceptions.md
|
|
188
189
|
- Tips and Tricks: tips_and_tricks.md
|
|
189
190
|
- Comparison: comparison.md
|
|
191
|
+
- v1 → v2 Migration: v2-migration.md
|
|
190
192
|
- References:
|
|
191
193
|
- API Documentation: https://drexed.github.io/cmdx/api/index.html
|
|
192
194
|
- llms.txt: https://drexed.github.io/cmdx/llms.txt
|