cmdx 1.0.1 → 1.1.1

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.
Files changed (170) hide show
  1. checksums.yaml +4 -4
  2. data/.cursor/prompts/docs.md +9 -0
  3. data/.cursor/prompts/rspec.md +21 -0
  4. data/.cursor/prompts/yardoc.md +13 -0
  5. data/.rubocop.yml +2 -0
  6. data/CHANGELOG.md +29 -3
  7. data/README.md +2 -1
  8. data/docs/ai_prompts.md +269 -195
  9. data/docs/basics/call.md +126 -60
  10. data/docs/basics/chain.md +190 -160
  11. data/docs/basics/context.md +242 -154
  12. data/docs/basics/setup.md +302 -32
  13. data/docs/callbacks.md +382 -119
  14. data/docs/configuration.md +211 -49
  15. data/docs/deprecation.md +245 -0
  16. data/docs/getting_started.md +161 -39
  17. data/docs/internationalization.md +590 -70
  18. data/docs/interruptions/exceptions.md +135 -118
  19. data/docs/interruptions/faults.md +152 -127
  20. data/docs/interruptions/halt.md +134 -80
  21. data/docs/logging.md +183 -120
  22. data/docs/middlewares.md +165 -392
  23. data/docs/outcomes/result.md +140 -112
  24. data/docs/outcomes/states.md +134 -99
  25. data/docs/outcomes/statuses.md +204 -146
  26. data/docs/parameters/coercions.md +251 -289
  27. data/docs/parameters/defaults.md +224 -169
  28. data/docs/parameters/definitions.md +289 -141
  29. data/docs/parameters/namespacing.md +250 -161
  30. data/docs/parameters/validations.md +247 -159
  31. data/docs/testing.md +196 -203
  32. data/docs/workflows.md +146 -101
  33. data/lib/cmdx/.DS_Store +0 -0
  34. data/lib/cmdx/callback.rb +39 -55
  35. data/lib/cmdx/callback_registry.rb +80 -73
  36. data/lib/cmdx/chain.rb +65 -122
  37. data/lib/cmdx/chain_inspector.rb +23 -116
  38. data/lib/cmdx/chain_serializer.rb +34 -146
  39. data/lib/cmdx/coercion.rb +57 -0
  40. data/lib/cmdx/coercion_registry.rb +113 -0
  41. data/lib/cmdx/coercions/array.rb +18 -36
  42. data/lib/cmdx/coercions/big_decimal.rb +21 -33
  43. data/lib/cmdx/coercions/boolean.rb +21 -40
  44. data/lib/cmdx/coercions/complex.rb +18 -31
  45. data/lib/cmdx/coercions/date.rb +20 -39
  46. data/lib/cmdx/coercions/date_time.rb +22 -39
  47. data/lib/cmdx/coercions/float.rb +19 -32
  48. data/lib/cmdx/coercions/hash.rb +22 -41
  49. data/lib/cmdx/coercions/integer.rb +20 -33
  50. data/lib/cmdx/coercions/rational.rb +20 -32
  51. data/lib/cmdx/coercions/string.rb +23 -31
  52. data/lib/cmdx/coercions/time.rb +24 -40
  53. data/lib/cmdx/coercions/virtual.rb +14 -31
  54. data/lib/cmdx/configuration.rb +101 -162
  55. data/lib/cmdx/context.rb +34 -166
  56. data/lib/cmdx/core_ext/hash.rb +42 -67
  57. data/lib/cmdx/core_ext/module.rb +35 -79
  58. data/lib/cmdx/core_ext/object.rb +63 -98
  59. data/lib/cmdx/correlator.rb +59 -154
  60. data/lib/cmdx/error.rb +37 -202
  61. data/lib/cmdx/errors.rb +153 -216
  62. data/lib/cmdx/fault.rb +68 -150
  63. data/lib/cmdx/faults.rb +26 -137
  64. data/lib/cmdx/immutator.rb +22 -110
  65. data/lib/cmdx/lazy_struct.rb +110 -186
  66. data/lib/cmdx/log_formatters/json.rb +14 -40
  67. data/lib/cmdx/log_formatters/key_value.rb +14 -40
  68. data/lib/cmdx/log_formatters/line.rb +14 -48
  69. data/lib/cmdx/log_formatters/logstash.rb +14 -57
  70. data/lib/cmdx/log_formatters/pretty_json.rb +14 -50
  71. data/lib/cmdx/log_formatters/pretty_key_value.rb +13 -46
  72. data/lib/cmdx/log_formatters/pretty_line.rb +16 -54
  73. data/lib/cmdx/log_formatters/raw.rb +19 -49
  74. data/lib/cmdx/logger.rb +22 -79
  75. data/lib/cmdx/logger_ansi.rb +31 -72
  76. data/lib/cmdx/logger_serializer.rb +74 -103
  77. data/lib/cmdx/middleware.rb +56 -60
  78. data/lib/cmdx/middleware_registry.rb +82 -77
  79. data/lib/cmdx/middlewares/correlate.rb +41 -226
  80. data/lib/cmdx/middlewares/timeout.rb +46 -185
  81. data/lib/cmdx/parameter.rb +167 -183
  82. data/lib/cmdx/parameter_evaluator.rb +231 -0
  83. data/lib/cmdx/parameter_inspector.rb +37 -55
  84. data/lib/cmdx/parameter_registry.rb +65 -84
  85. data/lib/cmdx/parameter_serializer.rb +32 -76
  86. data/lib/cmdx/railtie.rb +24 -107
  87. data/lib/cmdx/result.rb +254 -259
  88. data/lib/cmdx/result_ansi.rb +28 -80
  89. data/lib/cmdx/result_inspector.rb +34 -70
  90. data/lib/cmdx/result_logger.rb +23 -77
  91. data/lib/cmdx/result_serializer.rb +59 -125
  92. data/lib/cmdx/rspec/matchers.rb +28 -0
  93. data/lib/cmdx/rspec/result_matchers/be_executed.rb +42 -0
  94. data/lib/cmdx/rspec/result_matchers/be_failed_task.rb +94 -0
  95. data/lib/cmdx/rspec/result_matchers/be_skipped_task.rb +94 -0
  96. data/lib/cmdx/rspec/result_matchers/be_state_matchers.rb +59 -0
  97. data/lib/cmdx/rspec/result_matchers/be_status_matchers.rb +57 -0
  98. data/lib/cmdx/rspec/result_matchers/be_successful_task.rb +87 -0
  99. data/lib/cmdx/rspec/result_matchers/have_bad_outcome.rb +51 -0
  100. data/lib/cmdx/rspec/result_matchers/have_caused_failure.rb +58 -0
  101. data/lib/cmdx/rspec/result_matchers/have_chain_index.rb +59 -0
  102. data/lib/cmdx/rspec/result_matchers/have_context.rb +86 -0
  103. data/lib/cmdx/rspec/result_matchers/have_empty_metadata.rb +54 -0
  104. data/lib/cmdx/rspec/result_matchers/have_good_outcome.rb +52 -0
  105. data/lib/cmdx/rspec/result_matchers/have_metadata.rb +114 -0
  106. data/lib/cmdx/rspec/result_matchers/have_preserved_context.rb +66 -0
  107. data/lib/cmdx/rspec/result_matchers/have_received_thrown_failure.rb +64 -0
  108. data/lib/cmdx/rspec/result_matchers/have_runtime.rb +78 -0
  109. data/lib/cmdx/rspec/result_matchers/have_thrown_failure.rb +76 -0
  110. data/lib/cmdx/rspec/task_matchers/be_well_formed_task.rb +62 -0
  111. data/lib/cmdx/rspec/task_matchers/have_callback.rb +85 -0
  112. data/lib/cmdx/rspec/task_matchers/have_cmd_setting.rb +68 -0
  113. data/lib/cmdx/rspec/task_matchers/have_executed_callbacks.rb +92 -0
  114. data/lib/cmdx/rspec/task_matchers/have_middleware.rb +46 -0
  115. data/lib/cmdx/rspec/task_matchers/have_parameter.rb +181 -0
  116. data/lib/cmdx/task.rb +336 -427
  117. data/lib/cmdx/task_deprecator.rb +52 -0
  118. data/lib/cmdx/task_processor.rb +246 -0
  119. data/lib/cmdx/task_serializer.rb +34 -69
  120. data/lib/cmdx/utils/ansi_color.rb +13 -89
  121. data/lib/cmdx/utils/log_timestamp.rb +13 -42
  122. data/lib/cmdx/utils/monotonic_runtime.rb +11 -63
  123. data/lib/cmdx/utils/name_affix.rb +21 -71
  124. data/lib/cmdx/validator.rb +57 -0
  125. data/lib/cmdx/validator_registry.rb +108 -0
  126. data/lib/cmdx/validators/exclusion.rb +55 -94
  127. data/lib/cmdx/validators/format.rb +31 -85
  128. data/lib/cmdx/validators/inclusion.rb +65 -110
  129. data/lib/cmdx/validators/length.rb +117 -133
  130. data/lib/cmdx/validators/numeric.rb +123 -130
  131. data/lib/cmdx/validators/presence.rb +38 -79
  132. data/lib/cmdx/version.rb +1 -7
  133. data/lib/cmdx/workflow.rb +58 -330
  134. data/lib/cmdx.rb +1 -1
  135. data/lib/generators/cmdx/install_generator.rb +14 -31
  136. data/lib/generators/cmdx/task_generator.rb +39 -55
  137. data/lib/generators/cmdx/templates/install.rb +24 -6
  138. data/lib/generators/cmdx/workflow_generator.rb +41 -66
  139. data/lib/locales/ar.yml +0 -1
  140. data/lib/locales/cs.yml +0 -1
  141. data/lib/locales/da.yml +0 -1
  142. data/lib/locales/de.yml +0 -1
  143. data/lib/locales/el.yml +0 -1
  144. data/lib/locales/en.yml +0 -1
  145. data/lib/locales/es.yml +0 -1
  146. data/lib/locales/fi.yml +0 -1
  147. data/lib/locales/fr.yml +0 -1
  148. data/lib/locales/he.yml +0 -1
  149. data/lib/locales/hi.yml +0 -1
  150. data/lib/locales/it.yml +0 -1
  151. data/lib/locales/ja.yml +0 -1
  152. data/lib/locales/ko.yml +0 -1
  153. data/lib/locales/nl.yml +0 -1
  154. data/lib/locales/no.yml +0 -1
  155. data/lib/locales/pl.yml +0 -1
  156. data/lib/locales/pt.yml +0 -1
  157. data/lib/locales/ru.yml +0 -1
  158. data/lib/locales/sv.yml +0 -1
  159. data/lib/locales/th.yml +0 -1
  160. data/lib/locales/tr.yml +0 -1
  161. data/lib/locales/vi.yml +0 -1
  162. data/lib/locales/zh.yml +0 -1
  163. metadata +36 -8
  164. data/lib/cmdx/parameter_validator.rb +0 -81
  165. data/lib/cmdx/parameter_value.rb +0 -244
  166. data/lib/cmdx/parameters_inspector.rb +0 -72
  167. data/lib/cmdx/parameters_serializer.rb +0 -115
  168. data/lib/cmdx/rspec/result_matchers.rb +0 -917
  169. data/lib/cmdx/rspec/task_matchers.rb +0 -570
  170. data/lib/cmdx/validators/custom.rb +0 -102
