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
@@ -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
|
data/lib/cmdx/task_deprecator.rb
DELETED
@@ -1,58 +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
|
-
type = task.cmd_setting(:deprecated)
|
42
|
-
|
43
|
-
case type
|
44
|
-
when :error
|
45
|
-
raise DeprecationError, "#{task.class.name} usage prohibited"
|
46
|
-
when :log, true
|
47
|
-
task.logger.warn { "DEPRECATED: migrate to replacement or discontinue use" }
|
48
|
-
when :warning
|
49
|
-
warn("[#{task.class.name}] DEPRECATED: migrate to replacement or discontinue use", category: :deprecated)
|
50
|
-
when nil, false
|
51
|
-
# Do nothing
|
52
|
-
else
|
53
|
-
raise UnknownDeprecationError, "unknown deprecation type #{type}"
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
end
|
58
|
-
end
|