cmdx 1.0.1 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. checksums.yaml +4 -4
  2. data/.cursor/prompts/docs.md +9 -0
  3. data/.cursor/prompts/rspec.md +21 -0
  4. data/.cursor/prompts/yardoc.md +13 -0
  5. data/.rubocop.yml +2 -0
  6. data/CHANGELOG.md +29 -3
  7. data/README.md +2 -1
  8. data/docs/ai_prompts.md +269 -195
  9. data/docs/basics/call.md +126 -60
  10. data/docs/basics/chain.md +190 -160
  11. data/docs/basics/context.md +242 -154
  12. data/docs/basics/setup.md +302 -32
  13. data/docs/callbacks.md +382 -119
  14. data/docs/configuration.md +211 -49
  15. data/docs/deprecation.md +245 -0
  16. data/docs/getting_started.md +161 -39
  17. data/docs/internationalization.md +590 -70
  18. data/docs/interruptions/exceptions.md +135 -118
  19. data/docs/interruptions/faults.md +152 -127
  20. data/docs/interruptions/halt.md +134 -80
  21. data/docs/logging.md +183 -120
  22. data/docs/middlewares.md +165 -392
  23. data/docs/outcomes/result.md +140 -112
  24. data/docs/outcomes/states.md +134 -99
  25. data/docs/outcomes/statuses.md +204 -146
  26. data/docs/parameters/coercions.md +251 -289
  27. data/docs/parameters/defaults.md +224 -169
  28. data/docs/parameters/definitions.md +289 -141
  29. data/docs/parameters/namespacing.md +250 -161
  30. data/docs/parameters/validations.md +247 -159
  31. data/docs/testing.md +196 -203
  32. data/docs/workflows.md +146 -101
  33. data/lib/cmdx/.DS_Store +0 -0
  34. data/lib/cmdx/callback.rb +39 -55
  35. data/lib/cmdx/callback_registry.rb +80 -73
  36. data/lib/cmdx/chain.rb +65 -122
  37. data/lib/cmdx/chain_inspector.rb +23 -116
  38. data/lib/cmdx/chain_serializer.rb +34 -146
  39. data/lib/cmdx/coercion.rb +57 -0
  40. data/lib/cmdx/coercion_registry.rb +113 -0
  41. data/lib/cmdx/coercions/array.rb +18 -36
  42. data/lib/cmdx/coercions/big_decimal.rb +21 -33
  43. data/lib/cmdx/coercions/boolean.rb +21 -40
  44. data/lib/cmdx/coercions/complex.rb +18 -31
  45. data/lib/cmdx/coercions/date.rb +20 -39
  46. data/lib/cmdx/coercions/date_time.rb +22 -39
  47. data/lib/cmdx/coercions/float.rb +19 -32
  48. data/lib/cmdx/coercions/hash.rb +22 -41
  49. data/lib/cmdx/coercions/integer.rb +20 -33
  50. data/lib/cmdx/coercions/rational.rb +20 -32
  51. data/lib/cmdx/coercions/string.rb +23 -31
  52. data/lib/cmdx/coercions/time.rb +24 -40
  53. data/lib/cmdx/coercions/virtual.rb +14 -31
  54. data/lib/cmdx/configuration.rb +101 -162
  55. data/lib/cmdx/context.rb +34 -166
  56. data/lib/cmdx/core_ext/hash.rb +42 -67
  57. data/lib/cmdx/core_ext/module.rb +35 -79
  58. data/lib/cmdx/core_ext/object.rb +63 -98
  59. data/lib/cmdx/correlator.rb +59 -154
  60. data/lib/cmdx/error.rb +37 -202
  61. data/lib/cmdx/errors.rb +153 -216
  62. data/lib/cmdx/fault.rb +68 -150
  63. data/lib/cmdx/faults.rb +26 -137
  64. data/lib/cmdx/immutator.rb +22 -110
  65. data/lib/cmdx/lazy_struct.rb +110 -186
  66. data/lib/cmdx/log_formatters/json.rb +14 -40
  67. data/lib/cmdx/log_formatters/key_value.rb +14 -40
  68. data/lib/cmdx/log_formatters/line.rb +14 -48
  69. data/lib/cmdx/log_formatters/logstash.rb +14 -57
  70. data/lib/cmdx/log_formatters/pretty_json.rb +14 -50
  71. data/lib/cmdx/log_formatters/pretty_key_value.rb +13 -46
  72. data/lib/cmdx/log_formatters/pretty_line.rb +16 -54
  73. data/lib/cmdx/log_formatters/raw.rb +19 -49
  74. data/lib/cmdx/logger.rb +22 -79
  75. data/lib/cmdx/logger_ansi.rb +31 -72
  76. data/lib/cmdx/logger_serializer.rb +74 -103
  77. data/lib/cmdx/middleware.rb +56 -60
  78. data/lib/cmdx/middleware_registry.rb +82 -77
  79. data/lib/cmdx/middlewares/correlate.rb +41 -226
  80. data/lib/cmdx/middlewares/timeout.rb +46 -185
  81. data/lib/cmdx/parameter.rb +167 -183
  82. data/lib/cmdx/parameter_evaluator.rb +231 -0
  83. data/lib/cmdx/parameter_inspector.rb +37 -55
  84. data/lib/cmdx/parameter_registry.rb +65 -84
  85. data/lib/cmdx/parameter_serializer.rb +32 -76
  86. data/lib/cmdx/railtie.rb +24 -107
  87. data/lib/cmdx/result.rb +254 -259
  88. data/lib/cmdx/result_ansi.rb +28 -80
  89. data/lib/cmdx/result_inspector.rb +34 -70
  90. data/lib/cmdx/result_logger.rb +23 -77
  91. data/lib/cmdx/result_serializer.rb +59 -125
  92. data/lib/cmdx/rspec/matchers.rb +28 -0
  93. data/lib/cmdx/rspec/result_matchers/be_executed.rb +42 -0
  94. data/lib/cmdx/rspec/result_matchers/be_failed_task.rb +94 -0
  95. data/lib/cmdx/rspec/result_matchers/be_skipped_task.rb +94 -0
  96. data/lib/cmdx/rspec/result_matchers/be_state_matchers.rb +59 -0
  97. data/lib/cmdx/rspec/result_matchers/be_status_matchers.rb +57 -0
  98. data/lib/cmdx/rspec/result_matchers/be_successful_task.rb +87 -0
  99. data/lib/cmdx/rspec/result_matchers/have_bad_outcome.rb +51 -0
  100. data/lib/cmdx/rspec/result_matchers/have_caused_failure.rb +58 -0
  101. data/lib/cmdx/rspec/result_matchers/have_chain_index.rb +59 -0
  102. data/lib/cmdx/rspec/result_matchers/have_context.rb +86 -0
  103. data/lib/cmdx/rspec/result_matchers/have_empty_metadata.rb +54 -0
  104. data/lib/cmdx/rspec/result_matchers/have_good_outcome.rb +52 -0
  105. data/lib/cmdx/rspec/result_matchers/have_metadata.rb +114 -0
  106. data/lib/cmdx/rspec/result_matchers/have_preserved_context.rb +66 -0
  107. data/lib/cmdx/rspec/result_matchers/have_received_thrown_failure.rb +64 -0
  108. data/lib/cmdx/rspec/result_matchers/have_runtime.rb +78 -0
  109. data/lib/cmdx/rspec/result_matchers/have_thrown_failure.rb +76 -0
  110. data/lib/cmdx/rspec/task_matchers/be_well_formed_task.rb +62 -0
  111. data/lib/cmdx/rspec/task_matchers/have_callback.rb +85 -0
  112. data/lib/cmdx/rspec/task_matchers/have_cmd_setting.rb +68 -0
  113. data/lib/cmdx/rspec/task_matchers/have_executed_callbacks.rb +92 -0
  114. data/lib/cmdx/rspec/task_matchers/have_middleware.rb +46 -0
  115. data/lib/cmdx/rspec/task_matchers/have_parameter.rb +181 -0
  116. data/lib/cmdx/task.rb +336 -427
  117. data/lib/cmdx/task_deprecator.rb +52 -0
  118. data/lib/cmdx/task_processor.rb +246 -0
  119. data/lib/cmdx/task_serializer.rb +34 -69
  120. data/lib/cmdx/utils/ansi_color.rb +13 -89
  121. data/lib/cmdx/utils/log_timestamp.rb +13 -42
  122. data/lib/cmdx/utils/monotonic_runtime.rb +11 -63
  123. data/lib/cmdx/utils/name_affix.rb +21 -71
  124. data/lib/cmdx/validator.rb +57 -0
  125. data/lib/cmdx/validator_registry.rb +108 -0
  126. data/lib/cmdx/validators/exclusion.rb +55 -94
  127. data/lib/cmdx/validators/format.rb +31 -85
  128. data/lib/cmdx/validators/inclusion.rb +65 -110
  129. data/lib/cmdx/validators/length.rb +117 -133
  130. data/lib/cmdx/validators/numeric.rb +123 -130
  131. data/lib/cmdx/validators/presence.rb +38 -79
  132. data/lib/cmdx/version.rb +1 -7
  133. data/lib/cmdx/workflow.rb +58 -330
  134. data/lib/cmdx.rb +1 -1
  135. data/lib/generators/cmdx/install_generator.rb +14 -31
  136. data/lib/generators/cmdx/task_generator.rb +39 -55
  137. data/lib/generators/cmdx/templates/install.rb +24 -6
  138. data/lib/generators/cmdx/workflow_generator.rb +41 -66
  139. data/lib/locales/ar.yml +0 -1
  140. data/lib/locales/cs.yml +0 -1
  141. data/lib/locales/da.yml +0 -1
  142. data/lib/locales/de.yml +0 -1
  143. data/lib/locales/el.yml +0 -1
  144. data/lib/locales/en.yml +0 -1
  145. data/lib/locales/es.yml +0 -1
  146. data/lib/locales/fi.yml +0 -1
  147. data/lib/locales/fr.yml +0 -1
  148. data/lib/locales/he.yml +0 -1
  149. data/lib/locales/hi.yml +0 -1
  150. data/lib/locales/it.yml +0 -1
  151. data/lib/locales/ja.yml +0 -1
  152. data/lib/locales/ko.yml +0 -1
  153. data/lib/locales/nl.yml +0 -1
  154. data/lib/locales/no.yml +0 -1
  155. data/lib/locales/pl.yml +0 -1
  156. data/lib/locales/pt.yml +0 -1
  157. data/lib/locales/ru.yml +0 -1
  158. data/lib/locales/sv.yml +0 -1
  159. data/lib/locales/th.yml +0 -1
  160. data/lib/locales/tr.yml +0 -1
  161. data/lib/locales/vi.yml +0 -1
  162. data/lib/locales/zh.yml +0 -1
  163. metadata +36 -8
  164. data/lib/cmdx/parameter_validator.rb +0 -81
  165. data/lib/cmdx/parameter_value.rb +0 -244
  166. data/lib/cmdx/parameters_inspector.rb +0 -72
  167. data/lib/cmdx/parameters_serializer.rb +0 -115
  168. data/lib/cmdx/rspec/result_matchers.rb +0 -917
  169. data/lib/cmdx/rspec/task_matchers.rb +0 -570
  170. data/lib/cmdx/validators/custom.rb +0 -102