@@ -2,119 +2,90 @@
2
2
 
3
3
  module CMDx
4
4
 
5
- ##
6
- # Provides global configuration management for CMDx framework settings.
7
- # The configuration system allows customization of default behaviors for tasks,
8
- # workflows, logging, and error handling across the entire application.
5
+ # Global configuration class for CMDx framework settings.
9
6
  #
10
- # Configuration settings are stored as instance variables with explicit accessors
11
- # and can be modified through the configure block pattern. These settings serve
12
- # as defaults that can be overridden at the task or workflow level when needed.
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.
13
12
  #
14
- # ## Available Configuration Options
15
- #
16
- # - **logger**: Logger instance for task execution logging
17
- # - **task_halt**: Result statuses that cause `call!` to raise faults
18
- # - **workflow_halt**: Result statuses that halt workflow execution
19
- # - **middlewares**: Global middleware registry applied to all tasks
20
- # - **callbacks**: Global callback registry applied to all tasks
21
- #
22
- # ## Configuration Hierarchy
23
- #
24
- # CMDx follows a configuration hierarchy where settings can be overridden:
25
- # 1. **Global Configuration**: Framework-wide defaults (this module)
26
- # 2. **Task Settings**: Class-level overrides via `task_settings!`
27
- # 3. **Runtime Parameters**: Instance-specific overrides during execution
28
- #
29
- # @example Basic configuration setup
30
- # CMDx.configure do |config|
31
- # config.logger = Logger.new($stdout)
32
- # config.task_halt = ["failed"] # Only halt on failures
33
- # config.middlewares.use CMDx::Middlewares::Timeout, 30
34
- # end
35
- #
36
- # @example Rails initializer configuration
37
- # # config/initializers/cmdx.rb
38
- # CMDx.configure do |config|
39
- # config.logger = Logger.new($stdout)
40
- # config.task_halt = CMDx::Result::FAILED
41
- # config.workflow_halt = [CMDx::Result::FAILED, CMDx::Result::SKIPPED]
42
- #
43
- # # Add global middlewares
44
- # config.middlewares.use CMDx::Middlewares::Timeout, 30
45
- # config.middlewares.use AuthenticationMiddleware if Rails.env.production?
46
- #
47
- # # Add global callbacks
48
- # config.callbacks.register :before_execution, :log_task_start
49
- # config.callbacks.register :on_success, NotificationCallback.new([:slack])
50
- # config.callbacks.register :on_failure, :alert_admin, if: :production?
51
- # end
52
- #
53
- # @example Custom logger configuration
54
- # CMDx.configure do |config|
55
- # config.logger = Logger.new(
56
- # Rails.root.join('log', 'cmdx.log'),
57
- # formatter: CMDx::LogFormatters::Json.new
58
- # )
59
- # end
60
- #
61
- # @example Environment-specific configuration
62
- # CMDx.configure do |config|
63
- # case Rails.env
64
- # when 'development'
65
- # config.logger = Logger.new($stdout, formatter: CMDx::LogFormatters::PrettyLine.new)
66
- # when 'test'
67
- # config.logger = Logger.new('/dev/null') # Silent logging
68
- # when 'production'
69
- # config.logger = Logger.new($stdout, formatter: CMDx::LogFormatters::Json.new)
70
- # end
71
- # end
72
- #
73
- # @see Task Task-level configuration overrides
74
- # @see Workflow Workflow-level configuration overrides
75
- # @see LogFormatters Available logging formatters
76
- # @see Result Result statuses for halt configuration
77
- # @since 1.0.0
78
-
79
- ##
80
- # Configuration class that manages CMDx framework settings.
81
- # Provides explicit attribute accessors for all configuration options.
82
- #
83
- # @since 1.0.0
13
+ # The configuration system supports both global and per-task customization, allowing
14
+ # fine-grained control over framework behavior while maintaining sensible defaults.
84
15
  class Configuration
