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
data/lib/cmdx/locale.rb
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module CMDx
|
|
4
|
-
# Provides internationalization and localization support for CMDx.
|
|
5
|
-
# Handles translation lookups with fallback to the configured locale's
|
|
6
|
-
# default messages when I18n gem is not available.
|
|
7
|
-
module Locale
|
|
8
|
-
|
|
9
|
-
extend self
|
|
10
|
-
|
|
11
|
-
# Translates a key to the current locale with optional interpolation.
|
|
12
|
-
# Falls back to the configured locale's YAML file translations if
|
|
13
|
-
# I18n gem is unavailable.
|
|
14
|
-
#
|
|
15
|
-
# @param key [String, Symbol] The translation key (supports dot notation)
|
|
16
|
-
# @param options [Hash] Translation options
|
|
17
|
-
# @option options [String] :default Fallback message if translation missing
|
|
18
|
-
# @option options [String] :locale Target locale (when I18n available)
|
|
19
|
-
# @option options [Hash] :scope Translation scope (when I18n available)
|
|
20
|
-
# @option options [Object] :* Any other options passed to I18n.t or string interpolation
|
|
21
|
-
#
|
|
22
|
-
# @return [String] The translated message
|
|
23
|
-
#
|
|
24
|
-
# @raise [ArgumentError] When interpolation fails due to missing keys
|
|
25
|
-
#
|
|
26
|
-
# @example Basic translation
|
|
27
|
-
# Locale.translate("errors.invalid_input")
|
|
28
|
-
# # => "Invalid input provided"
|
|
29
|
-
# @example With interpolation
|
|
30
|
-
# Locale.translate("welcome.message", name: "John")
|
|
31
|
-
# # => "Welcome, John!"
|
|
32
|
-
# @example With fallback
|
|
33
|
-
# Locale.translate("missing.key", default: "Custom fallback message")
|
|
34
|
-
# # => "Custom fallback message"
|
|
35
|
-
#
|
|
36
|
-
# @rbs ((String | Symbol) key, **untyped options) -> String
|
|
37
|
-
def translate(key, **options)
|
|
38
|
-
options[:default] ||= translation_default(key)
|
|
39
|
-
return ::I18n.t(key, **options) if defined?(::I18n)
|
|
40
|
-
|
|
41
|
-
case message = options.delete(:default)
|
|
42
|
-
when String then message % options
|
|
43
|
-
when NilClass then "Translation missing: #{key}"
|
|
44
|
-
else message
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
# @see #translate
|
|
49
|
-
alias t translate
|
|
50
|
-
|
|
51
|
-
private
|
|
52
|
-
|
|
53
|
-
# Resolves and caches the default translation for a key by digging
|
|
54
|
-
# into the configured locale's YAML translations.
|
|
55
|
-
#
|
|
56
|
-
# @param key [String, Symbol] The translation key
|
|
57
|
-
#
|
|
58
|
-
# @return [String, nil] The resolved translation or nil
|
|
59
|
-
#
|
|
60
|
-
# @rbs ((String | Symbol) key) -> String?
|
|
61
|
-
def translation_default(key)
|
|
62
|
-
tkey = "#{CMDx.configuration.default_locale}.#{key}"
|
|
63
|
-
|
|
64
|
-
@translation_defaults ||= {}
|
|
65
|
-
return @translation_defaults[tkey] if @translation_defaults.key?(tkey)
|
|
66
|
-
|
|
67
|
-
@default_translations ||= begin
|
|
68
|
-
path = CMDx.gem_path.join("lib/locales/#{CMDx.configuration.default_locale}.yml")
|
|
69
|
-
raise ArgumentError, "locale file not found: #{path}" unless path.exist?
|
|
70
|
-
|
|
71
|
-
YAML.load_file(path).freeze
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
@translation_defaults[tkey] = @default_translations.dig(*tkey.split("."))
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
end
|
|
78
|
-
end
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module CMDx
|
|
4
|
-
# Registry for managing middleware components in a task execution chain.
|
|
5
|
-
#
|
|
6
|
-
# The MiddlewareRegistry maintains an ordered list of middleware components
|
|
7
|
-
# that can be inserted, removed, and executed in sequence. Each middleware
|
|
8
|
-
# can be configured with specific options and is executed in the order
|
|
9
|
-
# they were registered.
|
|
10
|
-
#
|
|
11
|
-
# Supports copy-on-write semantics: a duped registry shares the parent's
|
|
12
|
-
# data until a write operation triggers materialization.
|
|
13
|
-
class MiddlewareRegistry
|
|
14
|
-
|
|
15
|
-
# Initialize a new middleware registry.
|
|
16
|
-
#
|
|
17
|
-
# @param registry [Array, nil] Initial array of middleware entries
|
|
18
|
-
#
|
|
19
|
-
# @example
|
|
20
|
-
# registry = MiddlewareRegistry.new
|
|
21
|
-
# registry = MiddlewareRegistry.new([[MyMiddleware, {option: 'value'}]])
|
|
22
|
-
#
|
|
23
|
-
# @rbs (?Array[Array[untyped]]? registry) -> void
|
|
24
|
-
def initialize(registry = nil)
|
|
25
|
-
@registry = registry || []
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
# Sets up copy-on-write state when duplicated via dup.
|
|
29
|
-
#
|
|
30
|
-
# @param source [MiddlewareRegistry] The registry being duplicated
|
|
31
|
-
#
|
|
32
|
-
# @rbs (MiddlewareRegistry source) -> void
|
|
33
|
-
def initialize_dup(source)
|
|
34
|
-
@parent = source
|
|
35
|
-
@registry = nil
|
|
36
|
-
super
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
# Returns the ordered collection of middleware entries.
|
|
40
|
-
# Delegates to the parent registry when not yet materialized.
|
|
41
|
-
#
|
|
42
|
-
# @return [Array<Array>] Array of middleware-options pairs
|
|
43
|
-
#
|
|
44
|
-
# @example
|
|
45
|
-
# registry.registry # => [[LoggingMiddleware, {level: :debug}], [AuthMiddleware, {}]]
|
|
46
|
-
#
|
|
47
|
-
# @rbs () -> Array[Array[untyped]]
|
|
48
|
-
def registry
|
|
49
|
-
@registry || @parent.registry
|
|
50
|
-
end
|
|
51
|
-
alias to_a registry
|
|
52
|
-
|
|
53
|
-
# Register a middleware component in the registry.
|
|
54
|
-
#
|
|
55
|
-
# @param middleware [Object] The middleware object to register
|
|
56
|
-
# @param at [Integer] Position to insert the middleware (default: -1, end of list)
|
|
57
|
-
# @param options [Hash] Configuration options for the middleware
|
|
58
|
-
# @option options [Symbol] :key Configuration key for the middleware
|
|
59
|
-
# @option options [Object] :value Configuration value for the middleware
|
|
60
|
-
#
|
|
61
|
-
# @return [MiddlewareRegistry] Returns self for method chaining
|
|
62
|
-
#
|
|
63
|
-
# @example
|
|
64
|
-
# registry.register(LoggingMiddleware, at: 0, log_level: :debug)
|
|
65
|
-
# registry.register(AuthMiddleware, at: -1, timeout: 30)
|
|
66
|
-
#
|
|
67
|
-
# @rbs (untyped middleware, ?at: Integer, **untyped options) -> self
|
|
68
|
-
def register(middleware, at: -1, **options)
|
|
69
|
-
materialize!
|
|
70
|
-
|
|
71
|
-
@registry.insert(at, [middleware, options])
|
|
72
|
-
self
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
# Remove a middleware component from the registry.
|
|
76
|
-
#
|
|
77
|
-
# @param middleware [Object] The middleware object to remove
|
|
78
|
-
#
|
|
79
|
-
# @return [MiddlewareRegistry] Returns self for method chaining
|
|
80
|
-
#
|
|
81
|
-
# @example
|
|
82
|
-
# registry.deregister(LoggingMiddleware)
|
|
83
|
-
#
|
|
84
|
-
# @rbs (untyped middleware) -> self
|
|
85
|
-
def deregister(middleware)
|
|
86
|
-
materialize!
|
|
87
|
-
|
|
88
|
-
@registry.reject! { |mw, _opts| mw == middleware }
|
|
89
|
-
self
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
# Execute the middleware chain for a given task.
|
|
93
|
-
#
|
|
94
|
-
# @param task [Object] The task object to process through middleware
|
|
95
|
-
#
|
|
96
|
-
# @yield [task] Block to execute after all middleware processing
|
|
97
|
-
# @yieldparam task [Object] The processed task object
|
|
98
|
-
#
|
|
99
|
-
# @return [Object] Result of the block execution
|
|
100
|
-
#
|
|
101
|
-
# @raise [ArgumentError] When no block is provided
|
|
102
|
-
#
|
|
103
|
-
# @example
|
|
104
|
-
# result = registry.call!(my_task) do |processed_task|
|
|
105
|
-
# processed_task.execute
|
|
106
|
-
# end
|
|
107
|
-
#
|
|
108
|
-
# @rbs (untyped task) { (untyped) -> untyped } -> untyped
|
|
109
|
-
def call!(task, &)
|
|
110
|
-
raise ArgumentError, "block required" unless block_given?
|
|
111
|
-
|
|
112
|
-
recursively_call_middleware(0, task, &)
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
private
|
|
116
|
-
|
|
117
|
-
# Copies the parent's registry data into this instance,
|
|
118
|
-
# severing the copy-on-write link.
|
|
119
|
-
#
|
|
120
|
-
# @rbs () -> void
|
|
121
|
-
def materialize!
|
|
122
|
-
return if @registry
|
|
123
|
-
|
|
124
|
-
@registry = @parent.registry.map(&:dup)
|
|
125
|
-
@parent = nil
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
# Recursively execute middleware in the chain.
|
|
129
|
-
#
|
|
130
|
-
# @param index [Integer] Current middleware index in the chain
|
|
131
|
-
# @param task [Object] The task object being processed
|
|
132
|
-
# @param block [Proc] Block to execute after middleware processing
|
|
133
|
-
#
|
|
134
|
-
# @yield [task] Block to execute after middleware processing
|
|
135
|
-
# @yieldparam task [Object] The processed task object
|
|
136
|
-
#
|
|
137
|
-
# @return [Object] Result of the block execution or next middleware call
|
|
138
|
-
#
|
|
139
|
-
# @rbs (Integer index, untyped task) { (untyped) -> untyped } -> untyped
|
|
140
|
-
def recursively_call_middleware(index, task, &block)
|
|
141
|
-
return yield(task) if index >= registry.size
|
|
142
|
-
|
|
143
|
-
middleware, options = registry[index]
|
|
144
|
-
middleware.call(task, **options) { recursively_call_middleware(index + 1, task, &block) }
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
end
|
|
148
|
-
end
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module CMDx
|
|
4
|
-
module Middlewares
|
|
5
|
-
# Middleware for correlating task executions with unique identifiers.
|
|
6
|
-
#
|
|
7
|
-
# The Correlate middleware provides thread and fiber safe correlation ID management
|
|
8
|
-
# for tracking task execution flows across different operations. It automatically
|
|
9
|
-
# generates correlation IDs when none are provided and stores them in task result
|
|
10
|
-
# metadata for traceability.
|
|
11
|
-
module Correlate
|
|
12
|
-
|
|
13
|
-
extend self
|
|
14
|
-
|
|
15
|
-
# @rbs CONCURRENCY_KEY: Symbol
|
|
16
|
-
CONCURRENCY_KEY = :cmdx_correlate
|
|
17
|
-
|
|
18
|
-
# Retrieves the current correlation ID from local storage.
|
|
19
|
-
#
|
|
20
|
-
# @return [String, nil] The current correlation ID or nil if not set
|
|
21
|
-
#
|
|
22
|
-
# @example Get current correlation ID
|
|
23
|
-
# Correlate.id # => "550e8400-e29b-41d4-a716-446655440000"
|
|
24
|
-
#
|
|
25
|
-
# @rbs () -> String?
|
|
26
|
-
def id
|
|
27
|
-
thread_or_fiber[CONCURRENCY_KEY]
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
# Sets the correlation ID in local storage.
|
|
31
|
-
#
|
|
32
|
-
# @param id [String] The correlation ID to set
|
|
33
|
-
# @return [String] The set correlation ID
|
|
34
|
-
#
|
|
35
|
-
# @example Set correlation ID
|
|
36
|
-
# Correlate.id = "abc-123-def"
|
|
37
|
-
#
|
|
38
|
-
# @rbs (String id) -> String
|
|
39
|
-
def id=(id)
|
|
40
|
-
thread_or_fiber[CONCURRENCY_KEY] = id
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
# Clears the current correlation ID from local storage.
|
|
44
|
-
#
|
|
45
|
-
# @return [nil] Always returns nil
|
|
46
|
-
#
|
|
47
|
-
# @example Clear correlation ID
|
|
48
|
-
# Correlate.clear
|
|
49
|
-
#
|
|
50
|
-
# @rbs () -> nil
|
|
51
|
-
def clear
|
|
52
|
-
thread_or_fiber[CONCURRENCY_KEY] = nil
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
# Temporarily uses a new correlation ID for the duration of a block.
|
|
56
|
-
# Restores the previous ID after the block completes, even if an error occurs.
|
|
57
|
-
#
|
|
58
|
-
# @param new_id [String] The correlation ID to use temporarily
|
|
59
|
-
# @yield The block to execute with the new correlation ID
|
|
60
|
-
# @return [Object] The result of the yielded block
|
|
61
|
-
#
|
|
62
|
-
# @example Use temporary correlation ID
|
|
63
|
-
# Correlate.use("temp-id") do
|
|
64
|
-
# # Operations here use "temp-id"
|
|
65
|
-
# perform_operation
|
|
66
|
-
# end
|
|
67
|
-
# # Previous ID is restored
|
|
68
|
-
#
|
|
69
|
-
# @rbs (String new_id) { () -> untyped } -> untyped
|
|
70
|
-
def use(new_id)
|
|
71
|
-
old_id = id
|
|
72
|
-
self.id = new_id
|
|
73
|
-
yield
|
|
74
|
-
ensure
|
|
75
|
-
self.id = old_id
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
# Middleware entry point that applies correlation ID logic to task execution.
|
|
79
|
-
#
|
|
80
|
-
# Evaluates the condition from options and applies correlation ID handling
|
|
81
|
-
# if enabled. Generates or retrieves correlation IDs based on the :id option
|
|
82
|
-
# and stores them in task result metadata.
|
|
83
|
-
#
|
|
84
|
-
# @param task [Task] The task being executed
|
|
85
|
-
# @param options [Hash] Configuration options for correlation
|
|
86
|
-
# @option options [Symbol, Proc, Object, nil] :id The correlation ID source
|
|
87
|
-
# @option options [Symbol, Proc, Object, nil] :if Condition to enable correlation
|
|
88
|
-
# @option options [Symbol, Proc, Object, nil] :unless Condition to disable correlation
|
|
89
|
-
#
|
|
90
|
-
# @yield The task execution block
|
|
91
|
-
#
|
|
92
|
-
# @return [Object] The result of task execution
|
|
93
|
-
#
|
|
94
|
-
# @example Basic usage with automatic ID generation
|
|
95
|
-
# Correlate.call(task, &block)
|
|
96
|
-
# @example Use custom correlation ID
|
|
97
|
-
# Correlate.call(task, id: "custom-123", &block)
|
|
98
|
-
# @example Use task method for ID
|
|
99
|
-
# Correlate.call(task, id: :correlation_id, &block)
|
|
100
|
-
# @example Use proc for dynamic ID generation
|
|
101
|
-
# Correlate.call(task, id: -> { "dynamic-#{Time.now.to_i}" }, &block)
|
|
102
|
-
# @example Conditional correlation
|
|
103
|
-
# Correlate.call(task, if: :enable_correlation, &block)
|
|
104
|
-
#
|
|
105
|
-
# @rbs (Task task, **untyped options) { () -> untyped } -> untyped
|
|
106
|
-
def call(task, **options, &)
|
|
107
|
-
return yield unless Utils::Condition.evaluate(task, options)
|
|
108
|
-
|
|
109
|
-
correlation_id = task.result.metadata[:correlation_id] ||=
|
|
110
|
-
id ||
|
|
111
|
-
case callable = options[:id]
|
|
112
|
-
when Symbol then task.send(callable)
|
|
113
|
-
when Proc then task.instance_eval(&callable)
|
|
114
|
-
else
|
|
115
|
-
if callable.respond_to?(:call)
|
|
116
|
-
callable.call(task)
|
|
117
|
-
else
|
|
118
|
-
callable || id || Identifier.generate
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
use(correlation_id, &)
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
private
|
|
126
|
-
|
|
127
|
-
# Returns the thread or fiber storage for the current execution context.
|
|
128
|
-
#
|
|
129
|
-
# @return [Hash] The thread or fiber storage
|
|
130
|
-
#
|
|
131
|
-
# @rbs () -> Hash
|
|
132
|
-
if Fiber.respond_to?(:storage)
|
|
133
|
-
def thread_or_fiber = Fiber.storage
|
|
134
|
-
else
|
|
135
|
-
def thread_or_fiber = Thread.current
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
end
|
|
139
|
-
end
|
|
140
|
-
end
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module CMDx
|
|
4
|
-
module Middlewares
|
|
5
|
-
# Middleware for measuring task execution runtime.
|
|
6
|
-
#
|
|
7
|
-
# The Runtime middleware provides performance monitoring by measuring
|
|
8
|
-
# the execution time of tasks using monotonic clock for accuracy.
|
|
9
|
-
# It stores runtime measurements in task result metadata for analysis.
|
|
10
|
-
module Runtime
|
|
11
|
-
|
|
12
|
-
extend self
|
|
13
|
-
|
|
14
|
-
# Middleware entry point that measures task execution runtime and
|
|
15
|
-
# task execution start and end times.
|
|
16
|
-
#
|
|
17
|
-
# Evaluates the condition from options and measures execution time
|
|
18
|
-
# if enabled. Uses monotonic clock for precise timing measurements
|
|
19
|
-
# and stores the result in task metadata.
|
|
20
|
-
#
|
|
21
|
-
# @param task [Task] The task being executed
|
|
22
|
-
# @param options [Hash] Configuration options for runtime measurement
|
|
23
|
-
# @option options [Symbol, Proc, Object, nil] :if Condition to enable runtime measurement
|
|
24
|
-
# @option options [Symbol, Proc, Object, nil] :unless Condition to disable runtime measurement
|
|
25
|
-
#
|
|
26
|
-
# @yield The task execution block
|
|
27
|
-
#
|
|
28
|
-
# @return [Object] The result of task execution
|
|
29
|
-
#
|
|
30
|
-
# @example Basic usage with automatic runtime measurement
|
|
31
|
-
# Runtime.call(task, &block)
|
|
32
|
-
# @example Conditional runtime measurement
|
|
33
|
-
# Runtime.call(task, if: :enable_profiling, &block)
|
|
34
|
-
# @example Disable runtime measurement
|
|
35
|
-
# Runtime.call(task, unless: :skip_profiling, &block)
|
|
36
|
-
#
|
|
37
|
-
# @rbs (Task task, **untyped options) { () -> untyped } -> untyped
|
|
38
|
-
def call(task, **options)
|
|
39
|
-
unow = utc_time
|
|
40
|
-
mnow = monotonic_time
|
|
41
|
-
return yield unless Utils::Condition.evaluate(task, options)
|
|
42
|
-
|
|
43
|
-
result = yield
|
|
44
|
-
task.result.metadata.merge!(
|
|
45
|
-
runtime: monotonic_time - mnow,
|
|
46
|
-
ended_at: utc_time,
|
|
47
|
-
started_at: unow
|
|
48
|
-
)
|
|
49
|
-
result
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
private
|
|
53
|
-
|
|
54
|
-
# Gets the current monotonic time in milliseconds.
|
|
55
|
-
#
|
|
56
|
-
# Uses Process.clock_gettime with CLOCK_MONOTONIC for consistent
|
|
57
|
-
# timing measurements that are not affected by system clock changes.
|
|
58
|
-
#
|
|
59
|
-
# @return [Integer] Current monotonic time in milliseconds
|
|
60
|
-
#
|
|
61
|
-
# @rbs () -> Integer
|
|
62
|
-
def monotonic_time
|
|
63
|
-
Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
# Gets the current UTC time in ISO 8601 format.
|
|
67
|
-
#
|
|
68
|
-
# @return [String] Current UTC time in ISO 8601 format
|
|
69
|
-
#
|
|
70
|
-
# @rbs () -> String
|
|
71
|
-
def utc_time
|
|
72
|
-
Time.now.utc.iso8601
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
end
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module CMDx
|
|
4
|
-
module Middlewares
|
|
5
|
-
# Middleware for enforcing execution time limits on tasks.
|
|
6
|
-
#
|
|
7
|
-
# The Timeout middleware provides execution time control by wrapping
|
|
8
|
-
# task execution with Ruby's Timeout module. It automatically fails
|
|
9
|
-
# tasks that exceed the configured time limit and provides detailed
|
|
10
|
-
# error information including the exceeded limit.
|
|
11
|
-
module Timeout
|
|
12
|
-
|
|
13
|
-
extend self
|
|
14
|
-
|
|
15
|
-
# Default timeout limit in seconds when none is specified.
|
|
16
|
-
#
|
|
17
|
-
# @rbs DEFAULT_LIMIT: Integer
|
|
18
|
-
DEFAULT_LIMIT = 3
|
|
19
|
-
|
|
20
|
-
# Middleware entry point that enforces execution time limits.
|
|
21
|
-
#
|
|
22
|
-
# Evaluates the condition from options and applies timeout control
|
|
23
|
-
# if enabled. Supports various timeout limit configurations including
|
|
24
|
-
# numeric values, task method calls, and dynamic proc evaluation.
|
|
25
|
-
#
|
|
26
|
-
# @param task [Task] The task being executed
|
|
27
|
-
# @param options [Hash] Configuration options for timeout control
|
|
28
|
-
# @option options [Numeric, Symbol, Proc, Object] :seconds The timeout limit source
|
|
29
|
-
# @option options [Symbol, Proc, Object, nil] :if Condition to enable timeout control
|
|
30
|
-
# @option options [Symbol, Proc, Object, nil] :unless Condition to disable timeout control
|
|
31
|
-
#
|
|
32
|
-
# @yield The task execution block
|
|
33
|
-
#
|
|
34
|
-
# @return [Object] The result of task execution
|
|
35
|
-
#
|
|
36
|
-
# @raise [TimeoutError] When execution exceeds the configured limit
|
|
37
|
-
#
|
|
38
|
-
# @example Basic usage with default 3 second timeout
|
|
39
|
-
# Timeout.call(task, &block)
|
|
40
|
-
# @example Custom timeout limit in seconds
|
|
41
|
-
# Timeout.call(task, seconds: 10, &block)
|
|
42
|
-
# @example Use task method for timeout limit
|
|
43
|
-
# Timeout.call(task, seconds: :timeout_limit, &block)
|
|
44
|
-
# @example Use proc for dynamic timeout calculation
|
|
45
|
-
# Timeout.call(task, seconds: -> { calculate_timeout }, &block)
|
|
46
|
-
# @example Conditional timeout control
|
|
47
|
-
# Timeout.call(task, if: :enable_timeout, &block)
|
|
48
|
-
#
|
|
49
|
-
# @rbs (Task task, **untyped options) { () -> untyped } -> untyped
|
|
50
|
-
def call(task, **options, &)
|
|
51
|
-
return yield unless Utils::Condition.evaluate(task, options)
|
|
52
|
-
|
|
53
|
-
limit =
|
|
54
|
-
case callable = options[:seconds]
|
|
55
|
-
when Numeric then callable
|
|
56
|
-
when Symbol then task.send(callable)
|
|
57
|
-
when Proc then task.instance_eval(&callable)
|
|
58
|
-
else callable.respond_to?(:call) ? callable.call(task) : DEFAULT_LIMIT
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
limit = Float(limit)
|
|
62
|
-
return yield unless limit.positive?
|
|
63
|
-
|
|
64
|
-
::Timeout.timeout(limit, TimeoutError, "execution exceeded #{limit} seconds", &)
|
|
65
|
-
rescue TimeoutError => e
|
|
66
|
-
task.result.tap do |r|
|
|
67
|
-
r.fail!(
|
|
68
|
-
Utils::Normalize.exception(e),
|
|
69
|
-
cause: e,
|
|
70
|
-
source: :timeout,
|
|
71
|
-
limit:
|
|
72
|
-
)
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
end
|
data/lib/cmdx/parallelizer.rb
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module CMDx
|
|
4
|
-
# Bounded thread pool that processes items concurrently.
|
|
5
|
-
#
|
|
6
|
-
# Distributes work across a fixed number of threads using a queue,
|
|
7
|
-
# collecting results in submission order.
|
|
8
|
-
class Parallelizer
|
|
9
|
-
|
|
10
|
-
# Returns the items to process.
|
|
11
|
-
#
|
|
12
|
-
# @return [Array] the items to process
|
|
13
|
-
#
|
|
14
|
-
# @example
|
|
15
|
-
# parallelizer.items # => [1, 2, 3]
|
|
16
|
-
#
|
|
17
|
-
# @rbs @items: Array[untyped]
|
|
18
|
-
attr_reader :items
|
|
19
|
-
|
|
20
|
-
# Returns the number of threads in the pool.
|
|
21
|
-
#
|
|
22
|
-
# @return [Integer] the thread pool size
|
|
23
|
-
#
|
|
24
|
-
# @example
|
|
25
|
-
# parallelizer.pool_size # => 4
|
|
26
|
-
#
|
|
27
|
-
# @rbs @pool_size: Integer
|
|
28
|
-
attr_reader :pool_size
|
|
29
|
-
|
|
30
|
-
# Creates a new Parallelizer instance.
|
|
31
|
-
#
|
|
32
|
-
# @param items [Array] the items to process concurrently
|
|
33
|
-
# @param pool_size [Integer] number of threads (defaults to item count)
|
|
34
|
-
#
|
|
35
|
-
# @return [Parallelizer] a new parallelizer instance
|
|
36
|
-
#
|
|
37
|
-
# @example
|
|
38
|
-
# Parallelizer.new([1, 2, 3], pool_size: 2)
|
|
39
|
-
#
|
|
40
|
-
# @rbs (Array[untyped] items, ?pool_size: Integer) -> void
|
|
41
|
-
def initialize(items, pool_size: nil)
|
|
42
|
-
@items = items
|
|
43
|
-
@pool_size = Integer(pool_size || items.size)
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
# Processes items concurrently and returns results in submission order.
|
|
47
|
-
#
|
|
48
|
-
# @param items [Array] the items to process concurrently
|
|
49
|
-
# @param pool_size [Integer] number of threads (defaults to item count)
|
|
50
|
-
#
|
|
51
|
-
# @yield [item] block called for each item in a worker thread
|
|
52
|
-
# @yieldparam item [Object] an item from the items array
|
|
53
|
-
# @yieldreturn [Object] the result for this item
|
|
54
|
-
#
|
|
55
|
-
# @return [Array] results in the same order as input items
|
|
56
|
-
#
|
|
57
|
-
# @example
|
|
58
|
-
# Parallelizer.call([1, 2, 3], pool_size: 2) { |n| n * 10 }
|
|
59
|
-
# # => [10, 20, 30]
|
|
60
|
-
#
|
|
61
|
-
# @rbs [T, R] (Array[T] items, ?pool_size: Integer) { (T) -> R } -> Array[R]
|
|
62
|
-
def self.call(items, pool_size: nil, &block)
|
|
63
|
-
new(items, pool_size:).call(&block)
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
# Distributes items across the thread pool and returns results
|
|
67
|
-
# in submission order.
|
|
68
|
-
#
|
|
69
|
-
# @yield [item] block called for each item in a worker thread
|
|
70
|
-
# @yieldparam item [Object] an item from the items array
|
|
71
|
-
# @yieldreturn [Object] the result for this item
|
|
72
|
-
#
|
|
73
|
-
# @return [Array] results in the same order as input items
|
|
74
|
-
#
|
|
75
|
-
# @example
|
|
76
|
-
# Parallelizer.new(%w[a b c]).call { |s| s.upcase }
|
|
77
|
-
# # => ["A", "B", "C"]
|
|
78
|
-
#
|
|
79
|
-
# @rbs [T, R] () { (T) -> R } -> Array[R]
|
|
80
|
-
def call(&block)
|
|
81
|
-
results = Array.new(items.size)
|
|
82
|
-
queue = Queue.new
|
|
83
|
-
|
|
84
|
-
items.each_with_index { |item, i| queue << [item, i] }
|
|
85
|
-
pool_size.times { queue << nil }
|
|
86
|
-
|
|
87
|
-
Array.new(pool_size) do
|
|
88
|
-
Thread.new do
|
|
89
|
-
while (entry = queue.pop)
|
|
90
|
-
item, index = entry
|
|
91
|
-
results[index] = block.call(item) # rubocop:disable Performance/RedundantBlockCall
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
end.each(&:join)
|
|
95
|
-
|
|
96
|
-
results
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
end
|
|
100
|
-
end
|
data/lib/cmdx/utils/call.rb
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module CMDx
|
|
4
|
-
module Utils
|
|
5
|
-
# Utility module for invoking callable objects with different invocation strategies.
|
|
6
|
-
#
|
|
7
|
-
# This module provides a unified interface for calling methods, procs, and other
|
|
8
|
-
# callable objects on target objects, handling the appropriate invocation method
|
|
9
|
-
# based on the callable type.
|
|
10
|
-
module Call
|
|
11
|
-
|
|
12
|
-
extend self
|
|
13
|
-
|
|
14
|
-
# Invokes a callable object on the target with the given arguments.
|
|
15
|
-
#
|
|
16
|
-
# @param target [Object] The target object to invoke the callable on
|
|
17
|
-
# @param callable [Symbol, Proc, #call] The callable to invoke
|
|
18
|
-
# @param args [Array] Positional arguments to pass to the callable
|
|
19
|
-
# @param kwargs [Hash] Keyword arguments to pass to the callable
|
|
20
|
-
# @option kwargs [Object] :* Any keyword arguments to pass to the callable
|
|
21
|
-
#
|
|
22
|
-
# @yield [Object] Block to pass to the callable
|
|
23
|
-
#
|
|
24
|
-
# @return [Object] The result of invoking the callable
|
|
25
|
-
#
|
|
26
|
-
# @raise [RuntimeError] When the callable cannot be invoked
|
|
27
|
-
#
|
|
28
|
-
# @example Invoking a method by symbol
|
|
29
|
-
# Call.invoke(user, :name)
|
|
30
|
-
# Call.invoke(user, :update, { name: 'John' })
|
|
31
|
-
# @example Invoking a proc
|
|
32
|
-
# proc = ->(name) { "Hello #{name}" }
|
|
33
|
-
# Call.invoke(user, proc, 'John')
|
|
34
|
-
# @example Invoking a callable object
|
|
35
|
-
# callable = MyCallable.new
|
|
36
|
-
# Call.invoke(user, callable, 'data')
|
|
37
|
-
#
|
|
38
|
-
# @rbs (untyped target, (Symbol | Proc | untyped) callable, *untyped args, **untyped kwargs) ?{ () -> untyped } -> untyped
|
|
39
|
-
def invoke(target, callable, *args, **kwargs, &)
|
|
40
|
-
if callable.is_a?(Symbol)
|
|
41
|
-
target.send(callable, *args, **kwargs, &)
|
|
42
|
-
elsif callable.is_a?(Proc)
|
|
43
|
-
target.instance_exec(*args, **kwargs, &callable)
|
|
44
|
-
elsif callable.respond_to?(:call)
|
|
45
|
-
callable.call(*args, **kwargs, &)
|
|
46
|
-
else
|
|
47
|
-
raise "cannot invoke #{callable}"
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
end
|