@@ -2,51 +2,28 @@
2
2
 
3
3
  module CMDx
4
4
  module CoreExt
5
- # Extensions to Hash that provide CMDx-specific key access methods.
6
- #
7
- # HashExtensions adds flexible key access that works with both
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
- # Fetch a value with automatic symbol/string key conversion.
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
- # This method provides flexible key access by trying both the original
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
- # @param key [Symbol, String, Object] key to fetch
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 Symbol to string conversion
39
- # hash = {"name" => "John"}
40
- # hash.__cmdx_fetch(:name) # => "John" (tries :name, then "name")
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 String to symbol conversion
43
- # hash = {name: "John"}
44
- # hash.__cmdx_fetch("name") # => "John" (tries "name", then :name)
45
- #
46
- # @example Direct key access
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
- # Check if a key exists with automatic symbol/string conversion.
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
- # This method checks for key existence by trying both the original
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
- # @param key [Symbol, String, Object] key to check
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 Symbol/string checking
66
- # hash = {name: "John", "age" => 30}
67
- # hash.__cmdx_key?(:name) # => true
68
- # hash.__cmdx_key?("name") # => true (checks :name fallback)
69
- # hash.__cmdx_key?(:age) # => true (checks "age" fallback)
70
- # hash.__cmdx_key?(:missing) # => false
71
- def __cmdx_key?(key)
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
- # Check if hash responds to a method or contains a key.
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
- # This method extends respond_to? behavior to also check if the
85
- # hash contains a key that matches the method name. This enables
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
- # @param key [Symbol, String] method name to check
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 Method response checking
93
- # hash = {name: "John"}
94
- # hash.__cmdx_respond_to?(:name) # => true (has key :name)
95
- # hash.__cmdx_respond_to?(:keys) # => true (real Hash method)
96
- # hash.__cmdx_respond_to?(:missing) # => false
97
- def __cmdx_respond_to?(key, include_private = false)
98
- respond_to?(key.to_sym, include_private) || __cmdx_key?(key)
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
- __cmdx_key?(key)
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)
@@ -2,65 +2,39 @@
2
2
 