85
16
 
86
- # Default configuration values
87
17
  DEFAULT_HALT = "failed"
88
18
 
89
- # Configuration attributes
90
- attr_accessor :logger, :middlewares, :callbacks, :task_halt, :workflow_halt
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
91
27
 
92
- ##
93
- # Initializes a new configuration with default values.
28
+ # @return [CoercionRegistry] Global coercion registry for custom parameter types
29
+ attr_accessor :coercions
30
+
31
+ # @return [ValidatorRegistry] Global validator registry for custom parameter validation
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.
41
+ #
42
+ # Initializes all configuration attributes with sensible defaults including
43
+ # a stdout logger with line formatting, empty registries for extensibility
44
+ # components, and default halt conditions for both tasks and workflows.
94
45
  #
95
- # @example
96
- # config = CMDx::Configuration.new
46
+ # @return [Configuration] a new configuration instance with default settings
47
+ #
48
+ # @example Create a new configuration
49
+ # config = Configuration.new
50
+ # config.logger.class #=> Logger
51
+ # config.task_halt #=> "failed"
97
52
  def initialize
98
- @logger = ::Logger.new($stdout, formatter: CMDx::LogFormatters::Line.new)
99
- @middlewares = MiddlewareRegistry.new
100
- @callbacks = CallbackRegistry.new
101
- @task_halt = DEFAULT_HALT
53
+ @logger = ::Logger.new($stdout, formatter: CMDx::LogFormatters::Line.new)
54
+ @middlewares = MiddlewareRegistry.new
55
+ @callbacks = CallbackRegistry.new
56
+ @coercions = CoercionRegistry.new
57
+ @validators = ValidatorRegistry.new
58
+ @task_halt = DEFAULT_HALT
102
59
  @workflow_halt = DEFAULT_HALT
