cmdx 1.1.1 → 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.
Files changed (193) hide show
  1. checksums.yaml +4 -4
  2. data/.DS_Store +0 -0
  3. data/.cursor/prompts/docs.md +4 -1
  4. data/.cursor/prompts/llms.md +20 -0
  5. data/.cursor/prompts/rspec.md +4 -1
  6. data/.cursor/prompts/yardoc.md +3 -2
  7. data/.cursor/rules/cursor-instructions.mdc +56 -1
  8. data/.irbrc +6 -0
  9. data/.rubocop.yml +29 -18
  10. data/.ruby-version +1 -1
  11. data/CHANGELOG.md +6 -128
  12. data/LLM.md +3317 -0
  13. data/README.md +68 -44
  14. data/docs/attributes/coercions.md +162 -0
  15. data/docs/attributes/defaults.md +90 -0
  16. data/docs/attributes/definitions.md +281 -0
  17. data/docs/attributes/naming.md +78 -0
  18. data/docs/attributes/validations.md +309 -0
  19. data/docs/basics/chain.md +56 -249
  20. data/docs/basics/context.md +56 -289
  21. data/docs/basics/execution.md +114 -0
  22. data/docs/basics/setup.md +37 -334
  23. data/docs/callbacks.md +89 -467
  24. data/docs/deprecation.md +91 -174
  25. data/docs/getting_started.md +212 -202
  26. data/docs/internationalization.md +11 -647
  27. data/docs/interruptions/exceptions.md +23 -198
  28. data/docs/interruptions/faults.md +71 -151
  29. data/docs/interruptions/halt.md +109 -186
  30. data/docs/logging.md +44 -256
  31. data/docs/middlewares.md +113 -426
  32. data/docs/outcomes/result.md +81 -228
  33. data/docs/outcomes/states.md +33 -221
  34. data/docs/outcomes/statuses.md +21 -311
  35. data/docs/tips_and_tricks.md +120 -70
  36. data/docs/workflows.md +99 -283
  37. data/lib/cmdx/.DS_Store +0 -0
  38. data/lib/cmdx/attribute.rb +229 -0
  39. data/lib/cmdx/attribute_registry.rb +94 -0
  40. data/lib/cmdx/attribute_value.rb +193 -0
  41. data/lib/cmdx/callback_registry.rb +69 -77
  42. data/lib/cmdx/chain.rb +56 -73
  43. data/lib/cmdx/coercion_registry.rb +52 -68
  44. data/lib/cmdx/coercions/array.rb +19 -18
  45. data/lib/cmdx/coercions/big_decimal.rb +20 -24
  46. data/lib/cmdx/coercions/boolean.rb +26 -25
  47. data/lib/cmdx/coercions/complex.rb +21 -22
  48. data/lib/cmdx/coercions/date.rb +25 -23
  49. data/lib/cmdx/coercions/date_time.rb +24 -25
  50. data/lib/cmdx/coercions/float.rb +25 -22
  51. data/lib/cmdx/coercions/hash.rb +31 -32
  52. data/lib/cmdx/coercions/integer.rb +30 -24
  53. data/lib/cmdx/coercions/rational.rb +29 -24
  54. data/lib/cmdx/coercions/string.rb +19 -22
  55. data/lib/cmdx/coercions/symbol.rb +37 -0
  56. data/lib/cmdx/coercions/time.rb +26 -25
  57. data/lib/cmdx/configuration.rb +49 -108
  58. data/lib/cmdx/context.rb +222 -44
  59. data/lib/cmdx/deprecator.rb +61 -0
  60. data/lib/cmdx/errors.rb +42 -252
  61. data/lib/cmdx/exceptions.rb +39 -0
  62. data/lib/cmdx/faults.rb +78 -39
  63. data/lib/cmdx/freezer.rb +51 -0
  64. data/lib/cmdx/identifier.rb +30 -0
  65. data/lib/cmdx/locale.rb +52 -0
  66. data/lib/cmdx/log_formatters/json.rb +21 -22
  67. data/lib/cmdx/log_formatters/key_value.rb +20 -22
  68. data/lib/cmdx/log_formatters/line.rb +15 -22
  69. data/lib/cmdx/log_formatters/logstash.rb +22 -23
  70. data/lib/cmdx/log_formatters/raw.rb +16 -22
  71. data/lib/cmdx/middleware_registry.rb +70 -74
  72. data/lib/cmdx/middlewares/correlate.rb +90 -54
  73. data/lib/cmdx/middlewares/runtime.rb +58 -0
  74. data/lib/cmdx/middlewares/timeout.rb +48 -68
  75. data/lib/cmdx/railtie.rb +12 -45
  76. data/lib/cmdx/result.rb +229 -314
  77. data/lib/cmdx/task.rb +194 -366
  78. data/lib/cmdx/utils/call.rb +49 -0
  79. data/lib/cmdx/utils/condition.rb +71 -0
  80. data/lib/cmdx/utils/format.rb +61 -0
  81. data/lib/cmdx/validator_registry.rb +63 -72
  82. data/lib/cmdx/validators/exclusion.rb +38 -67
  83. data/lib/cmdx/validators/format.rb +48 -49
  84. data/lib/cmdx/validators/inclusion.rb +43 -74
  85. data/lib/cmdx/validators/length.rb +91 -154
  86. data/lib/cmdx/validators/numeric.rb +87 -162
  87. data/lib/cmdx/validators/presence.rb +37 -50
  88. data/lib/cmdx/version.rb +1 -1
  89. data/lib/cmdx/worker.rb +178 -0
  90. data/lib/cmdx/workflow.rb +85 -81
  91. data/lib/cmdx.rb +19 -13
  92. data/lib/generators/cmdx/install_generator.rb +14 -13
  93. data/lib/generators/cmdx/task_generator.rb +25 -50
  94. data/lib/generators/cmdx/templates/install.rb +11 -46
  95. data/lib/generators/cmdx/templates/task.rb.tt +3 -2
  96. data/lib/locales/en.yml +18 -4
  97. data/src/cmdx-logo.png +0 -0
  98. metadata +32 -116
  99. data/docs/ai_prompts.md +0 -393
  100. data/docs/basics/call.md +0 -317
  101. data/docs/configuration.md +0 -344
  102. data/docs/parameters/coercions.md +0 -396
  103. data/docs/parameters/defaults.md +0 -335
  104. data/docs/parameters/definitions.md +0 -446
  105. data/docs/parameters/namespacing.md +0 -378
  106. data/docs/parameters/validations.md +0 -405
  107. data/docs/testing.md +0 -553
  108. data/lib/cmdx/callback.rb +0 -53
  109. data/lib/cmdx/chain_inspector.rb +0 -56
  110. data/lib/cmdx/chain_serializer.rb +0 -63
  111. data/lib/cmdx/coercion.rb +0 -57
  112. data/lib/cmdx/coercions/virtual.rb +0 -29
  113. data/lib/cmdx/core_ext/hash.rb +0 -83
  114. data/lib/cmdx/core_ext/module.rb +0 -98
  115. data/lib/cmdx/core_ext/object.rb +0 -125
  116. data/lib/cmdx/correlator.rb +0 -122
  117. data/lib/cmdx/error.rb +0 -60
  118. data/lib/cmdx/fault.rb +0 -140
  119. data/lib/cmdx/immutator.rb +0 -52
  120. data/lib/cmdx/lazy_struct.rb +0 -246
  121. data/lib/cmdx/log_formatters/pretty_json.rb +0 -40
  122. data/lib/cmdx/log_formatters/pretty_key_value.rb +0 -38
  123. data/lib/cmdx/log_formatters/pretty_line.rb +0 -41
  124. data/lib/cmdx/logger.rb +0 -49
  125. data/lib/cmdx/logger_ansi.rb +0 -68
  126. data/lib/cmdx/logger_serializer.rb +0 -116
  127. data/lib/cmdx/middleware.rb +0 -70
  128. data/lib/cmdx/parameter.rb +0 -312
  129. data/lib/cmdx/parameter_evaluator.rb +0 -231
  130. data/lib/cmdx/parameter_inspector.rb +0 -66
  131. data/lib/cmdx/parameter_registry.rb +0 -106
  132. data/lib/cmdx/parameter_serializer.rb +0 -59
  133. data/lib/cmdx/result_ansi.rb +0 -71
  134. data/lib/cmdx/result_inspector.rb +0 -71
  135. data/lib/cmdx/result_logger.rb +0 -59
  136. data/lib/cmdx/result_serializer.rb +0 -104
  137. data/lib/cmdx/rspec/matchers.rb +0 -28
  138. data/lib/cmdx/rspec/result_matchers/be_executed.rb +0 -42
  139. data/lib/cmdx/rspec/result_matchers/be_failed_task.rb +0 -94
  140. data/lib/cmdx/rspec/result_matchers/be_skipped_task.rb +0 -94
  141. data/lib/cmdx/rspec/result_matchers/be_state_matchers.rb +0 -59
  142. data/lib/cmdx/rspec/result_matchers/be_status_matchers.rb +0 -57
  143. data/lib/cmdx/rspec/result_matchers/be_successful_task.rb +0 -87
  144. data/lib/cmdx/rspec/result_matchers/have_bad_outcome.rb +0 -51
  145. data/lib/cmdx/rspec/result_matchers/have_caused_failure.rb +0 -58
  146. data/lib/cmdx/rspec/result_matchers/have_chain_index.rb +0 -59
  147. data/lib/cmdx/rspec/result_matchers/have_context.rb +0 -86
  148. data/lib/cmdx/rspec/result_matchers/have_empty_metadata.rb +0 -54
  149. data/lib/cmdx/rspec/result_matchers/have_good_outcome.rb +0 -52
  150. data/lib/cmdx/rspec/result_matchers/have_metadata.rb +0 -114
  151. data/lib/cmdx/rspec/result_matchers/have_preserved_context.rb +0 -66
  152. data/lib/cmdx/rspec/result_matchers/have_received_thrown_failure.rb +0 -64
  153. data/lib/cmdx/rspec/result_matchers/have_runtime.rb +0 -78
  154. data/lib/cmdx/rspec/result_matchers/have_thrown_failure.rb +0 -76
  155. data/lib/cmdx/rspec/task_matchers/be_well_formed_task.rb +0 -62
  156. data/lib/cmdx/rspec/task_matchers/have_callback.rb +0 -85
  157. data/lib/cmdx/rspec/task_matchers/have_cmd_setting.rb +0 -68
  158. data/lib/cmdx/rspec/task_matchers/have_executed_callbacks.rb +0 -92
  159. data/lib/cmdx/rspec/task_matchers/have_middleware.rb +0 -46
  160. data/lib/cmdx/rspec/task_matchers/have_parameter.rb +0 -181
  161. data/lib/cmdx/task_deprecator.rb +0 -52
  162. data/lib/cmdx/task_processor.rb +0 -246
  163. data/lib/cmdx/task_serializer.rb +0 -57
  164. data/lib/cmdx/utils/ansi_color.rb +0 -73
  165. data/lib/cmdx/utils/log_timestamp.rb +0 -36
  166. data/lib/cmdx/utils/monotonic_runtime.rb +0 -34
  167. data/lib/cmdx/utils/name_affix.rb +0 -52
  168. data/lib/cmdx/validator.rb +0 -57
  169. data/lib/generators/cmdx/templates/workflow.rb.tt +0 -7
  170. data/lib/generators/cmdx/workflow_generator.rb +0 -84
  171. data/lib/locales/ar.yml +0 -35
  172. data/lib/locales/cs.yml +0 -35
  173. data/lib/locales/da.yml +0 -35
  174. data/lib/locales/de.yml +0 -35
  175. data/lib/locales/el.yml +0 -35
  176. data/lib/locales/es.yml +0 -35
  177. data/lib/locales/fi.yml +0 -35
  178. data/lib/locales/fr.yml +0 -35
  179. data/lib/locales/he.yml +0 -35
  180. data/lib/locales/hi.yml +0 -35
  181. data/lib/locales/it.yml +0 -35
  182. data/lib/locales/ja.yml +0 -35
  183. data/lib/locales/ko.yml +0 -35
  184. data/lib/locales/nl.yml +0 -35
  185. data/lib/locales/no.yml +0 -35
  186. data/lib/locales/pl.yml +0 -35
  187. data/lib/locales/pt.yml +0 -35
  188. data/lib/locales/ru.yml +0 -35
  189. data/lib/locales/sv.yml +0 -35
  190. data/lib/locales/th.yml +0 -35
  191. data/lib/locales/tr.yml +0 -35
  192. data/lib/locales/vi.yml +0 -35
  193. data/lib/locales/zh.yml +0 -35