3
3
  module CMDx
4
4
  module CoreExt
5
- # Extensions to Module that provide CMDx-specific metaprogramming capabilities.
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
- # Create delegator methods that forward calls to another object.
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
- # This method generates instance methods that delegate to methods on
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 method name (required)
36
- # @option options [Boolean] :allow_missing whether to allow missing methods
37
- # @option options [Boolean] :private make delegated methods private
38
- # @option options [Boolean] :protected make delegated methods protected
39
- # @return [void]
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
- # @example Basic delegation
42
- # class User
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 Private delegation
24
+ # @example Delegate methods to an instance variable
48
25
  # class Task
49
- # __cmdx_attr_delegator :validate, to: :validator, private: true
50
- # end
26
+ # def initialize
27
+ # @logger = Logger.new
28
+ # end
51
29
  #
52
- # @example Class delegation
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 With missing method handling
58
- # class Task
59
- # __cmdx_attr_delegator :optional_method, to: :service, allow_missing: true
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
- # Create class-level attribute accessor with lazy evaluation and inheritance.
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
- # This method generates a class method that provides lazy-loaded attribute
89
- # access with inheritance support. Values are cached and can be initialized
90
- # with default values or procs.
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 Simple attribute setting
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
- # __cmdx_attr_setting :options, default: {retry: 3}
71
+ # cmdx_attr_setting :timeout, default: 30
112
72
  # end