103
60
  end
104
61
 
105
- ##
106
- # Returns a hash representation of the configuration.
107
- # Used internally by the framework for configuration merging.
62
+ # Converts the configuration to a hash representation.
63
+ #
64
+ # Creates a hash containing all configuration attributes for serialization,
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
108
76
  #
109
- # @return [Hash] configuration attributes as a hash
110
- # @example
111
- # config = CMDx.configuration
112
- # config.to_h #=> { logger: ..., task_halt: "failed", ... }
77
+ # @example Convert configuration to hash
78
+ # config = Configuration.new
79
+ # hash = config.to_h
80
+ # hash[:logger].class #=> Logger
81
+ # hash[:task_halt] #=> "failed"
113
82
  def to_h
114
83
  {
115
84
  logger: @logger,
116
85
  middlewares: @middlewares,
117
86
  callbacks: @callbacks,
87
+ coercions: @coercions,
88
+ validators: @validators,
118
89
  task_halt: @task_halt,
119
90
  workflow_halt: @workflow_halt
120
91
  }
@@ -124,71 +95,48 @@ module CMDx
124
95
 
125
96
  module_function
126
97
 
127
- ##
128
98
  # Returns the current global configuration instance.
129
- # Creates a new configuration with default values if none exists.
130
99
  #
