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.
- checksums.yaml +4 -4
- data/.cursor/prompts/docs.md +9 -0
- data/.cursor/prompts/rspec.md +21 -0
- data/.cursor/prompts/yardoc.md +13 -0
- data/.rubocop.yml +2 -0
- data/CHANGELOG.md +29 -3
- data/README.md +2 -1
- data/docs/ai_prompts.md +269 -195
- data/docs/basics/call.md +126 -60
- data/docs/basics/chain.md +190 -160
- data/docs/basics/context.md +242 -154
- data/docs/basics/setup.md +302 -32
- data/docs/callbacks.md +382 -119
- data/docs/configuration.md +211 -49
- data/docs/deprecation.md +245 -0
- data/docs/getting_started.md +161 -39
- data/docs/internationalization.md +590 -70
- data/docs/interruptions/exceptions.md +135 -118
- data/docs/interruptions/faults.md +152 -127
- data/docs/interruptions/halt.md +134 -80
- data/docs/logging.md +183 -120
- data/docs/middlewares.md +165 -392
- data/docs/outcomes/result.md +140 -112
- data/docs/outcomes/states.md +134 -99
- data/docs/outcomes/statuses.md +204 -146
- data/docs/parameters/coercions.md +251 -289
- data/docs/parameters/defaults.md +224 -169
- data/docs/parameters/definitions.md +289 -141
- data/docs/parameters/namespacing.md +250 -161
- data/docs/parameters/validations.md +247 -159
- data/docs/testing.md +196 -203
- data/docs/workflows.md +146 -101
- data/lib/cmdx/.DS_Store +0 -0
- data/lib/cmdx/callback.rb +39 -55
- data/lib/cmdx/callback_registry.rb +80 -73
- data/lib/cmdx/chain.rb +65 -122
- data/lib/cmdx/chain_inspector.rb +23 -116
- data/lib/cmdx/chain_serializer.rb +34 -146
- data/lib/cmdx/coercion.rb +57 -0
- data/lib/cmdx/coercion_registry.rb +113 -0
- data/lib/cmdx/coercions/array.rb +18 -36
- data/lib/cmdx/coercions/big_decimal.rb +21 -33
- data/lib/cmdx/coercions/boolean.rb +21 -40
- data/lib/cmdx/coercions/complex.rb +18 -31
- data/lib/cmdx/coercions/date.rb +20 -39
- data/lib/cmdx/coercions/date_time.rb +22 -39
- data/lib/cmdx/coercions/float.rb +19 -32
- data/lib/cmdx/coercions/hash.rb +22 -41
- data/lib/cmdx/coercions/integer.rb +20 -33
- data/lib/cmdx/coercions/rational.rb +20 -32
- data/lib/cmdx/coercions/string.rb +23 -31
- data/lib/cmdx/coercions/time.rb +24 -40
- data/lib/cmdx/coercions/virtual.rb +14 -31
- data/lib/cmdx/configuration.rb +101 -162
- data/lib/cmdx/context.rb +34 -166
- data/lib/cmdx/core_ext/hash.rb +42 -67
- data/lib/cmdx/core_ext/module.rb +35 -79
- data/lib/cmdx/core_ext/object.rb +63 -98
- data/lib/cmdx/correlator.rb +59 -154
- data/lib/cmdx/error.rb +37 -202
- data/lib/cmdx/errors.rb +153 -216
- data/lib/cmdx/fault.rb +68 -150
- data/lib/cmdx/faults.rb +26 -137
- data/lib/cmdx/immutator.rb +22 -110
- data/lib/cmdx/lazy_struct.rb +110 -186
- data/lib/cmdx/log_formatters/json.rb +14 -40
- data/lib/cmdx/log_formatters/key_value.rb +14 -40
- data/lib/cmdx/log_formatters/line.rb +14 -48
- data/lib/cmdx/log_formatters/logstash.rb +14 -57
- data/lib/cmdx/log_formatters/pretty_json.rb +14 -50
- data/lib/cmdx/log_formatters/pretty_key_value.rb +13 -46
- data/lib/cmdx/log_formatters/pretty_line.rb +16 -54
- data/lib/cmdx/log_formatters/raw.rb +19 -49
- data/lib/cmdx/logger.rb +22 -79
- data/lib/cmdx/logger_ansi.rb +31 -72
- data/lib/cmdx/logger_serializer.rb +74 -103
- data/lib/cmdx/middleware.rb +56 -60
- data/lib/cmdx/middleware_registry.rb +82 -77
- data/lib/cmdx/middlewares/correlate.rb +41 -226
- data/lib/cmdx/middlewares/timeout.rb +46 -185
- data/lib/cmdx/parameter.rb +167 -183
- data/lib/cmdx/parameter_evaluator.rb +231 -0
- data/lib/cmdx/parameter_inspector.rb +37 -55
- data/lib/cmdx/parameter_registry.rb +65 -84
- data/lib/cmdx/parameter_serializer.rb +32 -76
- data/lib/cmdx/railtie.rb +24 -107
- data/lib/cmdx/result.rb +254 -259
- data/lib/cmdx/result_ansi.rb +28 -80
- data/lib/cmdx/result_inspector.rb +34 -70
- data/lib/cmdx/result_logger.rb +23 -77
- data/lib/cmdx/result_serializer.rb +59 -125
- data/lib/cmdx/rspec/matchers.rb +28 -0
- data/lib/cmdx/rspec/result_matchers/be_executed.rb +42 -0
- data/lib/cmdx/rspec/result_matchers/be_failed_task.rb +94 -0
- data/lib/cmdx/rspec/result_matchers/be_skipped_task.rb +94 -0
- data/lib/cmdx/rspec/result_matchers/be_state_matchers.rb +59 -0
- data/lib/cmdx/rspec/result_matchers/be_status_matchers.rb +57 -0
- data/lib/cmdx/rspec/result_matchers/be_successful_task.rb +87 -0
- data/lib/cmdx/rspec/result_matchers/have_bad_outcome.rb +51 -0
- data/lib/cmdx/rspec/result_matchers/have_caused_failure.rb +58 -0
- data/lib/cmdx/rspec/result_matchers/have_chain_index.rb +59 -0
- data/lib/cmdx/rspec/result_matchers/have_context.rb +86 -0
- data/lib/cmdx/rspec/result_matchers/have_empty_metadata.rb +54 -0
- data/lib/cmdx/rspec/result_matchers/have_good_outcome.rb +52 -0
- data/lib/cmdx/rspec/result_matchers/have_metadata.rb +114 -0
- data/lib/cmdx/rspec/result_matchers/have_preserved_context.rb +66 -0
- data/lib/cmdx/rspec/result_matchers/have_received_thrown_failure.rb +64 -0
- data/lib/cmdx/rspec/result_matchers/have_runtime.rb +78 -0
- data/lib/cmdx/rspec/result_matchers/have_thrown_failure.rb +76 -0
- data/lib/cmdx/rspec/task_matchers/be_well_formed_task.rb +62 -0
- data/lib/cmdx/rspec/task_matchers/have_callback.rb +85 -0
- data/lib/cmdx/rspec/task_matchers/have_cmd_setting.rb +68 -0
- data/lib/cmdx/rspec/task_matchers/have_executed_callbacks.rb +92 -0
- data/lib/cmdx/rspec/task_matchers/have_middleware.rb +46 -0
- data/lib/cmdx/rspec/task_matchers/have_parameter.rb +181 -0
- data/lib/cmdx/task.rb +336 -427
- data/lib/cmdx/task_deprecator.rb +52 -0
- data/lib/cmdx/task_processor.rb +246 -0
- data/lib/cmdx/task_serializer.rb +34 -69
- data/lib/cmdx/utils/ansi_color.rb +13 -89
- data/lib/cmdx/utils/log_timestamp.rb +13 -42
- data/lib/cmdx/utils/monotonic_runtime.rb +11 -63
- data/lib/cmdx/utils/name_affix.rb +21 -71
- data/lib/cmdx/validator.rb +57 -0
- data/lib/cmdx/validator_registry.rb +108 -0
- data/lib/cmdx/validators/exclusion.rb +55 -94
- data/lib/cmdx/validators/format.rb +31 -85
- data/lib/cmdx/validators/inclusion.rb +65 -110
- data/lib/cmdx/validators/length.rb +117 -133
- data/lib/cmdx/validators/numeric.rb +123 -130
- data/lib/cmdx/validators/presence.rb +38 -79
- data/lib/cmdx/version.rb +1 -7
- data/lib/cmdx/workflow.rb +58 -330
- data/lib/cmdx.rb +1 -1
- data/lib/generators/cmdx/install_generator.rb +14 -31
- data/lib/generators/cmdx/task_generator.rb +39 -55
- data/lib/generators/cmdx/templates/install.rb +24 -6
- data/lib/generators/cmdx/workflow_generator.rb +41 -66
- data/lib/locales/ar.yml +0 -1
- data/lib/locales/cs.yml +0 -1
- data/lib/locales/da.yml +0 -1
- data/lib/locales/de.yml +0 -1
- data/lib/locales/el.yml +0 -1
- data/lib/locales/en.yml +0 -1
- data/lib/locales/es.yml +0 -1
- data/lib/locales/fi.yml +0 -1
- data/lib/locales/fr.yml +0 -1
- data/lib/locales/he.yml +0 -1
- data/lib/locales/hi.yml +0 -1
- data/lib/locales/it.yml +0 -1
- data/lib/locales/ja.yml +0 -1
- data/lib/locales/ko.yml +0 -1
- data/lib/locales/nl.yml +0 -1
- data/lib/locales/no.yml +0 -1
- data/lib/locales/pl.yml +0 -1
- data/lib/locales/pt.yml +0 -1
- data/lib/locales/ru.yml +0 -1
- data/lib/locales/sv.yml +0 -1
- data/lib/locales/th.yml +0 -1
- data/lib/locales/tr.yml +0 -1
- data/lib/locales/vi.yml +0 -1
- data/lib/locales/zh.yml +0 -1
- metadata +36 -8
- data/lib/cmdx/parameter_validator.rb +0 -81
- data/lib/cmdx/parameter_value.rb +0 -244
- data/lib/cmdx/parameters_inspector.rb +0 -72
- data/lib/cmdx/parameters_serializer.rb +0 -115
- data/lib/cmdx/rspec/result_matchers.rb +0 -917
- data/lib/cmdx/rspec/task_matchers.rb +0 -570
- data/lib/cmdx/validators/custom.rb +0 -102
data/lib/cmdx/core_ext/hash.rb
CHANGED
@@ -2,51 +2,28 @@
|
|
2
2
|
|
3
3
|
module CMDx
|
4
4
|
module CoreExt
|
5
|
-
# Extensions
|
6
|
-
#
|
7
|
-
#
|
8
|
-
# string and symbol keys interchangeably. These methods are prefixed
|
9
|
-
# with `__cmdx_` to avoid conflicts with existing Hash methods.
|
10
|
-
#
|
11
|
-
# @example Flexible key access
|
12
|
-
# hash = {name: "John", "age" => 30}
|
13
|
-
# hash.__cmdx_fetch(:name) # => "John" (symbol key)
|
14
|
-
# hash.__cmdx_fetch("name") # => "John" (tries symbol fallback)
|
15
|
-
# hash.__cmdx_fetch(:age) # => 30 (string fallback)
|
16
|
-
#
|
17
|
-
# @example Key checking
|
18
|
-
# hash.__cmdx_key?(:name) # => true (checks both symbol and string)
|
19
|
-
# hash.__cmdx_key?("age") # => true (checks both string and symbol)
|
20
|
-
#
|
21
|
-
# @example Method response checking
|
22
|
-
# hash.__cmdx_respond_to?(:name) # => true (considers key as method)
|
23
|
-
#
|
24
|
-
# @see Context Context objects that use hash extensions
|
25
|
-
# @see LazyStruct Structs that leverage hash-like behavior
|
5
|
+
# Extensions for Ruby's Hash class that provide flexible key access and querying.
|
6
|
+
# These extensions are automatically included in all hashes when CMDx is loaded, providing
|
7
|
+
# seamless symbol/string key interoperability and enhanced key existence checking.
|
26
8
|
module HashExtensions
|
27
9
|
|
28
|
-
#
|
10
|
+
# Fetches a value from the hash with flexible key matching.
|
11
|
+
# Tries the exact key first, then attempts symbol/string conversion if not found.
|
29
12
|
#
|
30
|
-
#
|
31
|
-
# key and its converted form (symbol to string or string to symbol).
|
32
|
-
# This is particularly useful for parameter hashes that might use
|
33
|
-
# either format.
|
13
|
+
# @param key [Symbol, String, Object] the key to fetch from the hash
|
34
14
|
#
|
35
|
-
# @
|
36
|
-
# @return [Object] value for the key or its converted equivalent
|
15
|
+
# @return [Object, nil] the value associated with the key, or nil if not found
|
37
16
|
#
|
38
|
-
# @example
|
39
|
-
# hash = {
|
40
|
-
# hash.
|
17
|
+
# @example Fetch with symbol key
|
18
|
+
# hash = { name: "John", "age" => 30 }
|
19
|
+
# hash.cmdx_fetch(:name) #=> "John"
|
20
|
+
# hash.cmdx_fetch(:age) #=> 30
|
41
21
|
#
|
42
|
-
# @example
|
43
|
-
# hash = {name: "John"}
|
44
|
-
# hash.
|
45
|
-
#
|
46
|
-
|
47
|
-
# hash = {id: 123}
|
48
|
-
# hash.__cmdx_fetch(:id) # => 123 (direct match)
|
49
|
-
def __cmdx_fetch(key)
|
22
|
+
# @example Fetch with string key
|
23
|
+
# hash = { name: "John", "age" => 30 }
|
24
|
+
# hash.cmdx_fetch("name") #=> "John"
|
25
|
+
# hash.cmdx_fetch("age") #=> 30
|
26
|
+
def cmdx_fetch(key)
|
50
27
|
case key
|
51
28
|
when Symbol then fetch(key) { self[key.to_s] }
|
52
29
|
when String then fetch(key) { self[key.to_sym] }
|
@@ -54,21 +31,21 @@ module CMDx
|
|
54
31
|
end
|
55
32
|
end
|
56
33
|
|
57
|
-
#
|
34
|
+
# Checks if a key exists in the hash with flexible key matching.
|
35
|
+
# Tries the exact key first, then attempts symbol/string conversion.
|
58
36
|
#
|
59
|
-
#
|
60
|
-
# key and its converted form. Returns true if either variant exists.
|
37
|
+
# @param key [Symbol, String, Object] the key to check for existence
|
61
38
|
#
|
62
|
-
# @
|
63
|
-
# @return [Boolean] true if key exists in either format
|
39
|
+
# @return [Boolean] true if the key exists (in any form), false otherwise
|
64
40
|
#
|
65
|
-
# @example
|
66
|
-
# hash = {name: "John", "age" => 30}
|
67
|
-
# hash.
|
68
|
-
# hash.
|
69
|
-
# hash.
|
70
|
-
# hash.
|
71
|
-
|
41
|
+
# @example Check key existence
|
42
|
+
# hash = { name: "John", "age" => 30 }
|
43
|
+
# hash.cmdx_key?(:name) #=> true
|
44
|
+
# hash.cmdx_key?("name") #=> true
|
45
|
+
# hash.cmdx_key?(:age) #=> true
|
46
|
+
# hash.cmdx_key?("age") #=> true
|
47
|
+
# hash.cmdx_key?(:missing) #=> false
|
48
|
+
def cmdx_key?(key)
|
72
49
|
key?(key) || key?(
|
73
50
|
case key
|
74
51
|
when Symbol then key.to_s
|
@@ -79,30 +56,28 @@ module CMDx
|
|
79
56
|
false
|
80
57
|
end
|
81
58
|
|
82
|
-
#
|
59
|
+
# Checks if the hash responds to a method or contains a key.
|
60
|
+
# Combines method existence checking with flexible key existence checking.
|
83
61
|
#
|
84
|
-
#
|
85
|
-
#
|
86
|
-
# hash keys to be treated as virtual methods.
|
62
|
+
# @param key [Symbol, String] the method name or key to check
|
63
|
+
# @param include_private [Boolean] whether to include private methods in the check
|
87
64
|
#
|
88
|
-
# @
|
89
|
-
# @param include_private [Boolean] whether to include private methods
|
90
|
-
# @return [Boolean] true if responds to method or contains key
|
65
|
+
# @return [Boolean] true if the hash responds to the method or contains the key
|
91
66
|
#
|
92
|
-
# @example
|
93
|
-
# hash = {name: "John"}
|
94
|
-
# hash.
|
95
|
-
# hash.
|
96
|
-
# hash.
|
97
|
-
|
98
|
-
|
67
|
+
# @example Check method or key response
|
68
|
+
# hash = { name: "John", "age" => 30 }
|
69
|
+
# hash.cmdx_respond_to?(:keys) #=> true (method exists)
|
70
|
+
# hash.cmdx_respond_to?(:name) #=> true (key exists)
|
71
|
+
# hash.cmdx_respond_to?("age") #=> true (key exists)
|
72
|
+
# hash.cmdx_respond_to?(:missing) #=> false
|
73
|
+
def cmdx_respond_to?(key, include_private = false)
|
74
|
+
respond_to?(key.to_sym, include_private) || cmdx_key?(key)
|
99
75
|
rescue NoMethodError
|
100
|
-
|
76
|
+
cmdx_key?(key)
|
101
77
|
end
|
102
78
|
|
103
79
|
end
|
104
80
|
end
|
105
81
|
end
|
106
82
|
|
107
|
-
# Extend all hashes with CMDx utility methods
|
108
83
|
Hash.include(CMDx::CoreExt::HashExtensions)
|
data/lib/cmdx/core_ext/module.rb
CHANGED
@@ -2,65 +2,39 @@
|
|
2
2
|
|
3
3
|
module CMDx
|
4
4
|
module CoreExt
|
5
|
-
# Extensions
|
6
|
-
#
|
7
|
-
# ModuleExtensions adds method delegation and attribute setting functionality
|
8
|
-
# used throughout the CMDx framework. These methods enable declarative
|
9
|
-
# programming patterns and automatic method generation.
|
10
|
-
#
|
11
|
-
# @example Method delegation
|
12
|
-
# class Task
|
13
|
-
# __cmdx_attr_delegator :name, :email, to: :user
|
14
|
-
# __cmdx_attr_delegator :save, to: :record, private: true
|
15
|
-
# end
|
16
|
-
#
|
17
|
-
# @example Attribute settings
|
18
|
-
# class Task
|
19
|
-
# __cmdx_attr_setting :default_options, default: -> { {} }
|
20
|
-
# __cmdx_attr_setting :configuration, default: {}
|
21
|
-
# end
|
22
|
-
#
|
23
|
-
# @see Task Tasks that use module extensions for delegation
|
24
|
-
# @see Parameter Parameters that use attribute settings
|
5
|
+
# Extensions for Ruby's Module class that provide attribute delegation and settings functionality.
|
6
|
+
# These extensions are automatically included in all modules when CMDx is loaded.
|
25
7
|
module ModuleExtensions
|
26
8
|
|
27
|
-
#
|
9
|
+
# Creates delegated methods that forward calls to another object or class.
|
10
|
+
# Supports method name prefixing, privacy levels, and optional method existence checking.
|
28
11
|
#
|
29
|
-
#
|
30
|
-
# another object. It supports method visibility controls and optional
|
31
|
-
# missing method handling.
|
32
|
-
#
|
33
|
-
# @param methods [Array<Symbol>] method names to delegate
|
12
|
+
# @param methods [Array<Symbol>] the method names to delegate
|
34
13
|
# @param options [Hash] delegation options
|
35
|
-
# @option options [Symbol] :to target object
|
36
|
-
# @option options [Boolean] :allow_missing whether to allow
|
37
|
-
# @option options [Boolean] :
|
38
|
-
# @option options [Boolean] :
|
39
|
-
# @
|
14
|
+
# @option options [Symbol] :to the target object or :class to delegate to
|
15
|
+
# @option options [Boolean] :allow_missing (false) whether to allow delegation to non-existent methods
|
16
|
+
# @option options [Boolean] :protected (false) whether to make the delegated method protected
|
17
|
+
# @option options [Boolean] :private (false) whether to make the delegated method private
|
18
|
+
# @option options [String, Symbol] :prefix optional prefix for the delegated method name
|
19
|
+
# @option options [String, Symbol] :suffix optional suffix for the delegated method name
|
40
20
|
#
|
41
|
-
# @
|
42
|
-
#
|
43
|
-
# __cmdx_attr_delegator :first_name, :last_name, to: :profile
|
44
|
-
# # Creates: def first_name; profile.first_name; end
|
45
|
-
# end
|
21
|
+
# @return [void]
|
22
|
+
# @raise [NoMethodError] when delegating to a non-existent method and :allow_missing is false
|
46
23
|
#
|
47
|
-
# @example
|
24
|
+
# @example Delegate methods to an instance variable
|
48
25
|
# class Task
|
49
|
-
#
|
50
|
-
#
|
26
|
+
# def initialize
|
27
|
+
# @logger = Logger.new
|
28
|
+
# end
|
51
29
|
#
|
52
|
-
#
|
53
|
-
# class Task
|
54
|
-
# __cmdx_attr_delegator :configuration, to: :class
|
30
|
+
# cmdx_attr_delegator :info, :warn, :error, to: :@logger
|
55
31
|
# end
|
56
32
|
#
|
57
|
-
# @example
|
58
|
-
# class
|
59
|
-
#
|
33
|
+
# @example Delegate with prefix and privacy
|
34
|
+
# class Workflow
|
35
|
+
# cmdx_attr_delegator :perform, to: :task, prefix: 'execute_', private: true
|
60
36
|
# end
|
61
|
-
|
62
|
-
# @raise [NoMethodError] if target object doesn't respond to method and allow_missing is false
|
63
|
-
def __cmdx_attr_delegator(*methods, **options)
|
37
|
+
def cmdx_attr_delegator(*methods, **options)
|
64
38
|
methods.each do |method|
|
65
39
|
method_name = Utils::NameAffix.call(method, options.fetch(:to), options)
|
66
40
|
|
@@ -83,53 +57,36 @@ module CMDx
|
|
83
57
|
end
|
84
58
|
end
|
85
59
|
|
86
|
-
#
|
60
|
+
# Creates a singleton method for accessing inheritable settings with caching and default values.
|
61
|
+
# Settings are inherited from superclass and can have default values via blocks or static values.
|
87
62
|
#
|
88
|
-
#
|
89
|
-
#
|
90
|
-
#
|
63
|
+
# @param method [Symbol] the name of the setting method to create
|
64
|
+
# @param options [Hash] setting options
|
65
|
+
# @option options [Object, Proc] :default the default value or a proc that returns the default value
|
91
66
|
#
|
92
|
-
# @param method [Symbol] name of the attribute method
|
93
|
-
# @param options [Hash] attribute options
|
94
|
-
# @option options [Object, Proc] :default default value or proc to generate value
|
95
67
|
# @return [void]
|
96
68
|
#
|
97
|
-
# @example
|
98
|
-
# class Task
|
99
|
-
# __cmdx_attr_setting :timeout, default: 30
|
100
|
-
# end
|
101
|
-
# # Task.timeout => 30
|
102
|
-
#
|
103
|
-
# @example Dynamic default with proc
|
104
|
-
# class Task
|
105
|
-
# __cmdx_attr_setting :timestamp, default: -> { Time.now }
|
106
|
-
# end
|
107
|
-
# # Task.timestamp => current time (evaluated lazily)
|
108
|
-
#
|
109
|
-
# @example Inherited settings
|
69
|
+
# @example Define a setting with a default value
|
110
70
|
# class BaseTask
|
111
|
-
#
|
71
|
+
# cmdx_attr_setting :timeout, default: 30
|
112
72
|
# end
|
113
73
|
#
|
114
|
-
#
|
115
|
-
# end
|
116
|
-
# # ProcessTask.options => {retry: 3} (inherited from BaseTask)
|
74
|
+
# BaseTask.timeout #=> 30
|
117
75
|
#
|
118
|
-
# @example
|
76
|
+
# @example Define a setting with a dynamic default
|
119
77
|
# class Task
|
120
|
-
#
|
78
|
+
# cmdx_attr_setting :retry_count, default: -> { ENV['RETRY_COUNT']&.to_i || 3 }
|
121
79
|
# end
|
122
|
-
|
123
|
-
def __cmdx_attr_setting(method, **options)
|
80
|
+
def cmdx_attr_setting(method, **options)
|
124
81
|
define_singleton_method(method) do
|
125
82
|
@cmd_facets ||= {}
|
126
83
|
return @cmd_facets[method] if @cmd_facets.key?(method)
|
127
84
|
|
128
|
-
value = superclass.
|
85
|
+
value = superclass.cmdx_try(method)
|
129
86
|
return @cmd_facets[method] = value.dup unless value.nil?
|
130
87
|
|
131
88
|
default = options[:default]
|
132
|
-
value = default.
|
89
|
+
value = default.cmdx_call
|
133
90
|
@cmd_facets[method] = default.is_a?(Proc) ? value : value.dup
|
134
91
|
end
|
135
92
|
end
|
@@ -138,5 +95,4 @@ module CMDx
|
|
138
95
|
end
|
139
96
|
end
|
140
97
|
|
141
|
-
# Extend all modules with CMDx utility methods
|
142
98
|
Module.include(CMDx::CoreExt::ModuleExtensions)
|
data/lib/cmdx/core_ext/object.rb
CHANGED
@@ -2,151 +2,117 @@
|
|
2
2
|
|
3
3
|
module CMDx
|
4
4
|
module CoreExt
|
5
|
-
# Extensions
|
6
|
-
#
|
7
|
-
#
|
8
|
-
# and value yielding capabilities to all Ruby objects. These methods
|
9
|
-
# are prefixed with `__cmdx_` to avoid conflicts with existing methods.
|
10
|
-
#
|
11
|
-
# @example Safe method calling
|
12
|
-
# object.__cmdx_try(:some_method) # Returns nil if method doesn't exist
|
13
|
-
# object.__cmdx_try(proc { expensive_calculation }) # Calls proc safely
|
14
|
-
#
|
15
|
-
# @example Conditional evaluation
|
16
|
-
# object.__cmdx_eval(if: :valid?) # True if object.valid? is true
|
17
|
-
# object.__cmdx_eval(unless: :empty?) # True unless object.empty? is true
|
18
|
-
# object.__cmdx_eval(if: :valid?, unless: :processed?) # Combined conditions
|
19
|
-
#
|
20
|
-
# @example Value yielding
|
21
|
-
# object.__cmdx_yield(:name) # Returns object.name if method exists, otherwise :name
|
22
|
-
# object.__cmdx_yield(-> { compute }) # Executes lambda and returns result
|
23
|
-
#
|
24
|
-
# @see Task Tasks that use these object extensions
|
25
|
-
# @see Parameter Parameters that leverage object extensions
|
5
|
+
# Extensions for Ruby's Object class that provide flexible method calling and evaluation utilities.
|
6
|
+
# These extensions are automatically included in all objects when CMDx is loaded, providing
|
7
|
+
# safe method invocation, conditional evaluation, and dynamic yielding capabilities.
|
26
8
|
module ObjectExtensions
|
27
9
|
|
28
|
-
|
29
|
-
alias __cmdx_respond_to? respond_to?
|
10
|
+
alias cmdx_respond_to? respond_to?
|
30
11
|
|
31
|
-
# Safely
|
12
|
+
# Safely tries to call a method, evaluate a proc, or access a hash key.
|
13
|
+
# Provides flexible invocation that handles different types of callables gracefully.
|
32
14
|
#
|
33
|
-
#
|
34
|
-
#
|
15
|
+
# @param key [Symbol, String, Proc, Object] the method name, proc, or hash key to try
|
16
|
+
# @param args [Array] arguments to pass to the method or proc
|
35
17
|
#
|
36
|
-
# @
|
37
|
-
# @param args [Array] arguments to pass to the method/proc (passed via splat)
|
38
|
-
# @return [Object, nil] result of method call, proc execution, or nil if not possible
|
18
|
+
# @return [Object, nil] the result of the method call, proc evaluation, or hash access; nil if not found
|
39
19
|
#
|
40
|
-
# @example
|
41
|
-
#
|
42
|
-
#
|
20
|
+
# @example Try calling a method
|
21
|
+
# "hello".cmdx_try(:upcase) #=> "HELLO"
|
22
|
+
# "hello".cmdx_try(:missing) #=> nil
|
43
23
|
#
|
44
|
-
# @example
|
45
|
-
#
|
46
|
-
# user.__cmdx_try(proc { |x| x * 2 }, 5) # => 10
|
24
|
+
# @example Try evaluating a proc
|
25
|
+
# obj.cmdx_try(-> { self.class.name }) #=> "String"
|
47
26
|
#
|
48
|
-
# @example
|
49
|
-
#
|
50
|
-
|
51
|
-
def __cmdx_try(key, ...)
|
27
|
+
# @example Try accessing a hash key
|
28
|
+
# {name: "John"}.cmdx_try(:name) #=> "John"
|
29
|
+
def cmdx_try(key, *args, **kwargs, &)
|
52
30
|
if key.is_a?(Proc)
|
53
31
|
return instance_eval(&key) unless is_a?(Module) || key.inspect.include?("(lambda)")
|
54
32
|
|
55
|
-
key.
|
33
|
+
if key.arity.positive? && args.empty?
|
34
|
+
key.call(self, *args, **kwargs, &)
|
35
|
+
else
|
36
|
+
key.call(*args, **kwargs, &)
|
37
|
+
end
|
56
38
|
elsif respond_to?(key, true)
|
57
|
-
send(key,
|
39
|
+
send(key, *args, **kwargs, &)
|
58
40
|
elsif is_a?(Hash)
|
59
|
-
|
41
|
+
cmdx_fetch(key)
|
60
42
|
end
|
61
43
|
end
|
62
44
|
|
63
|
-
#
|
45
|
+
# Evaluates conditional options using :if and :unless logic.
|
46
|
+
# Supports both method names and procs for conditional evaluation.
|
64
47
|
#
|
65
|
-
#
|
66
|
-
#
|
67
|
-
#
|
48
|
+
# @param options [Hash] evaluation options
|
49
|
+
# @option options [Symbol, Proc] :if condition that must be truthy
|
50
|
+
# @option options [Symbol, Proc] :unless condition that must be falsy
|
51
|
+
# @option options [Object] :default (true) default value when no conditions are specified
|
68
52
|
#
|
69
|
-
# @
|
70
|
-
# @option options [Symbol, Proc] :if condition that must be true
|
71
|
-
# @option options [Symbol, Proc] :unless condition that must be false
|
72
|
-
# @option options [Boolean] :default default value if no conditions (default: true)
|
73
|
-
# @return [Boolean] true if conditions are met
|
53
|
+
# @return [Boolean] true if conditions are met, false otherwise
|
74
54
|
#
|
75
|
-
# @example
|
76
|
-
# user.
|
77
|
-
# user.__cmdx_eval(unless: :guest?) # => true unless user.guest? is true
|
55
|
+
# @example Evaluate with if condition
|
56
|
+
# user.cmdx_eval(if: :active?) #=> true if user.active? is truthy
|
78
57
|
#
|
79
|
-
# @example
|
80
|
-
# user.
|
58
|
+
# @example Evaluate with unless condition
|
59
|
+
# user.cmdx_eval(unless: :banned?) #=> true if user.banned? is falsy
|
81
60
|
#
|
82
|
-
# @example
|
83
|
-
# user.
|
84
|
-
def
|
61
|
+
# @example Evaluate with both conditions
|
62
|
+
# user.cmdx_eval(if: :active?, unless: :banned?) #=> true if active and not banned
|
63
|
+
def cmdx_eval(options = {})
|
85
64
|
if options[:if] && options[:unless]
|
86
|
-
|
65
|
+
cmdx_try(options[:if]) && !cmdx_try(options[:unless])
|
87
66
|
elsif options[:if]
|
88
|
-
|
67
|
+
cmdx_try(options[:if])
|
89
68
|
elsif options[:unless]
|
90
|
-
!
|
69
|
+
!cmdx_try(options[:unless])
|
91
70
|
else
|
92
71
|
options.fetch(:default, true)
|
93
72
|
end
|
94
73
|
end
|
95
74
|
|
96
|
-
#
|
75
|
+
# Yields or returns a value based on its type, with smart method calling.
|
76
|
+
# Handles symbols/strings as method names, procs/hashes via cmdx_try, and returns other values as-is.
|
97
77
|
#
|
98
|
-
#
|
99
|
-
#
|
100
|
-
# Otherwise, try to execute it as a proc or return the value as-is.
|
78
|
+
# @param key [Symbol, String, Proc, Hash, Object] the value to yield or method to call
|
79
|
+
# @param args [Array] arguments to pass to method calls
|
101
80
|
#
|
102
|
-
# @
|
103
|
-
# @param args [Array] arguments to pass if calling method/proc (passed via splat)
|
104
|
-
# @return [Object] yielded value
|
81
|
+
# @return [Object] the result of method call, proc evaluation, or the value itself
|
105
82
|
#
|
106
|
-
# @example
|
107
|
-
#
|
108
|
-
# user.__cmdx_yield("email") # => calls user.email if method exists, otherwise returns "email"
|
83
|
+
# @example Yield a method call
|
84
|
+
# "hello".cmdx_yield(:upcase) #=> "HELLO"
|
109
85
|
#
|
110
|
-
# @example
|
111
|
-
#
|
112
|
-
# hash.__cmdx_yield({key: "value"}) # => tries hash access
|
86
|
+
# @example Yield a static value
|
87
|
+
# obj.cmdx_yield("static") #=> "static"
|
113
88
|
#
|
114
|
-
# @example
|
115
|
-
#
|
116
|
-
|
117
|
-
def __cmdx_yield(key, ...)
|
89
|
+
# @example Yield a proc
|
90
|
+
# obj.cmdx_yield(-> { Time.now }) #=> 2023-01-01 12:00:00 UTC
|
91
|
+
def cmdx_yield(key, ...)
|
118
92
|
if key.is_a?(Symbol) || key.is_a?(String)
|
119
93
|
return key unless respond_to?(key, true)
|
120
94
|
|
121
95
|
send(key, ...)
|
122
96
|
elsif is_a?(Hash) || key.is_a?(Proc)
|
123
|
-
|
97
|
+
cmdx_try(key, ...)
|
124
98
|
else
|
125
99
|
key
|
126
100
|
end
|
127
101
|
end
|
128
102
|
|
129
|
-
#
|
130
|
-
#
|
131
|
-
# This method provides safe callable execution - if the object
|
132
|
-
# can be called (like a proc or lambda), call it with the given
|
133
|
-
# arguments. Otherwise, return the object unchanged.
|
103
|
+
# Invokes the object if it responds to :call, otherwise returns the object itself.
|
104
|
+
# Useful for handling both callable and non-callable objects uniformly.
|
134
105
|
#
|
135
|
-
# @param args [Array] arguments to pass to call method
|
136
|
-
# @return [Object] result of calling or the object itself
|
106
|
+
# @param args [Array] arguments to pass to the call method
|
137
107
|
#
|
138
|
-
# @
|
139
|
-
# proc = -> { "Hello" }
|
140
|
-
# proc.__cmdx_call # => "Hello"
|
108
|
+
# @return [Object] the result of calling the object, or the object itself if not callable
|
141
109
|
#
|
142
|
-
# @example
|
143
|
-
#
|
144
|
-
# string.__cmdx_call # => "Hello"
|
110
|
+
# @example Invoke a proc
|
111
|
+
# proc { "hello" }.cmdx_call #=> "hello"
|
145
112
|
#
|
146
|
-
# @example
|
147
|
-
#
|
148
|
-
|
149
|
-
def __cmdx_call(...)
|
113
|
+
# @example Invoke a non-callable object
|
114
|
+
# "hello".cmdx_call #=> "hello"
|
115
|
+
def cmdx_call(...)
|
150
116
|
return self unless respond_to?(:call)
|
151
117
|
|
152
118
|
call(...)
|
@@ -156,5 +122,4 @@ module CMDx
|
|
156
122
|
end
|
157
123
|
end
|
158
124
|
|
159
|
-
# Extend all objects with CMDx utility methods
|
160
125
|
Object.include(CMDx::CoreExt::ObjectExtensions)
|