@@ -1,62 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # RSpec matcher for asserting that a task class is well-formed.
4
- #
5
- # This matcher checks if a task class meets all the requirements to be a properly
6
- # structured CMDx::Task. A well-formed task must inherit from CMDx::Task, implement
7
- # the call method, and have properly initialized registries for parameters, callbacks,
8
- # and middlewares. This is essential for ensuring task classes will function correctly
9
- # within the CMDx framework and can be used in workflows.
10
- #
11
- # @return [Boolean] true if the task class is well-formed with all required components
12
- #
13
- # @example Testing a basic task class
14
- # class MyTask < CMDx::Task
15
- # def call; end
16
- # end
17
- # expect(MyTask).to be_well_formed_task
18
- #
19
- # @example Testing a task with parameters, callbacks and middlewares
20
- # class ComplexTask < CMDx::Task
21
- # before_validation :refresh_cache
22
- # use :middleware, CMDx::Middlewares::Timeout, timeout: 10
23
- # required :data
24
- # def call; end
25
- # end
26
- # expect(ComplexTask).to be_well_formed_task
27
- #
28
- # @example Testing generated task classes
29
- # task_class = Class.new(CMDx::Task) { def call; end }
30
- # expect(task_class).to be_well_formed_task
31
- #
32
- # @example Negative assertion for malformed tasks
33
- # class BrokenTask; end # Missing inheritance
34
- # expect(BrokenTask).not_to be_well_formed_task
35
- RSpec::Matchers.define :be_well_formed_task do
36
- match do |task_class|
37
- (task_class < CMDx::Task) &&
38
- task_class.instance_methods.include?(:call) &&
39
- task_class.cmd_parameters.is_a?(CMDx::ParameterRegistry) &&
40
- task_class.cmd_callbacks.is_a?(CMDx::CallbackRegistry) &&
41
- task_class.cmd_middlewares.is_a?(CMDx::MiddlewareRegistry)
42
- end
43
-
44
- failure_message do |task_class|
45
- issues = []
46
- issues << "does not inherit from CMDx::Task" unless task_class < CMDx::Task
47
- issues << "does not implement call method" unless task_class.instance_methods.include?(:call)
48
- issues << "does not have parameter registry" unless task_class.cmd_parameters.is_a?(CMDx::ParameterRegistry)
49
- issues << "does not have callback registry" unless task_class.cmd_callbacks.is_a?(CMDx::CallbackRegistry)
50
- issues << "does not have middleware registry" unless task_class.cmd_middlewares.is_a?(CMDx::MiddlewareRegistry)
51
-
52
- "expected task to be well-formed, but #{issues.join(', ')}"
53
- end
54
-
55
- failure_message_when_negated do |_task_class|
56
- "expected task not to be well-formed, but it was"
57
- end
58
-
59
- description do
60
- "be a well-formed task"
61
- end
62
- end
@@ -1,85 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # RSpec matcher for asserting that a task class has a specific callback.
4
- #
5
- # This matcher checks if a CMDx::Task class has registered a callback with the
6
- # specified name. Callbacks are methods that execute before, after, or around
7
- # the main task logic. The matcher can optionally verify that the callback has
8
- # a specific callable (method name, proc, or lambda) using the `with_callable`
9
- # chain method for more precise callback validation.
10
- #
11
- # @param callback_name [Symbol, String] the name of the callback to check for
12
- #
13
- # @return [Boolean] true if the task has the specified callback and optionally the expected callable
14
- #
15
- # @example Testing basic callback presence
16
- # class MyTask < CMDx::Task
17
- # before_execution :validate_input
18
- # def call; end
19
- # end
20
- # expect(MyTask).to have_callback(:before_execution)
21
- #
22
- # @example Testing callback with specific callable
23
- # class ProcessTask < CMDx::Task
24
- # after_execution :log_completion
25
- # def call; end
26
- # end
27
- # expect(ProcessTask).to have_callback(:after_execution).with_callable(:log_completion)
28
- #
29
- # @example Testing callbacks with procs
30
- # class CustomTask < CMDx::Task
31
- # before_execution -> { puts "Starting" }
32
- # def call; end
33
- # end
34
- # expect(CustomTask).to have_callback(:before_execution)
35
- #
36
- # @example Negative assertion
37
- # class SimpleTask < CMDx::Task
38
- # def call; end
39
- # end
40
- # expect(SimpleTask).not_to have_callback(:before_execution)
41
- RSpec::Matchers.define :have_callback do |callback_name|
42
- match do |task_class|
43
- task_class.cmd_callbacks.registered?(callback_name)
44
- end
45
-
46
- chain :with_callable do |callable|
47
- @expected_callable = callable
48
- end
49
-
50
- match do |task_class|
51
- callbacks_registered = task_class.cmd_callbacks.registered?(callback_name)
52
- return false unless callbacks_registered
53
-
54
- if @expected_callable
55
- task_class.cmd_callbacks.find(callback_name).any? do |callback|
56
- callback.callable == @expected_callable
57
- end
58
- else
59
- true
60
- end
61
- end
62
-
63
- failure_message do |task_class|
64
- if @expected_callable
65
- "expected task to have callback #{callback_name} with callable #{@expected_callable}, but it didn't"
66
- else
67
- registered_callbacks = task_class.cmd_callbacks.registered_callbacks
68
- "expected task to have callback #{callback_name}, but had #{registered_callbacks}"
69
- end
70
- end
71
-
72
- failure_message_when_negated do |_task_class|
73
- if @expected_callable
74
- "expected task not to have callback #{callback_name} with callable #{@expected_callable}, but it did"
75
- else
76
- "expected task not to have callback #{callback_name}, but it did"
77
- end
78
- end
79
-
80
- description do
81
- desc = "have callback #{callback_name}"
82
- desc += " with callable #{@expected_callable}" if @expected_callable
83
- desc
84
- end
85
- end
@@ -1,68 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # RSpec matcher for asserting that a task class has a specific task setting.
4
- #
5
- # This matcher checks if a CMDx::Task class has registered a task setting with the
6
- # specified name. Task settings are configuration options that control task behavior
7
- # such as execution timeouts, retry policies, or custom flags. The matcher can
8
- # optionally verify that the setting has a specific value for more precise validation.
9
- #
10
- # @param setting_name [Symbol, String] the name of the task setting to check for
11
- # @param expected_value [Object, nil] the expected value of the setting (optional)
12
- #
13
- # @return [Boolean] true if the task has the specified setting and optionally the expected value
14
- #
15
- # @example Testing basic task setting presence
16
- # class MyTask < CMDx::Task
17
- # cmd_setting tags: ["admin"]
18
- # def call; end
19
- # end
20
- # expect(MyTask).to have_cmd_setting(:tags)
21
- #
22
- # @example Testing task setting with specific value
23
- # class ProcessTask < CMDx::Task
24
- # cmd_setting tags: ["admin"]
25
- # def call; end
26
- # end
27
- # expect(ProcessTask).to have_cmd_setting(:tags, ["admin"])
28
- #
29
- # @example Negative assertion
30
- # class SimpleTask < CMDx::Task
31
- # def call; end
32
- # end
33
- # expect(SimpleTask).not_to have_cmd_setting(:tags)
34
- RSpec::Matchers.define :have_cmd_setting do |setting_name, expected_value = nil|
35
- match do |task_class|
36
- return false unless task_class.cmd_setting?(setting_name)
37
-
38
- if expected_value
39
- task_class.cmd_setting(setting_name) == expected_value
40
- else
41
- true
42
- end
43
- end
44
-
45
- failure_message do |task_class|
46
- if expected_value
47
- actual_value = task_class.cmd_setting(setting_name)
48
- "expected task to have setting #{setting_name} with value #{expected_value}, but was #{actual_value}"
49
- else
50
- available_settings = task_class.cmd_settings.keys
51
- "expected task to have setting #{setting_name}, but had #{available_settings}"
52
- end
53
- end
54
-
55
- failure_message_when_negated do |_task_class|
56
- if expected_value
57
- "expected task not to have setting #{setting_name} with value #{expected_value}, but it did"
58
- else
59
- "expected task not to have setting #{setting_name}, but it did"
60
- end
61
- end
62
-
63
- description do
64
- desc = "have task setting #{setting_name}"
65
- desc += " with value #{expected_value}" if expected_value
66
- desc
67
- end
68
- end
@@ -1,92 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # RSpec matcher for asserting that a task has executed specific callbacks.
4
- #
5
- # This matcher verifies that callbacks were actually invoked during task execution,
6
- # not just registered. It works by mocking the callback execution to track which
7
- # callbacks are called, then executing the task and checking that the expected
8
- # callbacks were invoked. This is useful for testing that callback logic is properly
9
- # triggered during task execution rather than just checking callback registration.
10
- #
11
- # @param callback_names [Array<Symbol, String>] the names of callbacks expected to execute
12
- #
13
- # @return [Boolean] true if all specified callbacks were executed during task execution
14
- #
15
- # @example Testing basic callback execution
16
- # class MyTask < CMDx::Task
17
- # before_execution :setup
18
- # def call; end
19
- # end
20
- # expect(MyTask.new).to have_executed_callbacks(:before_execution)
21
- #
22
- # @example Testing callback execution with specific callable
23
- # class ProcessTask < CMDx::Task
24
- # after_execution :log_completion
25
- # def call; end
26
- # end
27
- # expect(ProcessTask).to have_callback(:after_execution).with_callable(:log_completion)
28
- #
29
- # @example Testing callback execution with result
30
- # result = MyTask.call(data: "test")
31
- # expect(result).to have_executed_callbacks(:before_execution, :after_execution)
32
- #
33
- # @example Negative assertion
34
- # class SimpleTask < CMDx::Task
35
- # def call; end
36
- # end
37
- # expect(SimpleTask.new).not_to have_executed_callbacks(:before_execution)
38
- RSpec::Matchers.define :have_executed_callbacks do |*callback_names|
39
- match do |task_or_result|
40
- @executed_callbacks = []
41
-
42
- # Mock the callback execution to track what gets called
43
- if task_or_result.is_a?(CMDx::Task)
44
- task = task_or_result
45
- original_callback_call = task.cmd_callbacks.method(:call)
46
-
47
- allow(task.cmd_callbacks).to receive(:call) do |task_instance, callback_name|
48
- @executed_callbacks << callback_name
49
- original_callback_call.call(task_instance, callback_name)
50
- end
51
-
52
- task.process
53
- else
54
- # If it's a result, check if callbacks were executed during task execution
55
- result = task_or_result
56
- # This would require the callbacks to be tracked during execution
57
- # For now, assume callbacks were executed based on result state
58
- @executed_callbacks = infer_executed_callbacks(result)
59
- end
60
-
61
- callback_names.all? { |callback_name| @executed_callbacks.include?(callback_name) }
62
- end
63
-
64
- failure_message do |_task_or_result|
65
- missing_callbacks = callback_names - @executed_callbacks
66
- "expected to execute callbacks #{callback_names}, but missing #{missing_callbacks}. Executed: #{@executed_callbacks}"
67
- end
68
-
69
- failure_message_when_negated do |_task_or_result|
70
- "expected not to execute callbacks #{callback_names}, but executed #{@executed_callbacks & callback_names}"
71
- end
72
-
73
- description do
74
- "execute callbacks #{callback_names}"
75
- end
76
-
77
- private
78
-
79
- def infer_executed_callbacks(result)
80
- callbacks = []
81
- callbacks << :before_validation if result.executed?
82
- callbacks << :after_validation if result.executed?
83
- callbacks << :before_execution if result.executed?
84
- callbacks << :after_execution if result.executed?
85
- callbacks << :on_executed if result.executed?
86
- callbacks << :"on_#{result.status}" if result.executed?
87
- callbacks << :on_good if result.good?
88
- callbacks << :on_bad if result.bad?
89
- callbacks << :"on_#{result.state}" if result.executed?
90
- callbacks
91
- end
92
- end
@@ -1,46 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # RSpec matcher for asserting that a task class has a specific middleware.
4
- #
5
- # This matcher checks if a CMDx::Task class has registered a middleware of the
6
- # specified class. Middlewares are components that wrap around task execution to
7
- # provide cross-cutting concerns like logging, timing, error handling, or other
8
- # aspects. The matcher verifies that the middleware is properly registered and
9
- # available for execution during task performance.
10
- #
11
- # @param middleware_class [Class] the middleware class to check for
12
- #
13
- # @return [Boolean] true if the task has the specified middleware class registered
14
- #
15
- # @example Testing middleware registration
16
- # class MyTask < CMDx::Task
17
- # use :middleware, CMDx::Middlewares::Timeout, timeout: 10
18
- # def call; end
19
- # end
20
- # expect(MyTask).to have_middleware(TimeoutMiddleware)
21
- #
22
- # @example Negative assertion
23
- # class SimpleTask < CMDx::Task
24
- # def call; end
25
- # end
26
- # expect(SimpleTask).not_to have_middleware(TimeoutMiddleware)
27
- RSpec::Matchers.define :have_middleware do |middleware_class|
28
- match do |task_class|
29
- task_class.cmd_middlewares.any? do |middleware|
30
- middleware.is_a?(middleware_class) || middleware.instance_of?(middleware_class)
31
- end
32
- end
33
-
34
- failure_message do |task_class|
35
- middleware_classes = task_class.cmd_middlewares.map(&:class)
36
- "expected task to have middleware #{middleware_class}, but had #{middleware_classes}"
37
- end
38
-
39
- failure_message_when_negated do |_task_class|
40
- "expected task not to have middleware #{middleware_class}, but it did"
41
- end
42
-
43
- description do
44
- "have middleware #{middleware_class}"
45
- end
46
- end
@@ -1,181 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # RSpec matcher for asserting that a task class has a specific parameter.
4
- #
5
- # This matcher checks if a CMDx::Task class has registered a parameter with the
6
- # specified name. Parameters are inputs to task execution that can be required
7
- # or optional, typed with coercions, validated, and have default values. The
8
- # matcher supports various chain methods for precise parameter validation.
9
- #
10
- # @param parameter_name [Symbol, String] the name of the parameter to check for
11
- #
12
- # @return [Boolean] true if the task has the specified parameter and optionally matches all criteria
13
- #
14
- # @example Testing basic parameter presence
15
- # class MyTask < CMDx::Task
16
- # optional :input_file, type: :string
17
- # def call; end
18
- # end
19
- # expect(MyTask).to have_parameter(:input_file)
20
- #
21
- # @example Testing required parameter
22
- # class ProcessTask < CMDx::Task
23
- # required data, type: :string
24
- # def call; end
25
- # end
26
- # expect(ProcessTask).to have_parameter(:data).that_is_required
27
- #
28
- # @example Testing optional parameter with default
29
- # class ConfigTask < CMDx::Task
30
- # optional timeout, type: :integer, default: 30
31
- # def call; end
32
- # end
33
- # expect(ConfigTask).to have_parameter(:timeout).that_is_optional.with_default(30)
34
- #
35
- # @example Testing parameter with type coercion
36
- # class ImportTask < CMDx::Task
37
- # optional csv_file, type: :string
38
- # optional batch_size, type: :integer
39
- # def call; end
40
- # end
41
- # expect(ImportTask).to have_parameter(:csv_file).with_type(:string)
42
- # expect(ImportTask).to have_parameter(:batch_size).with_coercion(:integer)
43
- #
44
- # @example Testing parameter with validations
45
- # class UserTask < CMDx::Task
46
- # optional email, type: :string, format: /@/, presence: true
47
- # def call; end
48
- # end
49
- # expect(UserTask).to have_parameter(:email).with_validations(:format, :presence)
50
- #
51
- # @example Negative assertion
52
- # class SimpleTask < CMDx::Task
53
- # def call; end
54
- # end
55
- # expect(SimpleTask).not_to have_parameter(:nonexistent)
56
- RSpec::Matchers.define :have_parameter do |parameter_name|
57
- match do |task_class|
58
- @parameter = task_class.cmd_parameters.registry.find { |p| p.method_name == parameter_name }
59
- return false unless @parameter
60
-
61
- # Check if parameter exists
62
- parameter_exists = !@parameter.nil?
63
- return false unless parameter_exists
64
-
65
- # Check required/optional if specified
66
- unless @expected_required.nil?
67
- required_matches = @parameter.required? == @expected_required
68
- return false unless required_matches
69
- end
70
-
71
- # Check type/coercion if specified
72
- if @expected_type
73
- type_matches = @parameter.type == @expected_type
74
- return false unless type_matches
75
- end
76
-
77
- # Check validations if specified
78
- if @expected_validations&.any?
79
- validations_match = @expected_validations.all? do |validation_type|
80
- @parameter.options.key?(validation_type)
81
- end
82
- return false unless validations_match
83
- end
84
-
85
- # Check default value if specified
86
- if @expected_default_value != :__not_specified__
87
- default_matches = @parameter.options[:default] == @expected_default_value
88
- return false unless default_matches
89
- end
90
-
91
- true
92
- end
93
-
94
- chain :that_is_required do
95
- @expected_required = true
96
- end
97
-
98
- chain :that_is_optional do
99
- @expected_required = false
100
- end
101
-
102
- chain :with_type do |type|
103
- @expected_type = type
104
- end
105
-
106
- chain :with_coercion do |type|
107
- @expected_type = type
108
- end
109
-
110
- chain :with_validations do |*validations|
111
- @expected_validations = validations
112
- end
113
-
114
- chain :with_validation do |validation|
115
- @expected_validations = [@expected_validations, validation].flatten.compact
116
- end
117
-
118
- chain :with_default do |default_value|
119
- @expected_default_value = default_value
120
- end
121
-
122
- define_method :initialize do |parameter_name|
123
- @parameter_name = parameter_name
124
- @expected_required = nil
125
- @expected_type = nil
126
- @expected_validations = []
127
- @expected_default_value = :__not_specified__
128
- end
129
-
130
- failure_message do |task_class|
131
- if @parameter.nil?
132
- available_parameters = task_class.cmd_parameters.registry.map(&:method_name)
133
- "expected task to have parameter #{@parameter_name}, but had parameters: #{available_parameters}"
134
- else
135
- issues = []
136
-
137
- if !@expected_required.nil? && @parameter.required? != @expected_required
138
- expected_req_text = @expected_required ? "required" : "optional"
139
- actual_req_text = @parameter.required? ? "required" : "optional"
140
- issues << "expected parameter to be #{expected_req_text}, but was #{actual_req_text}"
141
- end
142
-
143
- if @expected_type
144
- actual_type = @parameter.type
145
- issues << "expected parameter type to be #{@expected_type}, but was #{actual_type}" unless actual_type == @expected_type
146
- end
147
-
148
- if @expected_validations&.any?
149
- missing_validations = @expected_validations.reject do |validation_type|
150
- @parameter.options.key?(validation_type)
151
- end
152
-
153
- if missing_validations.any?
154
- actual_validations = @parameter.options.keys
155
- issues << "expected parameter to have validations #{missing_validations}, but had #{actual_validations}"
156
- end
157
- end
158
-
159
- issues << "expected parameter default to be #{@expected_default_value}, but was #{@parameter.options[:default]}" if (@expected_default_value != :__not_specified__) && @parameter.options[:default] != @expected_default_value
160
-
161
- if issues.any?
162
- "expected parameter #{@parameter_name} to match criteria, but #{issues.join(', ')}"
163
- else
164
- "expected parameter #{@parameter_name} to match all criteria, but something didn't match"
165
- end
166
- end
167
- end
168
-
169
- failure_message_when_negated do |_task_class|
170
- "expected task not to have parameter #{@parameter_name}, but it did"
171
- end
172
-
173
- description do
174
- desc = "have parameter #{@parameter_name}"
175
- desc += " that is #{@expected_required ? 'required' : 'optional'}" unless @expected_required.nil?
176
- desc += " with type #{@expected_type}" if @expected_type
177
- desc += " with validations #{@expected_validations}" if @expected_validations&.any?
178
- desc += " with default #{@expected_default_value}" if @expected_default_value != :__not_specified__
179
- desc
180
- end
181
- end
@@ -1,52 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module CMDx
4
- # Task deprecation system for CMDx tasks.
5
- #
6
- # This module provides a centralized system for handling task deprecation
7
- # warnings and errors. It supports multiple deprecation modes including
8
- # raising exceptions, logging warnings, or issuing Ruby warnings based
9
- # on task configuration settings.
10
- module TaskDeprecator
11
-
12
- module_function
13
-
14
- # Processes task deprecation based on the task's deprecated setting.
15
- #
16
- # @param task [CMDx::Task] the task instance to check for deprecation
17
- # @return [void]
18
- # @raise [DeprecationError] when task's deprecated setting is :error
19
- #
20
- # @example Handle task with raise deprecation
21
- # class MyTask < CMDx::Task
22
- # cmd_setting!(deprecated: :error)
23
- # end
24
- # task = MyTask.new
25
- # TaskDeprecator.call(task) # raises DeprecationError
26
- #
27
- # @example Handle task with log deprecation
28
- # class MyTask < CMDx::Task
29
- # cmd_setting!(deprecated: :log)
30
- # end
31
- # task = MyTask.new
32
- # TaskDeprecator.call(task) # logs warning via task.logger
33
- #
34
- # @example Handle task with warn deprecation
35
- # class MyTask < CMDx::Task
36
- # cmd_setting!(deprecated: :warning)
37
- # end
38
- # task = MyTask.new
39
- # TaskDeprecator.call(task) # issues Ruby warning
40
- def call(task)
41
- case task.cmd_setting(:deprecated)
42
- when :error
43
- raise(DeprecationError, "#{task.class.name} usage prohibited")
44
- when :log, true
45
- task.logger.warn { "DEPRECATED: migrate to replacement or discontinue use" }
46
- when :warning
47
- warn("[#{task.class.name}] DEPRECATED: migrate to replacement or discontinue use", category: :deprecated)
48
- end
49
- end
50
-
51
- end
52
- end