131
- # The configuration is stored as a module-level variable and persists
132
- # throughout the application lifecycle. It uses lazy initialization,
133
- # creating the configuration only when first accessed.
100
+ # Provides access to the singleton configuration instance used by the entire
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.
134
104
  #
135
- # @return [Configuration] the current configuration object
105
+ # @return [Configuration] the current global configuration instance
136
106
  #
137
- # @example Accessing configuration values
138
- # CMDx.configuration.logger #=> <Logger instance>
139
- # CMDx.configuration.task_halt #=> "failed"
140
- #
141
- # @example Checking configuration state
107
+ # @example Access global configuration
142
108
  # config = CMDx.configuration
143
- # config.logger.class #=> Logger
109
+ # config.logger.level = Logger::DEBUG
110
+ # config.task_halt = ["failed", "skipped"]
144
111
  def configuration
145
112
  return @configuration if @configuration
146
113
 
147
114
  @configuration ||= Configuration.new
148
115
  end
149
116
 
150
- ##
151
- # Configures CMDx settings using a block-based DSL.
152
- # This is the preferred method for setting up CMDx configuration
153
- # as it provides a clean, readable syntax for configuration management.
117
+ # Configures the global CMDx settings using a block.
118
+ #
119
+ # Yields the current configuration instance to the provided block for
120
+ # modification. This is the recommended way to configure CMDx as it
121
+ # provides a clean DSL-like interface for setting up the framework.
154
122
  #
155
- # The configuration block yields the current configuration object,
156
- # allowing you to set multiple options in a single, organized block.
123
+ # @param block [Proc] configuration block that receives the configuration instance
124
+ #
125
+ # @return [Configuration] the configured configuration instance
157
126
  #
158
- # @yieldparam config [Configuration] the configuration object to modify
159
- # @return [Configuration] the updated configuration object
160
127
  # @raise [ArgumentError] if no block is provided
161
128
  #
162
- # @example Basic configuration
129
+ # @example Configure CMDx settings
163
130
  # CMDx.configure do |config|
