cmdx 1.18.0 → 1.19.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 80c55fa1f758dc69cebff57fa5dd770b4bd7692f4a37768c320b6813d28cc9fb
4
- data.tar.gz: babf14b98faa436de370d039cc2b6c796f2d0c43d9e408137ccdd1f666f4abe3
3
+ metadata.gz: ead1d181ed1eac565ea3287eeaaca3216e5d24a40f4071000539584435fc1327
4
+ data.tar.gz: ba0273c00286e10621f06fc5b0ba7be760afa99dd8d3fbfc6fc7da1c3c11891f
5
5
  SHA512:
6
- metadata.gz: cbcb58370126bc18aec53218930b11a7c6a3194b4b1e1c6247ae2d6e693ef16f84cc4dbf90a7dbd816e900f421e625cac3ca227aca7ce4898fa711c1451230e0
7
- data.tar.gz: de88ce41eacbf4f843803f84055d46dcfb7774aa5532396c820112a5ca3d80d68deb9dbc296f8b782d990122ed6ccc082bbc582f93d3714373c2705c55a936fe
6
+ metadata.gz: 30e8c09228e765f2ac8e9b4f8db854da21a13e3a2d7e67b9ad61ecad66d09dd7f8ca9327ac9a8ef8777e78d56d5844a06f52e55d282cc1d78d7ddb9141b9acc2
7
+ data.tar.gz: 922de5ad8342195dc94bf8137e7023612db4df09b3979581c0e77aecf599cae971004e64b9ab71261ea6795feb8ca8b7fe3fe4fc3f37a570ed50157c404aff33
data/CHANGELOG.md CHANGED
@@ -6,6 +6,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
 
7
7
  ## [UNRELEASED]
8
8
 
9
+ ### Changed
10
+ - Add attribute `source` fallback to `:context` when no task is given
11
+ - Improve falsy attribute derived Hash value lookup
12
+ - Freeze chain results
13
+ - Fix missing fault cause no method error issue
14
+ - Add context respond_to? with setter methods
15
+ - Fix validator `allow_nil` inverted logic
16
+ - Array coercion JSON parse error no returns CoercionError
17
+ - Boolean coercions now return `false` for `nil` and `""`
18
+ - Coerce anaglous date, datetime, and time class checks to rely on `to_date`, `to_time`, `to_datetime` methods
19
+
9
20
  ## [1.18.0] - 2025-03-09
10
21
 
11
22
  ### Changed
@@ -208,7 +208,8 @@ module CMDx
208
208
  !!@required
209
209
  end
210
210
 
211
- # Determines the source of the attribute value.
211
+ # Determines the source of the attribute value. Returns :context
212
+ # as a safe fallback when task is not yet set (e.g., schema introspection).
212
213
  #
213
214
  # @return [Symbol] The source identifier for the attribute value
214
215
  #
@@ -217,13 +218,15 @@ module CMDx
217
218
  #
218
219
  # @rbs () -> untyped
219
220
  def source
220
- @source ||= parent&.method_name || begin
221
+ return @source if defined?(@source)
222
+
223
+ @source = parent&.method_name || begin
221
224
  value = options[:source]
222
225
 
223
226
  if value.is_a?(Proc)
224
- task.instance_eval(&value)
227
+ task ? task.instance_eval(&value) : :context
225
228
  elsif value.respond_to?(:call)
226
- value.call(task)
229
+ task ? value.call(task) : :context
227
230
  else
228
231
  value || :context
229
232
  end
@@ -239,7 +242,9 @@ module CMDx
239
242
  #
240
243
  # @rbs () -> Symbol
241
244
  def method_name
242
- @method_name ||= options[:as] || begin
245
+ return @method_name if defined?(@method_name)
246
+
247
+ @method_name = options[:as] || begin
243
248
  prefix = AFFIX.call(options[:prefix]) { "#{source}_" }
244
249
  suffix = AFFIX.call(options[:suffix]) { "_#{source}" }
245
250
 
@@ -166,7 +166,12 @@ module CMDx
166
166
  derived_value =
167
167
  case source_value
168
168
  when Context then source_value[name]
169
- when Hash then source_value[name.to_s] || source_value[name.to_sym]
169
+ when Hash
170
+ if source_value.key?(name.to_s)
171
+ source_value[name.to_s]
172
+ elsif source_value.key?(name.to_sym)
173
+ source_value[name.to_sym]
174
+ end
170
175
  when Symbol then source_value.send(name)
