cmdx 1.1.2 → 1.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 +4 -4
- data/.DS_Store +0 -0
- data/.cursor/prompts/docs.md +4 -1
- data/.cursor/prompts/llms.md +20 -0
- data/.cursor/prompts/rspec.md +4 -1
- data/.cursor/prompts/yardoc.md +3 -2
- data/.cursor/rules/cursor-instructions.mdc +56 -1
- data/.irbrc +6 -0
- data/.rubocop.yml +29 -18
- data/CHANGELOG.md +5 -133
- data/LLM.md +3317 -0
- data/README.md +68 -44
- data/docs/attributes/coercions.md +162 -0
- data/docs/attributes/defaults.md +90 -0
- data/docs/attributes/definitions.md +281 -0
- data/docs/attributes/naming.md +78 -0
- data/docs/attributes/validations.md +309 -0
- data/docs/basics/chain.md +56 -249
- data/docs/basics/context.md +56 -289
- data/docs/basics/execution.md +114 -0
- data/docs/basics/setup.md +37 -334
- data/docs/callbacks.md +89 -467
- data/docs/deprecation.md +91 -174
- data/docs/getting_started.md +212 -202
- data/docs/internationalization.md +11 -647
- data/docs/interruptions/exceptions.md +23 -198
- data/docs/interruptions/faults.md +71 -151
- data/docs/interruptions/halt.md +109 -186
- data/docs/logging.md +44 -256
- data/docs/middlewares.md +113 -426
- data/docs/outcomes/result.md +81 -228
- data/docs/outcomes/states.md +33 -221
- data/docs/outcomes/statuses.md +21 -311
- data/docs/tips_and_tricks.md +120 -70
- data/docs/workflows.md +99 -283
- data/lib/cmdx/.DS_Store +0 -0
- data/lib/cmdx/attribute.rb +229 -0
- data/lib/cmdx/attribute_registry.rb +94 -0
- data/lib/cmdx/attribute_value.rb +193 -0
- data/lib/cmdx/callback_registry.rb +69 -77
- data/lib/cmdx/chain.rb +56 -73
- data/lib/cmdx/coercion_registry.rb +52 -68
- data/lib/cmdx/coercions/array.rb +19 -18
- data/lib/cmdx/coercions/big_decimal.rb +20 -24
- data/lib/cmdx/coercions/boolean.rb +26 -25
- data/lib/cmdx/coercions/complex.rb +21 -22
- data/lib/cmdx/coercions/date.rb +25 -23
- data/lib/cmdx/coercions/date_time.rb +24 -25
- data/lib/cmdx/coercions/float.rb +25 -22
- data/lib/cmdx/coercions/hash.rb +31 -32
- data/lib/cmdx/coercions/integer.rb +30 -24
- data/lib/cmdx/coercions/rational.rb +29 -24
- data/lib/cmdx/coercions/string.rb +19 -22
- data/lib/cmdx/coercions/symbol.rb +37 -0
- data/lib/cmdx/coercions/time.rb +26 -25
- data/lib/cmdx/configuration.rb +49 -108
- data/lib/cmdx/context.rb +222 -44
- data/lib/cmdx/deprecator.rb +61 -0
- data/lib/cmdx/errors.rb +42 -252
- data/lib/cmdx/exceptions.rb +39 -0
- data/lib/cmdx/faults.rb +78 -39
- data/lib/cmdx/freezer.rb +51 -0
- data/lib/cmdx/identifier.rb +30 -0
- data/lib/cmdx/locale.rb +52 -0
- data/lib/cmdx/log_formatters/json.rb +21 -22
- data/lib/cmdx/log_formatters/key_value.rb +20 -22
- data/lib/cmdx/log_formatters/line.rb +15 -22
- data/lib/cmdx/log_formatters/logstash.rb +22 -23
- data/lib/cmdx/log_formatters/raw.rb +16 -22
- data/lib/cmdx/middleware_registry.rb +70 -74
- data/lib/cmdx/middlewares/correlate.rb +90 -54
- data/lib/cmdx/middlewares/runtime.rb +58 -0
- data/lib/cmdx/middlewares/timeout.rb +48 -68
- data/lib/cmdx/railtie.rb +12 -45
- data/lib/cmdx/result.rb +229 -314
- data/lib/cmdx/task.rb +194 -366
- data/lib/cmdx/utils/call.rb +49 -0
- data/lib/cmdx/utils/condition.rb +71 -0
- data/lib/cmdx/utils/format.rb +61 -0
- data/lib/cmdx/validator_registry.rb +63 -72
- data/lib/cmdx/validators/exclusion.rb +38 -67
- data/lib/cmdx/validators/format.rb +48 -49
- data/lib/cmdx/validators/inclusion.rb +43 -74
- data/lib/cmdx/validators/length.rb +91 -154
- data/lib/cmdx/validators/numeric.rb +87 -162
- data/lib/cmdx/validators/presence.rb +37 -50
- data/lib/cmdx/version.rb +1 -1
- data/lib/cmdx/worker.rb +178 -0
- data/lib/cmdx/workflow.rb +85 -81
- data/lib/cmdx.rb +19 -13
- data/lib/generators/cmdx/install_generator.rb +14 -13
- data/lib/generators/cmdx/task_generator.rb +25 -50
- data/lib/generators/cmdx/templates/install.rb +11 -46
- data/lib/generators/cmdx/templates/task.rb.tt +3 -2
- data/lib/locales/en.yml +18 -4
- data/src/cmdx-logo.png +0 -0
- metadata +32 -116
- data/docs/ai_prompts.md +0 -393
- data/docs/basics/call.md +0 -317
- data/docs/configuration.md +0 -344
- data/docs/parameters/coercions.md +0 -396
- data/docs/parameters/defaults.md +0 -335
- data/docs/parameters/definitions.md +0 -446
- data/docs/parameters/namespacing.md +0 -378
- data/docs/parameters/validations.md +0 -405
- data/docs/testing.md +0 -553
- data/lib/cmdx/callback.rb +0 -53
- data/lib/cmdx/chain_inspector.rb +0 -56
- data/lib/cmdx/chain_serializer.rb +0 -63
- data/lib/cmdx/coercion.rb +0 -57
- data/lib/cmdx/coercions/virtual.rb +0 -29
- data/lib/cmdx/core_ext/hash.rb +0 -83
- data/lib/cmdx/core_ext/module.rb +0 -98
- data/lib/cmdx/core_ext/object.rb +0 -125
- data/lib/cmdx/correlator.rb +0 -122
- data/lib/cmdx/error.rb +0 -67
- data/lib/cmdx/fault.rb +0 -140
- data/lib/cmdx/immutator.rb +0 -52
- data/lib/cmdx/lazy_struct.rb +0 -246
- data/lib/cmdx/log_formatters/pretty_json.rb +0 -40
- data/lib/cmdx/log_formatters/pretty_key_value.rb +0 -38
- data/lib/cmdx/log_formatters/pretty_line.rb +0 -41
- data/lib/cmdx/logger.rb +0 -49
- data/lib/cmdx/logger_ansi.rb +0 -68
- data/lib/cmdx/logger_serializer.rb +0 -116
- data/lib/cmdx/middleware.rb +0 -70
- data/lib/cmdx/parameter.rb +0 -312
- data/lib/cmdx/parameter_evaluator.rb +0 -231
- data/lib/cmdx/parameter_inspector.rb +0 -66
- data/lib/cmdx/parameter_registry.rb +0 -106
- data/lib/cmdx/parameter_serializer.rb +0 -59
- data/lib/cmdx/result_ansi.rb +0 -71
- data/lib/cmdx/result_inspector.rb +0 -71
- data/lib/cmdx/result_logger.rb +0 -59
- data/lib/cmdx/result_serializer.rb +0 -104
- data/lib/cmdx/rspec/matchers.rb +0 -28
- data/lib/cmdx/rspec/result_matchers/be_executed.rb +0 -42
- data/lib/cmdx/rspec/result_matchers/be_failed_task.rb +0 -94
- data/lib/cmdx/rspec/result_matchers/be_skipped_task.rb +0 -94
- data/lib/cmdx/rspec/result_matchers/be_state_matchers.rb +0 -59
- data/lib/cmdx/rspec/result_matchers/be_status_matchers.rb +0 -57
- data/lib/cmdx/rspec/result_matchers/be_successful_task.rb +0 -87
- data/lib/cmdx/rspec/result_matchers/have_bad_outcome.rb +0 -51
- data/lib/cmdx/rspec/result_matchers/have_caused_failure.rb +0 -58
- data/lib/cmdx/rspec/result_matchers/have_chain_index.rb +0 -59
- data/lib/cmdx/rspec/result_matchers/have_context.rb +0 -86
- data/lib/cmdx/rspec/result_matchers/have_empty_metadata.rb +0 -54
- data/lib/cmdx/rspec/result_matchers/have_good_outcome.rb +0 -52
- data/lib/cmdx/rspec/result_matchers/have_metadata.rb +0 -114
- data/lib/cmdx/rspec/result_matchers/have_preserved_context.rb +0 -66
- data/lib/cmdx/rspec/result_matchers/have_received_thrown_failure.rb +0 -64
- data/lib/cmdx/rspec/result_matchers/have_runtime.rb +0 -78
- data/lib/cmdx/rspec/result_matchers/have_thrown_failure.rb +0 -76
- data/lib/cmdx/rspec/task_matchers/be_well_formed_task.rb +0 -62
- data/lib/cmdx/rspec/task_matchers/have_callback.rb +0 -85
- data/lib/cmdx/rspec/task_matchers/have_cmd_setting.rb +0 -68
- data/lib/cmdx/rspec/task_matchers/have_executed_callbacks.rb +0 -92
- data/lib/cmdx/rspec/task_matchers/have_middleware.rb +0 -46
- data/lib/cmdx/rspec/task_matchers/have_parameter.rb +0 -181
- data/lib/cmdx/task_deprecator.rb +0 -58
- data/lib/cmdx/task_processor.rb +0 -246
- data/lib/cmdx/task_serializer.rb +0 -57
- data/lib/cmdx/utils/ansi_color.rb +0 -73
- data/lib/cmdx/utils/log_timestamp.rb +0 -36
- data/lib/cmdx/utils/monotonic_runtime.rb +0 -34
- data/lib/cmdx/utils/name_affix.rb +0 -52
- data/lib/cmdx/validator.rb +0 -57
- data/lib/generators/cmdx/templates/workflow.rb.tt +0 -7
- data/lib/generators/cmdx/workflow_generator.rb +0 -84
- data/lib/locales/ar.yml +0 -35
- data/lib/locales/cs.yml +0 -35
- data/lib/locales/da.yml +0 -35
- data/lib/locales/de.yml +0 -35
- data/lib/locales/el.yml +0 -35
- data/lib/locales/es.yml +0 -35
- data/lib/locales/fi.yml +0 -35
- data/lib/locales/fr.yml +0 -35
- data/lib/locales/he.yml +0 -35
- data/lib/locales/hi.yml +0 -35
- data/lib/locales/it.yml +0 -35
- data/lib/locales/ja.yml +0 -35
- data/lib/locales/ko.yml +0 -35
- data/lib/locales/nl.yml +0 -35
- data/lib/locales/no.yml +0 -35
- data/lib/locales/pl.yml +0 -35
- data/lib/locales/pt.yml +0 -35
- data/lib/locales/ru.yml +0 -35
- data/lib/locales/sv.yml +0 -35
- data/lib/locales/th.yml +0 -35
- data/lib/locales/tr.yml +0 -35
- data/lib/locales/vi.yml +0 -35
- data/lib/locales/zh.yml +0 -35
data/lib/cmdx/configuration.rb
CHANGED
@@ -2,141 +2,94 @@
|
|
2
2
|
|
3
3
|
module CMDx
|
4
4
|
|
5
|
-
#
|
6
|
-
#
|
7
|
-
# Manages logging, middleware, callbacks, coercions, validators, and halt conditions
|
8
|
-
# for the entire CMDx framework. The Configuration class provides centralized control
|
9
|
-
# over framework behavior including task execution flow, error handling, and component
|
10
|
-
# registration. All settings configured here become defaults for tasks and workflows
|
11
|
-
# unless explicitly overridden at the task or workflow level.
|
12
|
-
#
|
13
|
-
# The configuration system supports both global and per-task customization, allowing
|
14
|
-
# fine-grained control over framework behavior while maintaining sensible defaults.
|
5
|
+
# Configuration class that manages global settings for CMDx including middlewares,
|
6
|
+
# callbacks, coercions, validators, breakpoints, and logging.
|
15
7
|
class Configuration
|
16
8
|
|
17
|
-
|
18
|
-
|
19
|
-
# @return [Logger] Logger instance for task execution logging
|
20
|
-
attr_accessor :logger
|
21
|
-
|
22
|
-
# @return [MiddlewareRegistry] Global middleware registry applied to all tasks
|
23
|
-
attr_accessor :middlewares
|
24
|
-
|
25
|
-
# @return [CallbackRegistry] Global callback registry applied to all tasks
|
26
|
-
attr_accessor :callbacks
|
9
|
+
DEFAULT_BREAKPOINTS = %w[failed].freeze
|
27
10
|
|
28
|
-
|
29
|
-
|
11
|
+
attr_accessor :middlewares, :callbacks, :coercions, :validators,
|
12
|
+
:task_breakpoints, :workflow_breakpoints, :logger
|
30
13
|
|
31
|
-
#
|
32
|
-
attr_accessor :validators
|
33
|
-
|
34
|
-
# @return [String, Array<String>] Result statuses that cause `call!` to raise faults
|
35
|
-
attr_accessor :task_halt
|
36
|
-
|
37
|
-
# @return [String, Array<String>] Result statuses that halt workflow execution
|
38
|
-
attr_accessor :workflow_halt
|
39
|
-
|
40
|
-
# Creates a new configuration instance with default settings.
|
14
|
+
# Initializes a new Configuration instance with default values.
|
41
15
|
#
|
42
|
-
#
|
43
|
-
#
|
44
|
-
# components, and default halt conditions for both tasks and workflows.
|
16
|
+
# Creates new registry instances for middlewares, callbacks, coercions, and
|
17
|
+
# validators. Sets default breakpoints and configures a basic logger.
|
45
18
|
#
|
46
|
-
# @return [Configuration] a new
|
19
|
+
# @return [Configuration] a new Configuration instance
|
47
20
|
#
|
48
|
-
# @example
|
21
|
+
# @example
|
49
22
|
# config = Configuration.new
|
50
|
-
# config.
|
51
|
-
# config.
|
23
|
+
# config.middlewares.class # => MiddlewareRegistry
|
24
|
+
# config.task_breakpoints # => ["failed"]
|
52
25
|
def initialize
|
53
|
-
@
|
54
|
-
@
|
55
|
-
@
|
56
|
-
@
|
57
|
-
|
58
|
-
@
|
59
|
-
@
|
26
|
+
@middlewares = MiddlewareRegistry.new
|
27
|
+
@callbacks = CallbackRegistry.new
|
28
|
+
@coercions = CoercionRegistry.new
|
29
|
+
@validators = ValidatorRegistry.new
|
30
|
+
|
31
|
+
@task_breakpoints = DEFAULT_BREAKPOINTS
|
32
|
+
@workflow_breakpoints = DEFAULT_BREAKPOINTS
|
33
|
+
|
34
|
+
@logger = Logger.new(
|
35
|
+
$stdout,
|
36
|
+
progname: "cmdx",
|
37
|
+
formatter: LogFormatters::Line.new,
|
38
|
+
level: Logger::INFO
|
39
|
+
)
|
60
40
|
end
|
61
41
|
|
62
42
|
# Converts the configuration to a hash representation.
|
63
43
|
#
|
64
|
-
#
|
65
|
-
# inspection, or transfer between processes. The hash includes all registries
|
66
|
-
# and settings in their current state.
|
67
|
-
#
|
68
|
-
# @return [Hash] hash representation of the configuration
|
69
|
-
# @option return [Logger] :logger the configured logger instance
|
70
|
-
# @option return [MiddlewareRegistry] :middlewares the middleware registry
|
71
|
-
# @option return [CallbackRegistry] :callbacks the callback registry
|
72
|
-
# @option return [CoercionRegistry] :coercions the coercion registry
|
73
|
-
# @option return [ValidatorRegistry] :validators the validator registry
|
74
|
-
# @option return [String, Array<String>] :task_halt the task halt configuration
|
75
|
-
# @option return [String, Array<String>] :workflow_halt the workflow halt configuration
|
44
|
+
# @return [Hash<Symbol, Object>] hash containing all configuration values
|
76
45
|
#
|
77
|
-
# @example
|
46
|
+
# @example
|
78
47
|
# config = Configuration.new
|
79
|
-
#
|
80
|
-
#
|
81
|
-
# hash[:task_halt] #=> "failed"
|
48
|
+
# config.to_h
|
49
|
+
# # => { middlewares: #<MiddlewareRegistry>, callbacks: #<CallbackRegistry>, ... }
|
82
50
|
def to_h
|
83
51
|
{
|
84
|
-
logger: @logger,
|
85
52
|
middlewares: @middlewares,
|
86
53
|
callbacks: @callbacks,
|
87
54
|
coercions: @coercions,
|
88
55
|
validators: @validators,
|
89
|
-
|
90
|
-
|
56
|
+
task_breakpoints: @task_breakpoints,
|
57
|
+
workflow_breakpoints: @workflow_breakpoints,
|
58
|
+
logger: @logger
|
91
59
|
}
|
92
60
|
end
|
93
61
|
|
94
62
|
end
|
95
63
|
|
96
|
-
|
64
|
+
extend self
|
97
65
|
|
98
|
-
# Returns the
|
66
|
+
# Returns the global configuration instance, creating it if it doesn't exist.
|
99
67
|
#
|
100
|
-
#
|
101
|
-
# CMDx framework. Creates a new configuration with default settings if none
|
102
|
-
# exists. This method is thread-safe and ensures only one configuration
|
103
|
-
# instance exists per process.
|
68
|
+
# @return [Configuration] the global configuration instance
|
104
69
|
#
|
105
|
-
# @
|
106
|
-
#
|
107
|
-
# @example Access global configuration
|
70
|
+
# @example
|
108
71
|
# config = CMDx.configuration
|
109
|
-
# config.
|
110
|
-
# config.task_halt = ["failed", "skipped"]
|
72
|
+
# config.middlewares # => #<MiddlewareRegistry>
|
111
73
|
def configuration
|
112
74
|
return @configuration if @configuration
|
113
75
|
|
114
76
|
@configuration ||= Configuration.new
|
115
77
|
end
|
116
78
|
|
117
|
-
# Configures
|
79
|
+
# Configures CMDx using a block that receives the configuration instance.
|
118
80
|
#
|
119
|
-
#
|
120
|
-
# modification. This is the recommended way to configure CMDx as it
|
121
|
-
# provides a clean DSL-like interface for setting up the framework.
|
81
|
+
# @param block [Proc] the configuration block
|
122
82
|
#
|
123
|
-
# @
|
83
|
+
# @yield [Configuration] the configuration instance to configure
|
124
84
|
#
|
125
85
|
# @return [Configuration] the configured configuration instance
|
126
86
|
#
|
127
|
-
# @raise [ArgumentError]
|
128
|
-
#
|
129
|
-
# @example Configure CMDx settings
|
130
|
-
# CMDx.configure do |config|
|
131
|
-
# config.logger.level = Logger::INFO
|
132
|
-
# config.task_halt = ["failed", "skipped"]
|
133
|
-
# config.middlewares.register(CMDx::Middlewares::Timeout.new(seconds: 30))
|
134
|
-
# end
|
87
|
+
# @raise [ArgumentError] when no block is provided
|
135
88
|
#
|
136
|
-
# @example
|
89
|
+
# @example
|
137
90
|
# CMDx.configure do |config|
|
138
|
-
# config.
|
139
|
-
# config.logger.
|
91
|
+
# config.task_breakpoints = ["failed", "skipped"]
|
92
|
+
# config.logger.level = Logger::DEBUG
|
140
93
|
# end
|
141
94
|
def configure
|
142
95
|
raise ArgumentError, "block required" unless block_given?
|
@@ -146,25 +99,13 @@ module CMDx
|
|
146
99
|
config
|
147
100
|
end
|
148
101
|
|
149
|
-
# Resets the global configuration to default
|
102
|
+
# Resets the global configuration to a new instance with default values.
|
150
103
|
#
|
151
|
-
#
|
152
|
-
# any existing configuration. This is useful for testing scenarios or
|
153
|
-
# when you need to start with a clean configuration state.
|
154
|
-
#
|
155
|
-
# @return [Configuration] a new configuration instance with default settings
|
156
|
-
#
|
157
|
-
# @example Reset to defaults
|
158
|
-
# CMDx.configure { |c| c.task_halt = ["failed", "skipped"] }
|
159
|
-
# CMDx.configuration.task_halt #=> ["failed", "skipped"]
|
104
|
+
# @return [Configuration] the new configuration instance
|
160
105
|
#
|
106
|
+
# @example
|
161
107
|
# CMDx.reset_configuration!
|
162
|
-
#
|
163
|
-
#
|
164
|
-
# @example Use in test setup
|
165
|
-
# RSpec.configure do |config|
|
166
|
-
# config.before(:each) { CMDx.reset_configuration! }
|
167
|
-
# end
|
108
|
+
# # Configuration is now reset to defaults
|
168
109
|
def reset_configuration!
|
169
110
|
@configuration = Configuration.new
|
170
111
|
end
|
data/lib/cmdx/context.rb
CHANGED
@@ -1,53 +1,231 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module CMDx
|
4
|
-
#
|
4
|
+
# A hash-like context object that provides a flexible way to store and access
|
5
|
+
# key-value pairs during task execution. Keys are automatically converted to
|
6
|
+
# symbols for consistency.
|
5
7
|
#
|
6
|
-
# Context
|
7
|
-
#
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
# @
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
# context
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
# @
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
8
|
+
# The Context class extends Forwardable to delegate common hash methods and
|
9
|
+
# provides additional convenience methods for working with context data.
|
10
|
+
class Context
|
11
|
+
|
12
|
+
extend Forwardable
|
13
|
+
|
14
|
+
attr_reader :table
|
15
|
+
alias to_h table
|
16
|
+
|
17
|
+
def_delegators :table, :each, :map
|
18
|
+
|
19
|
+
# Creates a new Context instance from the given arguments.
|
20
|
+
#
|
21
|
+
# @param args [Hash, Object] arguments to initialize the context with
|
22
|
+
# @option args [Object] :key the key-value pairs to store in the context
|
23
|
+
#
|
24
|
+
# @return [Context] a new Context instance
|
25
|
+
#
|
26
|
+
# @raise [ArgumentError] when args doesn't respond to `to_h` or `to_hash`
|
27
|
+
#
|
28
|
+
# @example
|
29
|
+
# context = Context.new(name: "John", age: 30)
|
30
|
+
# context[:name] # => "John"
|
31
|
+
def initialize(args = {})
|
32
|
+
@table =
|
33
|
+
if args.respond_to?(:to_hash)
|
34
|
+
args.to_hash
|
35
|
+
elsif args.respond_to?(:to_h)
|
36
|
+
args.to_h
|
37
|
+
else
|
38
|
+
raise ArgumentError, "must respond to `to_h` or `to_hash`"
|
39
|
+
end.transform_keys(&:to_sym)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Builds a Context instance, reusing existing unfrozen contexts when possible.
|
43
|
+
#
|
44
|
+
# @param context [Context, Object] the context to build from
|
45
|
+
# @option context [Object] :key the key-value pairs to store in the context
|
46
|
+
#
|
47
|
+
# @return [Context] a Context instance, either new or reused
|
48
|
+
#
|
49
|
+
# @example
|
50
|
+
# existing = Context.new(name: "John")
|
51
|
+
# built = Context.build(existing) # reuses existing context
|
52
|
+
# built.object_id == existing.object_id # => true
|
47
53
|
def self.build(context = {})
|
48
|
-
|
54
|
+
if context.is_a?(self) && !context.frozen?
|
55
|
+
context
|
56
|
+
elsif context.respond_to?(:context)
|
57
|
+
build(context.context)
|
58
|
+
else
|
59
|
+
new(context)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Retrieves a value from the context by key.
|
64
|
+
#
|
65
|
+
# @param key [String, Symbol] the key to retrieve
|
66
|
+
#
|
67
|
+
# @return [Object, nil] the value associated with the key, or nil if not found
|
68
|
+
#
|
69
|
+
# @example
|
70
|
+
# context = Context.new(name: "John")
|
71
|
+
# context[:name] # => "John"
|
72
|
+
# context["name"] # => "John" (automatically converted to symbol)
|
73
|
+
def [](key)
|
74
|
+
table[key.to_sym]
|
75
|
+
end
|
76
|
+
|
77
|
+
# Stores a key-value pair in the context.
|
78
|
+
#
|
79
|
+
# @param key [String, Symbol] the key to store
|
80
|
+
# @param value [Object] the value to store
|
81
|
+
#
|
82
|
+
# @return [Object] the stored value
|
83
|
+
#
|
84
|
+
# @example
|
85
|
+
# context = Context.new
|
86
|
+
# context.store(:name, "John")
|
87
|
+
# context[:name] # => "John"
|
88
|
+
def store(key, value)
|
89
|
+
table[key.to_sym] = value
|
90
|
+
end
|
91
|
+
alias []= store
|
92
|
+
|
93
|
+
# Fetches a value from the context by key, with optional default value.
|
94
|
+
#
|
95
|
+
# @param key [String, Symbol] the key to fetch
|
96
|
+
# @param default [Object] the default value if key is not found
|
97
|
+
#
|
98
|
+
# @yield [key] a block to compute the default value
|
99
|
+
#
|
100
|
+
# @return [Object] the value associated with the key, or the default/default block result
|
101
|
+
#
|
102
|
+
# @example
|
103
|
+
# context = Context.new(name: "John")
|
104
|
+
# context.fetch(:name) # => "John"
|
105
|
+
# context.fetch(:age, 25) # => 25
|
106
|
+
# context.fetch(:city) { |key| "Unknown #{key}" } # => "Unknown city"
|
107
|
+
def fetch(key, ...)
|
108
|
+
table.fetch(key.to_sym, ...)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Merges the given arguments into the current context, modifying it in place.
|
112
|
+
#
|
113
|
+
# @param args [Hash, Object] arguments to merge into the context
|
114
|
+
# @option args [Object] :key the key-value pairs to merge
|
115
|
+
#
|
116
|
+
# @return [Context] self for method chaining
|
117
|
+
#
|
118
|
+
# @example
|
119
|
+
# context = Context.new(name: "John")
|
120
|
+
# context.merge!(age: 30, city: "NYC")
|
121
|
+
# context.to_h # => {name: "John", age: 30, city: "NYC"}
|
122
|
+
def merge!(args = {})
|
123
|
+
args.to_h.each { |key, value| self[key.to_sym] = value }
|
124
|
+
self
|
125
|
+
end
|
49
126
|
|
50
|
-
|
127
|
+
# Deletes a key-value pair from the context.
|
128
|
+
#
|
129
|
+
# @param key [String, Symbol] the key to delete
|
130
|
+
#
|
131
|
+
# @yield [key] a block to handle the case when key is not found
|
132
|
+
#
|
133
|
+
# @return [Object, nil] the deleted value, or the block result if key not found
|
134
|
+
#
|
135
|
+
# @example
|
136
|
+
# context = Context.new(name: "John", age: 30)
|
137
|
+
# context.delete!(:age) # => 30
|
138
|
+
# context.delete!(:city) { |key| "Key #{key} not found" } # => "Key city not found"
|
139
|
+
def delete!(key, &)
|
140
|
+
table.delete(key.to_sym, &)
|
141
|
+
end
|
142
|
+
|
143
|
+
# Compares this context with another object for equality.
|
144
|
+
#
|
145
|
+
# @param other [Object] the object to compare with
|
146
|
+
#
|
147
|
+
# @return [Boolean] true if other is a Context with the same data
|
148
|
+
#
|
149
|
+
# @example
|
150
|
+
# context1 = Context.new(name: "John")
|
151
|
+
# context2 = Context.new(name: "John")
|
152
|
+
# context1 == context2 # => true
|
153
|
+
def eql?(other)
|
154
|
+
other.is_a?(self.class) && (to_h == other.to_h)
|
155
|
+
end
|
156
|
+
alias == eql?
|
157
|
+
|
158
|
+
# Checks if the context contains a specific key.
|
159
|
+
#
|
160
|
+
# @param key [String, Symbol] the key to check
|
161
|
+
#
|
162
|
+
# @return [Boolean] true if the key exists in the context
|
163
|
+
#
|
164
|
+
# @example
|
165
|
+
# context = Context.new(name: "John")
|
166
|
+
# context.key?(:name) # => true
|
167
|
+
# context.key?(:age) # => false
|
168
|
+
def key?(key)
|
169
|
+
table.key?(key.to_sym)
|
170
|
+
end
|
171
|
+
|
172
|
+
# Digs into nested structures using the given keys.
|
173
|
+
#
|
174
|
+
# @param key [String, Symbol] the first key to dig with
|
175
|
+
# @param keys [Array<String, Symbol>] additional keys for deeper digging
|
176
|
+
#
|
177
|
+
# @return [Object, nil] the value found by digging, or nil if not found
|
178
|
+
#
|
179
|
+
# @example
|
180
|
+
# context = Context.new(user: {profile: {name: "John"}})
|
181
|
+
# context.dig(:user, :profile, :name) # => "John"
|
182
|
+
# context.dig(:user, :profile, :age) # => nil
|
183
|
+
def dig(key, *keys)
|
184
|
+
table.dig(key.to_sym, *keys)
|
185
|
+
end
|
186
|
+
|
187
|
+
# Converts the context to a string representation.
|
188
|
+
#
|
189
|
+
# @return [String] a formatted string representation of the context data
|
190
|
+
#
|
191
|
+
# @example
|
192
|
+
# context = Context.new(name: "John", age: 30)
|
193
|
+
# context.to_s # => "name: John, age: 30"
|
194
|
+
def to_s
|
195
|
+
Utils::Format.to_str(to_h)
|
196
|
+
end
|
197
|
+
|
198
|
+
private
|
199
|
+
|
200
|
+
# Handles method calls that don't match defined methods.
|
201
|
+
# Supports assignment-style calls (e.g., `name=`) and key access.
|
202
|
+
#
|
203
|
+
# @param method_name [Symbol] the method name that was called
|
204
|
+
# @param args [Array<Object>] arguments passed to the method
|
205
|
+
# @param _kwargs [Hash] keyword arguments (unused)
|
206
|
+
#
|
207
|
+
# @yield [Object] optional block
|
208
|
+
#
|
209
|
+
# @return [Object] the result of the method call
|
210
|
+
def method_missing(method_name, *args, **_kwargs, &)
|
211
|
+
fetch(method_name) do
|
212
|
+
store(method_name[0..-2], args.first) if method_name.end_with?("=")
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
# Checks if the object responds to a given method.
|
217
|
+
#
|
218
|
+
# @param method_name [Symbol] the method name to check
|
219
|
+
# @param include_private [Boolean] whether to include private methods
|
220
|
+
#
|
221
|
+
# @return [Boolean] true if the method can be called
|
222
|
+
#
|
223
|
+
# @example
|
224
|
+
# context = Context.new(name: "John")
|
225
|
+
# context.respond_to?(:name) # => true
|
226
|
+
# context.respond_to?(:age) # => false
|
227
|
+
def respond_to_missing?(method_name, include_private = false)
|
228
|
+
key?(method_name) || super
|
51
229
|
end
|
52
230
|
|
53
231
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CMDx
|
4
|
+
# Handles deprecation warnings and restrictions for tasks.
|
5
|
+
#
|
6
|
+
# The Deprecator module provides functionality to restrict usage of deprecated
|
7
|
+
# tasks based on configuration settings. It supports different deprecation
|
8
|
+
# behaviors including warnings, logging, and errors.
|
9
|
+
module Deprecator
|
10
|
+
|
11
|
+
extend self
|
12
|
+
|
13
|
+
EVAL = proc do |target, callable|
|
14
|
+
case callable
|
15
|
+
when /raise|log|warn/ then callable
|
16
|
+
when NilClass, FalseClass, TrueClass then !!callable
|
17
|
+
when Symbol then target.send(callable)
|
18
|
+
when Proc then target.instance_eval(&callable)
|
19
|
+
else
|
20
|
+
raise "cannot evaluate #{callable.inspect}" unless callable.respond_to?(:call)
|
21
|
+
|
22
|
+
callable.call(target)
|
23
|
+
end
|
24
|
+
end.freeze
|
25
|
+
private_constant :EVAL
|
26
|
+
|
27
|
+
# Restricts task usage based on deprecation settings.
|
28
|
+
#
|
29
|
+
# @param task [Object] The task object to check for deprecation
|
30
|
+
# @option task.class.settings[:deprecate] [Symbol, Proc, String, Boolean]
|
31
|
+
# The deprecation configuration for the task
|
32
|
+
# @option task.class.settings[:deprecate] :raise Raises DeprecationError
|
33
|
+
# @option task.class.settings[:deprecate] :log Logs deprecation warning
|
34
|
+
# @option task.class.settings[:deprecate] :warn Outputs warning to stderr
|
35
|
+
# @option task.class.settings[:deprecate] true Raises DeprecationError
|
36
|
+
# @option task.class.settings[:deprecate] false No action taken
|
37
|
+
# @option task.class.settings[:deprecate] nil No action taken
|
38
|
+
#
|
39
|
+
# @raise [DeprecationError] When deprecation type is :raise or true
|
40
|
+
# @raise [RuntimeError] When deprecation type is unknown
|
41
|
+
#
|
42
|
+
# @example
|
43
|
+
# class MyTask
|
44
|
+
# settings(deprecate: :warn)
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# MyTask.new # => [MyTask] DEPRECATED: migrate to replacement or discontinue use
|
48
|
+
def restrict(task)
|
49
|
+
type = EVAL.call(task, task.class.settings[:deprecate])
|
50
|
+
|
51
|
+
case type
|
52
|
+
when NilClass, FalseClass # Do nothing
|
53
|
+
when TrueClass, /raise/ then raise DeprecationError, "#{task.class.name} usage prohibited"
|
54
|
+
when /log/ then task.logger.warn { "DEPRECATED: migrate to replacement or discontinue use" }
|
55
|
+
when /warn/ then warn("[#{task.class.name}] DEPRECATED: migrate to replacement or discontinue use", category: :deprecated)
|
56
|
+
else raise "unknown deprecation type #{type.inspect}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|