131
+ # config.logger.level = Logger::INFO
164
132
  # config.task_halt = ["failed", "skipped"]
133
+ # config.middlewares.register(CMDx::Middlewares::Timeout.new(seconds: 30))
165
134
  # end
166
135
  #
167
- # @example Complex configuration with conditionals
168
- # CMDx.configure do |config|
169
- # config.logger = Rails.logger if defined?(Rails)
170
- #
171
- # config.task_halt = if Rails.env.production?
172
- # "failed" # Only halt on failures in production
173
- # else
174
- # ["failed", "skipped"] # Halt on both in development
175
- # end
176
- #
177
-
178
- # end
179
- #
180
- # @example Formatter configuration
136
+ # @example Configure with custom logger
181
137
  # CMDx.configure do |config|
182
- # config.logger = Logger.new($stdout).tap do |logger|
183
- # logger.formatter = case ENV['LOG_FORMAT']
184
- # when 'json'
185
- # CMDx::LogFormatters::Json.new
186
- # when 'pretty'
187
- # CMDx::LogFormatters::PrettyLine.new
188
- # else
189
- # CMDx::LogFormatters::Line.new
190
- # end
191
- # end
138
+ # config.logger = Rails.logger
139
+ # config.logger.formatter = CMDx::LogFormatters::JSON.new
192
140
  # end
193
141
  def configure
194
142
  raise ArgumentError, "block required" unless block_given?
@@ -198,34 +146,25 @@ module CMDx
198
146
  config
199
147
  end
200
148
 
201
- ##
202
- # Resets the configuration to default values.
203
- # This method creates a fresh configuration object with framework defaults,
204
- # discarding any previously set custom values.
149
+ # Resets the global configuration to default settings.
150
+ #
151
+ # Creates a new configuration instance with default settings, discarding
152
+ # any existing configuration. This is useful for testing scenarios or
153
+ # when you need to start with a clean configuration state.
205
154
  #
206
- # @return [Configuration] the newly created configuration with default values
155
+ # @return [Configuration] a new configuration instance with default settings
207
156
  #
208
- # @example Resetting configuration
209
- # # After custom configuration
210
- # CMDx.configure { |c| c.task_halt = ["failed"] }
211
- # CMDx.configuration.task_halt #=> ["failed"]
157
+ # @example Reset to defaults
158
+ # CMDx.configure { |c| c.task_halt = ["failed", "skipped"] }
159
+ # CMDx.configuration.task_halt #=> ["failed", "skipped"]
212
160
  #
213
- # # Reset to defaults
214
161
  # CMDx.reset_configuration!
215
- # CMDx.configuration.task_halt #=> "failed"
162
+ # CMDx.configuration.task_halt #=> "failed"
216
163
  #
217
- # @example Testing with clean configuration
218
- # # In test setup
219
- # def setup
220
- # CMDx.reset_configuration! # Start with clean defaults
164
+ # @example Use in test setup
165
+ # RSpec.configure do |config|
166
+ # config.before(:each) { CMDx.reset_configuration! }
221
167
  # end
222
- #
223
- # @example Conditional reset
224
- # # Reset configuration in development for experimentation
225
- # CMDx.reset_configuration! if Rails.env.development?
226
- #
227
- # @note This method is primarily useful for testing or when you need
228
- # to return to a known default state.
229
168
  def reset_configuration!
230
169
  @configuration = Configuration.new
231
170
  end