171
176
  when Proc then task.instance_exec(name, &source_value)
172
177
  else
data/lib/cmdx/chain.rb CHANGED
@@ -136,6 +136,20 @@ module CMDx
136
136
  !!@dry_run
137
137
  end
138
138
 
139
+ # Freezes the chain and its internal results to prevent modifications.
140
+ #
141
+ # @return [Chain] the frozen chain
142
+ #
143
+ # @example
144
+ # chain.freeze
145
+ # chain.results << result # => raises FrozenError
146
+ #
147
+ # @rbs () -> self
148
+ def freeze
149
+ results.freeze
150
+ super
151
+ end
152
+
139
153
  # Converts the chain to a hash representation.
140
154
  #
141
155
  # @option return [String] :id The chain identifier
@@ -18,7 +18,7 @@ module CMDx
18
18
  #
19
19
  # @return [Array] The converted array value
20
20
  #
21
- # @raise [JSON::ParserError] If the string value contains invalid JSON
21
+ # @raise [CoercionError] If the value cannot be converted to an array
22
22
  #
23
23
  # @example Convert a JSON-like string to an array
24
24
  # Array.call("[1, 2, 3]") # => [1, 2, 3]
@@ -26,6 +26,8 @@ module CMDx
26
26
  # Array.call("hello") # => ["hello"]
27
27
  # Array.call(42) # => [42]
28
28
  # Array.call(nil) # => []
29
+ # @example Handle invalid JSON-like strings
30
+ # Array.call("[not json") # => raises CoercionError
29
31
  #
30
32
  # @rbs (untyped value, ?Hash[Symbol, untyped] options) -> Array[untyped]
31
33
  def call(value, options = {})
@@ -37,6 +39,9 @@ module CMDx
37
39
  else
38
40
  Array(value)
39
41
  end
42
+ rescue JSON::ParserError
43
+ type = Locale.t("cmdx.types.array")
44
+ raise CoercionError, Locale.t("cmdx.coercions.into_an", type:)
40
45
  end
41
46
 
42
47
  end
@@ -34,14 +34,18 @@ module CMDx
34
34
  # Boolean.call("false") # => false
35
35
  # Boolean.call("no") # => false
36
36
  # Boolean.call("0") # => false
37
+ # Boolean.call(nil) # => false
38
+ # Boolean.call("") # => false
37
39
  # @example Handle case-insensitive input
38
40
  # Boolean.call("TRUE") # => true
39
41
  # Boolean.call("False") # => false
42
+ # @example Handle edge cases
43
+ # Boolean.call("abc") # => raises CoercionError
40
44
  #
41
45
  # @rbs (untyped value, ?Hash[Symbol, untyped] options) -> bool
42
46
  def call(value, options = {})
43
47
  case value.to_s
44
- when FALSEY then false
48
+ when FALSEY, "" then false
45
49
  when TRUTHY then true
46
50
  else
47
51
  type = Locale.t("cmdx.types.boolean")
@@ -11,11 +11,6 @@ module CMDx
11
11
 
12
12
  extend self
13
13
 
14
- # Types that are already date-like and don't need conversion
15
- #
16
- # @rbs ANALOG_TYPES: Array[String]
17
- ANALOG_TYPES = %w[Date DateTime Time].freeze
18
-
19
14
  # Converts a value to a Date object
20
15
  #
21
16
  # @param value [Object] The value to convert to a Date
@@ -38,7 +33,7 @@ module CMDx
38
33
  #
39
34
  # @rbs (untyped value, ?Hash[Symbol, untyped] options) -> Date
40
35
  def call(value, options = {})
41
- return value if ANALOG_TYPES.include?(value.class.name)
36
+ return value.to_date if value.respond_to?(:to_date)
42
37
  return ::Date.strptime(value, options[:strptime]) if options[:strptime]
43
38
 
44
39
  ::Date.parse(value)
@@ -11,11 +11,6 @@ module CMDx
11
11
 
12
12
  extend self
13
13
 
14
- # Types that are already date-time-like and don't need conversion
15
- #
16
- # @rbs ANALOG_TYPES: Array[String]
17
- ANALOG_TYPES = %w[Date DateTime Time].freeze
18
-
19
14
  # Converts a value to a DateTime