113
73
  #
114
- # class ProcessTask < BaseTask
115
- # end
116
- # # ProcessTask.options => {retry: 3} (inherited from BaseTask)
74
+ # BaseTask.timeout #=> 30
117
75
  #
118
- # @example Hash settings (automatically duplicated)
76
+ # @example Define a setting with a dynamic default
119
77
  # class Task
120
- # __cmdx_attr_setting :config, default: {}
78
+ # cmdx_attr_setting :retry_count, default: -> { ENV['RETRY_COUNT']&.to_i || 3 }
121
79
  # end
122
- # # Each class gets its own copy of the hash
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.__cmdx_try(method)
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.__cmdx_call
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)
@@ -2,151 +2,117 @@
2
2
 
3
3
  module CMDx
4
4
  module CoreExt
5
- # Extensions to Object that provide CMDx-specific utility methods.
6
- #
7
- # ObjectExtensions adds safe method calling, conditional evaluation,
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
- # Store original respond_to? method before aliasing
29
- alias __cmdx_respond_to? respond_to?
10
+ alias cmdx_respond_to? respond_to?
30
11
 
31
- # Safely attempt to call a method or execute a proc on an object.
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
- # This method provides safe method calling with fallback behavior.
34
- # It handles method calls, proc execution, and hash key access gracefully.
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
- # @param key [Symbol, String, Proc] method name or callable to attempt
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 Method calling
41
- # user.__cmdx_try(:name) # => "John" or nil
42
- # user.__cmdx_try(:age, 25) # => calls user.age(25) or nil
20
+ # @example Try calling a method
21
+ # "hello".cmdx_try(:upcase) #=> "HELLO"
22
+ # "hello".cmdx_try(:missing) #=> nil
43
23
  #