data/lib/cmdx/context.rb CHANGED
@@ -1,181 +1,49 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CMDx
4
- ##
5
- # Context provides a flexible parameter storage and data passing mechanism for CMDx tasks.
6
- # It extends LazyStruct to offer dynamic attribute access with both hash-style and method-style
7
- # syntax, serving as the primary interface for task input parameters and inter-task communication.
8
- #
9
- # Context objects act as the data container for task execution, holding input parameters,
10
- # intermediate results, and any data that needs to be shared between tasks. They support
11
- # dynamic attribute assignment and provide a convenient API for data manipulation throughout
12
- # the task execution lifecycle.
13
- #
14
- #
15
- # ## Usage Patterns
16
- #
17
- # Context is typically used in three main scenarios:
18
- # 1. **Parameter Input**: Passing initial data to tasks
19
- # 2. **Data Storage**: Storing intermediate results during task execution
20
- # 3. **Task Communication**: Sharing data between multiple tasks
21
- #
22
- # @example Basic parameter input
23
- # class ProcessOrderTask < CMDx::Task
24
- # required :order_id, type: :integer
25
- # optional :notify_customer, type: :boolean, default: true
26
- #
27
- # def call
28
- # context.order = Order.find(order_id)
29
- # context.processed_at = Time.now
30
- #
31
- # if notify_customer
32
- # context.notification_sent = send_notification
33
- # end
34
- # end
35
- # end
36
- #
37
- # result = ProcessOrderTask.call(order_id: 123, notify_customer: false)
38
- # result.context.order #=> <Order id: 123>
39
- # result.context.processed_at #=> 2023-01-01 12:00:00 UTC
40
- # result.context.notification_sent #=> nil
41
- #
42
- # @example Dynamic attribute assignment
43
- # class DataProcessingTask < CMDx::Task
44
- # required :input_data, type: :hash
45
- #
46
- # def call
47
- # # Method-style assignment
48
- # context.processed_data = transform(input_data)
49
- # context.validation_errors = validate(context.processed_data)
50
- #
51
- # # Hash-style assignment
52
- # context[:metadata] = { processed_at: Time.now }
53
- # context["summary"] = generate_summary
54
- #
55
- # # Workflow assignment
56
- # context.merge!(
57
- # status: "complete",
58
- # record_count: context.processed_data.size
59
- # )
60
- # end
61
- # end
62
- #
63
- # @example Inter-task communication
64
- # class OrderProcessingWorkflow < CMDx::Workflow
65
- # def call
66
- # # First task sets up context
67
- # ValidateOrderTask.call(context)
68
- #
69
- # # Subsequent tasks use and modify context
70
- # ProcessPaymentTask.call(context)
71
- # UpdateInventoryTask.call(context)
72
- # SendConfirmationTask.call(context)
73
- # end
74
- # end
75
- #
76
- # # Initial context with order data
77
- # result = OrderProcessingWorkflow.call(
78
- # order_id: 123,
79
- # payment_method: "credit_card",
80
- # customer_email: "customer@example.com"
81
- # )
82
- #
83
- # # Context accumulates data from all tasks
84
- # result.context.order #=> <Order> (from ValidateOrderTask)
85
- # result.context.payment_result #=> <Payment> (from ProcessPaymentTask)
86
- # result.context.inventory_updated #=> true (from UpdateInventoryTask)
87
- # result.context.confirmation_sent #=> true (from SendConfirmationTask)
88
- #
89
- # @example Context passing between tasks
90
- # class ProcessOrderTask < CMDx::Task
91
- # required :order_id, type: :integer
92
- #
93
- # def call
94
- # context.order = Order.find(order_id)
95
- #
96
- # # Pass context to subtasks
97
- # payment_result = ProcessPaymentTask.call(context)
98
- # email_result = SendEmailTask.call(context)
99
- #
100
- # # Results maintain context continuity
101
- # context.payment_processed = payment_result.success?
102
- # context.email_sent = email_result.success?
103
- # end
104
- # end
105
- #
106
- # # After execution, context contains accumulated data
107
- # result = ProcessOrderTask.call(order_id: 123)
108
- # result.context.order #=> <Order>
109
- # result.context.payment_processed #=> true
110
- # result.context.email_sent #=> true
111
- #
112
- # @example Context with nested data structures
113
- # class AnalyticsTask < CMDx::Task
114
- # required :user_id, type: :integer
115
- #
116
- # def call
117
- # context.user = User.find(user_id)
118
- # context.analytics = {
119
- # page_views: calculate_page_views,
120
- # session_duration: calculate_session_duration,
121
- # conversion_rate: calculate_conversion_rate
122
- # }
123
- #
124
- # # Access nested data
125
- # context.dig(:analytics, :page_views) #=> 150
126
- #
127
- # # Add more nested data
128
- # context.analytics[:last_login] = context.user.last_login
129
- # end
130
- # end
131
- #
132
- # @see LazyStruct Base class providing dynamic attribute functionality
133
- # @see Task Task base class that uses Context for parameter storage
134
- # @see Chain Chain execution context that Context belongs to
135
- # @see Parameter Parameter definitions that populate Context
136
- # @since 1.0.0
4
+ # Execution context container for task parameter storage and access.
5
+ #
6
+ # Context provides normalized parameter storage for task execution, inheriting
7
+ # from LazyStruct to provide flexible attribute access patterns. It serves as
8
+ # the primary interface for storing and retrieving execution parameters, allowing
9
+ # both hash-style and method-style attribute access with automatic key normalization.
10
+ # Context instances are used throughout task execution to maintain parameter state
11
+ # and provide consistent data access across the task lifecycle.
137
12
  class Context < LazyStruct