20
15
  #
21
16
  # @param value [Object] The value to convert to DateTime
@@ -38,7 +33,7 @@ module CMDx
38
33
  #
39
34
  # @rbs (untyped value, ?Hash[Symbol, untyped] options) -> DateTime
40
35
  def call(value, options = {})
41
- return value if ANALOG_TYPES.include?(value.class.name)
36
+ return value.to_datetime if value.respond_to?(:to_datetime)
42
37
  return ::DateTime.strptime(value, options[:strptime]) if options[:strptime]
43
38
 
44
39
  ::DateTime.parse(value)
@@ -30,10 +30,9 @@ module CMDx
30
30
  # Integer.call(3.14) # => 3
31
31
  # Integer.call(0.0) # => 0
32
32
  # @example Handle edge cases
33
- # Integer.call("") # => 0
34
- # Integer.call(nil) # => 0
35
- # Integer.call(false) # => 0
36
- # Integer.call(true) # => 1
33
+ # Integer.call("") # => raises CoercionError
34
+ # Integer.call(nil) # => raises CoercionError
35
+ # Integer.call("abc") # => raises CoercionError
37
36
  #
38
37
  # @rbs (untyped value, ?Hash[Symbol, untyped] options) -> Integer
39
38
  def call(value, options = {})
@@ -11,11 +11,6 @@ module CMDx
11
11
 
12
12
  extend self
13
13
 
14
- # Types that are already time-like and don't need conversion
15
- #
16
- # @rbs ANALOG_TYPES: Array[String]
17
- ANALOG_TYPES = %w[DateTime Time].freeze
18
-
19
14
  # Converts a value to a Time object
20
15
  #
21
16
  # @param value [Object] The value to convert to a Time object
@@ -40,7 +35,6 @@ module CMDx
40
35
  #
41
36
  # @rbs (untyped value, ?Hash[Symbol, untyped] options) -> Time
42
37
  def call(value, options = {})
43
- return value if ANALOG_TYPES.include?(value.class.name)
44
38
  return value.to_time if value.respond_to?(:to_time)
45
39
  return ::Time.strptime(value, options[:strptime]) if options[:strptime]
46
40
 
data/lib/cmdx/executor.rb CHANGED
@@ -169,7 +169,7 @@ module CMDx
169
169
  jitter.to_f * current_retry
170
170
  end
171
171
 
172
- sleep(jitter) if jitter.positive?
172
+ sleep(jitter) if Float(jitter).positive?
173
173
 
174
174
  true
175
175
  end
@@ -291,8 +291,8 @@ module CMDx
291
291
  def log_backtrace!
292
292
  return unless result.failed?
293
293
 
294
- exception = result.caused_failure.cause
295
- return if exception.is_a?(Fault)
294
+ exception = result.caused_failure&.cause
295
+ return if exception.nil? || exception.is_a?(Fault)
296
296
 
297
297
  task.logger.error do
298
298
  "[#{exception.class}] #{exception.message}\n" <<
data/lib/cmdx/task.rb CHANGED
@@ -95,7 +95,9 @@ module CMDx
95
95
  @id = Identifier.generate
96
96
  @context = Context.build(context)
97
97
  @result = Result.new(self)
98
- @chain = Chain.build(@result, dry_run: @context.delete(:dry_run))
98
+
99
+ dry_run = @context.delete(:dry_run)
100
+ @chain = Chain.build(@result, dry_run:)
99
101
  end
100
102
 
101
103
  class << self
@@ -102,7 +102,7 @@ module CMDx
102
102
  match =
103
103
  if options.is_a?(Hash)
104
104
  case options
105
- in allow_nil: then allow_nil && value.nil?
105
+ in allow_nil: then !(allow_nil && value.nil?)
106
106
  else Utils::Condition.evaluate(task, options, value)
107
107
  end
108
108
  else
data/lib/cmdx/version.rb CHANGED
@@ -5,6 +5,6 @@ module CMDx
5
5
  # @return [String] the version of the CMDx gem
6
6
  #
7
7
  # @rbs return: String
8
- VERSION = "1.18.0"
8
+ VERSION = "1.19.0"
9
9
 
10
10
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cmdx
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.18.0
4
+ version: 1.19.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Juan Gomez