44
- # @example Proc execution
45
- # user.__cmdx_try(-> { expensive_calc }) # => executes lambda
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 Hash access
49
- # hash = {name: "John"}
50
- # hash.__cmdx_try(:name) # => "John"
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.call(...)
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
- __cmdx_fetch(key)
41
+ cmdx_fetch(key)
60
42
  end
61
43
  end
62
44
 
63
- # Evaluate conditional options for execution control.
45
+ # Evaluates conditional options using :if and :unless logic.
46
+ # Supports both method names and procs for conditional evaluation.
64
47
  #
65
- # This method evaluates :if and :unless conditions to determine
66
- # whether something should proceed. Used extensively in callbacks
67
- # and conditional parameter processing.
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
- # @param options [Hash] conditional options
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 Simple conditions
76
- # user.__cmdx_eval(if: :admin?) # => true if user.admin? is true
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 Combined conditions
80
- # user.__cmdx_eval(if: :active?, unless: :banned?) # => active AND not banned
58
+ # @example Evaluate with unless condition
59
+ # user.cmdx_eval(unless: :banned?) #=> true if user.banned? is falsy
81
60
  #
82
- # @example With procs
83
- # user.__cmdx_eval(if: -> { Time.now.monday? }) # => true if today is Monday
84
- def __cmdx_eval(options = {})
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
- __cmdx_try(options[:if]) && !__cmdx_try(options[:unless])
65
+ cmdx_try(options[:if]) && !cmdx_try(options[:unless])
87
66
  elsif options[:if]
88
- __cmdx_try(options[:if])
67
+ cmdx_try(options[:if])
89
68
  elsif options[:unless]
90
- !__cmdx_try(options[:unless])
69
+ !cmdx_try(options[:unless])
91
70
  else
92
71
  options.fetch(:default, true)
93
72
  end
94
73
  end
95
74
 
96
- # Yield a value by attempting to call it as a method or executing it.
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
- # This method provides intelligent value resolution - if the key is
99
- # a method name and the object responds to it, call the method.
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
- # @param key [Object] value to yield, method name, or callable
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 Method yielding
107
- # user.__cmdx_yield(:name) # => calls user.name if method exists, otherwise returns :name
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 Proc yielding
111
- # user.__cmdx_yield(-> { timestamp }) # => executes lambda
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 Direct values
115
- # user.__cmdx_yield(42) # => 42
116
- # user.__cmdx_yield("literal") # => "literal"
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
- __cmdx_try(key, ...)
97
+ cmdx_try(key, ...)
124
98
  else
125
99
  key
126
100
  end
127
101
  end
128
102
 
129
- # Call an object if it responds to call, otherwise return itself.
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 (passed via splat)
136
- # @return [Object] result of calling or the object itself
106
+ # @param args [Array] arguments to pass to the call method
137
107
  #
138
- # @example Callable objects
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 Non-callable objects
143
- # string = "Hello"
144
- # string.__cmdx_call # => "Hello"
110
+ # @example Invoke a proc
111
+ # proc { "hello" }.cmdx_call #=> "hello"
145
112
  #
146
- # @example With arguments
147
- # adder = ->(a, b) { a + b }
148
- # adder.__cmdx_call(2, 3) # => 5
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)