138
13
 
139
- ##
140
- # Builds a Context instance from the given input, with intelligent handling
141
- # of existing Context objects to avoid unnecessary object creation.
14
+ # Creates or returns a Context instance from the provided input.
142
15
  #
143
- # This factory method provides optimized Context creation by:
144
- # - Returning existing Context objects if they're unfrozen (reusable)
145
- # - Creating new Context objects for frozen contexts (immutable)
146
- # - Converting hash-like objects into new Context instances
16
+ # This factory method normalizes various input types into a proper Context instance,
17
+ # ensuring consistent context handling throughout the framework. If the input is
18
+ # already a Context instance and not frozen, it returns the input unchanged to
19
+ # avoid unnecessary object creation. Otherwise, it creates a new Context instance
20
+ # with the provided data.
147
21
  #
148
- # @param context [Hash, Context, #to_h] input data for context creation
149
- # @return [Context] a Context instance ready for task execution
22
+ # @param context [Hash, Context, Object] input data to convert to Context
23
+ # @option context [Object] any any attribute keys and values for context initialization
150
24
  #
151
- # @example Creating context from hash
152
- # context = Context.build(name: "John", age: 30)
153
- # context.name #=> "John"
154
- # context.age #=> 30
25
+ # @return [Context] a Context instance containing the provided data
155
26
  #
156
- # @example Reusing unfrozen context
157
- # original = Context.build(data: "test")
158
- # reused = Context.build(original)
159
- # original.object_id == reused.object_id #=> true
27
+ # @example Create context from hash
28
+ # context = Context.build(user_id: 123, action: "process")
29
+ # context.user_id #=> 123
30
+ # context.action #=> "process"
160
31
  #
161
- # @example Creating new context from frozen context
162
- # original = Context.build(data: "test")
163
- # original.freeze
164
- # new_context = Context.build(original)
165
- # original.object_id == new_context.object_id #=> false
32
+ # @example Return existing unfrozen context
33
+ # existing = Context.new(status: "active")
34
+ # result = Context.build(existing)
35
+ # result.equal?(existing) #=> true
166
36
  #
167
- # @example Converting ActionController::Parameters
168
- # # In Rails controllers
169
- # params = ActionController::Parameters.new(user: { name: "John" })
170
- # context = Context.build(params.permit(:user))
171
- # context.user #=> { name: "John" }
37
+ # @example Create new context from frozen context
38
+ # frozen_context = Context.new(data: "test").freeze
39
+ # new_context = Context.build(frozen_context)
40
+ # new_context.equal?(frozen_context) #=> false
41
+ # new_context.data #=> "test"
172
42
  #
173
- # @example Task execution with built context
174
- # # CMDx automatically uses Context.build for task parameters
175
- # result = ProcessOrderTask.call(order_id: 123, priority: "high")
176
- # # Equivalent to:
177
- # # context = Context.build(order_id: 123, priority: "high")
178
- # # ProcessOrderTask.new(context).call
43
+ # @example Create context from empty input
44
+ # context = Context.build
45
+ # context.class #=> CMDx::Context
46
+ # context.to_h #=> {}
179
47
  def self.build(context = {})
180
48
  return context if context.is_a?(self) && !context.frozen?
181
49