cmdx 1.16.0 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +23 -1
  3. data/README.md +3 -0
  4. data/lib/cmdx/attribute.rb +12 -0
  5. data/lib/cmdx/attribute_value.rb +22 -14
  6. data/lib/cmdx/chain.rb +20 -9
  7. data/lib/cmdx/coercions/array.rb +6 -3
  8. data/lib/cmdx/coercions/hash.rb +6 -2
  9. data/lib/cmdx/executor.rb +23 -0
  10. data/lib/cmdx/middlewares/correlate.rb +23 -12
  11. data/lib/cmdx/task.rb +45 -6
  12. data/lib/cmdx/version.rb +1 -1
  13. data/lib/locales/af.yml +3 -1
  14. data/lib/locales/ar.yml +3 -1
  15. data/lib/locales/az.yml +3 -1
  16. data/lib/locales/be.yml +3 -1
  17. data/lib/locales/bg.yml +3 -1
  18. data/lib/locales/bn.yml +3 -1
  19. data/lib/locales/bs.yml +3 -1
  20. data/lib/locales/ca.yml +3 -1
  21. data/lib/locales/cnr.yml +3 -1
  22. data/lib/locales/cs.yml +3 -1
  23. data/lib/locales/cy.yml +3 -1
  24. data/lib/locales/da.yml +3 -1
  25. data/lib/locales/de.yml +3 -1
  26. data/lib/locales/dz.yml +3 -1
  27. data/lib/locales/el.yml +3 -1
  28. data/lib/locales/en.yml +3 -1
  29. data/lib/locales/eo.yml +3 -1
  30. data/lib/locales/es.yml +3 -1
  31. data/lib/locales/et.yml +3 -1
  32. data/lib/locales/eu.yml +3 -1
  33. data/lib/locales/fa.yml +3 -1
  34. data/lib/locales/fi.yml +3 -1
  35. data/lib/locales/fr.yml +3 -1
  36. data/lib/locales/fy.yml +3 -1
  37. data/lib/locales/gd.yml +3 -1
  38. data/lib/locales/gl.yml +3 -1
  39. data/lib/locales/he.yml +3 -1
  40. data/lib/locales/hi.yml +3 -1
  41. data/lib/locales/hr.yml +3 -1
  42. data/lib/locales/hu.yml +3 -1
  43. data/lib/locales/hy.yml +3 -1
  44. data/lib/locales/id.yml +3 -1
  45. data/lib/locales/is.yml +3 -1
  46. data/lib/locales/it.yml +3 -1
  47. data/lib/locales/ja.yml +3 -1
  48. data/lib/locales/ka.yml +3 -1
  49. data/lib/locales/kk.yml +3 -1
  50. data/lib/locales/km.yml +3 -1
  51. data/lib/locales/kn.yml +3 -1
  52. data/lib/locales/ko.yml +3 -1
  53. data/lib/locales/lb.yml +3 -1
  54. data/lib/locales/lo.yml +3 -1
  55. data/lib/locales/lt.yml +3 -1
  56. data/lib/locales/lv.yml +3 -1
  57. data/lib/locales/mg.yml +3 -1
  58. data/lib/locales/mk.yml +3 -1
  59. data/lib/locales/ml.yml +3 -1
  60. data/lib/locales/mn.yml +3 -1
  61. data/lib/locales/mr-IN.yml +3 -1
  62. data/lib/locales/ms.yml +3 -1
  63. data/lib/locales/nb.yml +3 -1
  64. data/lib/locales/ne.yml +3 -1
  65. data/lib/locales/nl.yml +3 -1
  66. data/lib/locales/nn.yml +3 -1
  67. data/lib/locales/oc.yml +3 -1
  68. data/lib/locales/or.yml +3 -1
  69. data/lib/locales/pa.yml +3 -1
  70. data/lib/locales/pl.yml +3 -1
  71. data/lib/locales/pt.yml +3 -1
  72. data/lib/locales/rm.yml +3 -1
  73. data/lib/locales/ro.yml +3 -1
  74. data/lib/locales/ru.yml +3 -1
  75. data/lib/locales/sc.yml +3 -1
  76. data/lib/locales/sk.yml +3 -1
  77. data/lib/locales/sl.yml +3 -1
  78. data/lib/locales/sq.yml +3 -1
  79. data/lib/locales/sr.yml +3 -1
  80. data/lib/locales/st.yml +3 -1
  81. data/lib/locales/sv.yml +3 -1
  82. data/lib/locales/sw.yml +3 -1
  83. data/lib/locales/ta.yml +3 -1
  84. data/lib/locales/te.yml +3 -1
  85. data/lib/locales/th.yml +3 -1
  86. data/lib/locales/tl.yml +3 -1
  87. data/lib/locales/tr.yml +3 -1
  88. data/lib/locales/tt.yml +3 -1
  89. data/lib/locales/ug.yml +3 -1
  90. data/lib/locales/uk.yml +3 -1
  91. data/lib/locales/ur.yml +3 -1
  92. data/lib/locales/uz.yml +3 -1
  93. data/lib/locales/vi.yml +3 -1
  94. data/lib/locales/wo.yml +3 -1
  95. data/lib/locales/zh-CN.yml +3 -1
  96. data/lib/locales/zh-HK.yml +3 -1
  97. data/lib/locales/zh-TW.yml +3 -1
  98. data/lib/locales/zh-YUE.yml +3 -1
  99. data/mkdocs.yml +2 -0
  100. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d864a54c25ea1fd3a30f5c84b760bfa1f50207f7b26e47143876c76e2258b37a
4
- data.tar.gz: f1381fc4c62e761b0553800ccdd28a1aa0315e8dab4c2504692d936635b07063
3
+ metadata.gz: 80c55fa1f758dc69cebff57fa5dd770b4bd7692f4a37768c320b6813d28cc9fb
4
+ data.tar.gz: babf14b98faa436de370d039cc2b6c796f2d0c43d9e408137ccdd1f666f4abe3
5
5
  SHA512:
6
- metadata.gz: 3431a3d16f0ac92ebb52daa8c412d8f9e33975deab27482d6756e00935015100b3edeb008a419d28daf3daa5e3ba59456110b8ef9554a648ebda348d68152609
7
- data.tar.gz: b7837730a25c052356e45d56c69f2f41baa932362031f4f3f2fb9f5302020fff4ed8d690973afd0b482ba22a38d13091fe47f11b54635f1c321372bcbbf0cf60
6
+ metadata.gz: cbcb58370126bc18aec53218930b11a7c6a3194b4b1e1c6247ae2d6e693ef16f84cc4dbf90a7dbd816e900f421e625cac3ca227aca7ce4898fa711c1451230e0
7
+ data.tar.gz: de88ce41eacbf4f843803f84055d46dcfb7774aa5532396c820112a5ca3d80d68deb9dbc296f8b782d990122ed6ccc082bbc582f93d3714373c2705c55a936fe
data/CHANGELOG.md CHANGED
@@ -4,7 +4,29 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
- ## [Unreleased]
7
+ ## [UNRELEASED]
8
+
9
+ ## [1.18.0] - 2025-03-09
10
+
11
+ ### Changed
12
+ - Use `Fiber.storage` instead of `Thread.current` for `Chain` and `Correlate` storage, with fallback to `Thread.current` for Ruby < 3.2, making them thread and fiber safe
13
+ - Clone shared logger in `Task#logger` when `log_level` or `log_formatter` is customized to prevent mutation of the shared instance
14
+ - Derive attribute values from source objects that respond to the attribute name (via `send`) as fallback when the source is not callable
15
+
16
+ ## [1.17.0] - 2025-02-23
17
+
18
+ ### Added
19
+ - Add `returns` macro for context output validation after task execution
20
+ - Add `remove_return`/`remove_returns` macro to remove declared returns (supports inheritance)
21
+ - Add array coercion for JSON `"null"` string as empty array
22
+ - Add hash coercion for JSON `"null"` string as empty hash
23
+ - Add attribute sourcing to support both string and symbol keys when sourcing/deriving from Hash
24
+
25
+ ## Changed
26
+ - Do not fail coercion if nil on optional attributes
27
+ - Include the source method in the required attribute error message
28
+
29
+ ## [1.16.0] - 2025-02-06
8
30
 
9
31
  ### Added
10
32
  - Add `CMDx::Exception` alias for `CMDx::Error`
data/README.md CHANGED
@@ -59,8 +59,11 @@ class AnalyzeMetrics < CMDx::Task
59
59
  on_success :track_analysis_completion!
60
60
 
61
61
  required :dataset_id, type: :integer, numeric: { min: 1 }
62
+
62
63
  optional :analysis_type, default: "standard"
63
64
 
65
+ returns :result, :analyzed_at
66
+
64
67
  def work
65
68
  if dataset.nil?
66
69
  fail!("Dataset not found", code: 404)
@@ -184,6 +184,18 @@ module CMDx
184
184
 
185
185
  end
186
186
 
187
+ # Checks if the attribute is optional.
188
+ #
189
+ # @return [Boolean] true if the attribute is optional, false otherwise
190
+ #
191
+ # @example
192
+ # attribute.optional? # => true
193
+ #
194
+ # @rbs () -> bool
195
+ def optional?
196
+ !required? || !!options[:optional]
197
+ end
198
+
187
199
  # Checks if the attribute is required.
188
200
  #
189
201
  # @return [Boolean] true if the attribute is required, false otherwise
@@ -18,7 +18,7 @@ module CMDx
18
18
  # @rbs @attribute: Attribute
19
19
  attr_reader :attribute
20
20
 
21
- def_delegators :attribute, :task, :parent, :name, :options, :types, :source, :method_name, :required?
21
+ def_delegators :attribute, :task, :parent, :name, :options, :types, :source, :method_name, :required?, :optional?
22
22
  def_delegators :task, :attributes, :errors
23
23
 
24
24
  # Creates a new attribute value manager for the given attribute.
@@ -113,10 +113,11 @@ module CMDx
113
113
 
114
114
  if required? && (parent.nil? || parent&.required?) && Utils::Condition.evaluate(task, options)
115
115
  case sourced_value
116
- when Context, Hash then sourced_value.key?(name)
116
+ when Context then sourced_value.key?(name)
117
+ when Hash then sourced_value.key?(name.to_s) || sourced_value.key?(name.to_sym)
117
118
  when Proc then true # HACK: Cannot be determined so assume it will respond
118
119
  else sourced_value.respond_to?(name, true)
119
- end || errors.add(method_name, Locale.t("cmdx.attributes.required"))
120
+ end || errors.add(method_name, Locale.t("cmdx.attributes.required", method: source))
120
121
  end
121
122
 
122
123
  sourced_value
@@ -164,10 +165,16 @@ module CMDx
164
165
  def derive_value(source_value)
165
166
  derived_value =
166
167
  case source_value
167
- when Context, Hash then source_value[name]
168
+ when Context then source_value[name]
169
+ when Hash then source_value[name.to_s] || source_value[name.to_sym]
168
170
  when Symbol then source_value.send(name)
169
171
  when Proc then task.instance_exec(name, &source_value)
170
- else source_value.call(task, name) if source_value.respond_to?(:call)
172
+ else
173
+ if source_value.respond_to?(:call)
174
+ source_value.call(task, name)
175
+ elsif source_value.respond_to?(name, true)
176
+ source_value.send(name)
177
+ end
171
178
  end
172
179
 
173
180
  derived_value.nil? ? default_value : derived_value
@@ -222,16 +229,17 @@ module CMDx
222
229
  rescue CoercionError => e
223
230
  next if i != last_idx
224
231
 
225
- message =
226
- if last_idx.zero?
227
- e.message
228
- else
229
- tl = types.map { |t| Locale.t("cmdx.types.#{t}") }.join(", ")
230
- Locale.t("cmdx.coercions.into_any", types: tl)
231
- end
232
+ unless optional? && transformed_value.nil?
233
+ message =
234
+ if last_idx.zero?
235
+ e.message
236
+ else
237
+ tl = types.map { |t| Locale.t("cmdx.types.#{t}") }.join(", ")
238
+ Locale.t("cmdx.coercions.into_any", types: tl)
239
+ end
232
240
 
233
- errors.add(method_name, message)
234
- nil
241
+ errors.add(method_name, message)
242
+ end
235
243
  end
236
244
  end
237
245
 
data/lib/cmdx/chain.rb CHANGED
@@ -1,15 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CMDx
4
- # Manages a collection of task execution results in a thread-safe manner.
4
+ # Manages a collection of task execution results in a thread and fiber safe manner.
5
5
  # Chains provide a way to track related task executions and their outcomes
6
6
  # within the same execution context.
7
7
  class Chain
8
8
 
9
9
  extend Forwardable
10
10
 
11
- # @rbs THREAD_KEY: Symbol
12
- THREAD_KEY = :cmdx_chain
11
+ # @rbs CONCURRENCY_KEY: Symbol
12
+ CONCURRENCY_KEY = :cmdx_chain
13
13
 
14
14
  # Returns the unique identifier for this chain.
15
15
  #
@@ -47,7 +47,7 @@ module CMDx
47
47
 
48
48
  class << self
49
49
 
50
- # Retrieves the current chain for the current thread.
50
+ # Retrieves the current chain for the current execution context.
51
51
  #
52
52
  # @return [Chain, nil] The current chain or nil if none exists
53
53
  #
@@ -59,10 +59,10 @@ module CMDx
59
59
  #
60
60
  # @rbs () -> Chain?
61
61
  def current
62
- Thread.current[THREAD_KEY]
62
+ thread_or_fiber[CONCURRENCY_KEY]
63
63
  end
64
64
 
65
- # Sets the current chain for the current thread.
65
+ # Sets the current chain for the current execution context.
66
66
  #
67
67
  # @param chain [Chain] The chain to set as current
68
68
  #
@@ -73,10 +73,10 @@ module CMDx
73
73
  #
74
74
  # @rbs (Chain chain) -> Chain
75
75
  def current=(chain)
76
- Thread.current[THREAD_KEY] = chain
76
+ thread_or_fiber[CONCURRENCY_KEY] = chain
77
77
  end
78
78
 
79
- # Clears the current chain for the current thread.
79
+ # Clears the current chain for the current execution context.
80
80
  #
81
81
  # @return [nil] Always returns nil
82
82
  #
@@ -85,7 +85,7 @@ module CMDx
85
85
  #
86
86
  # @rbs () -> nil
87
87
  def clear
88
- Thread.current[THREAD_KEY] = nil
88
+ thread_or_fiber[CONCURRENCY_KEY] = nil
89
89
  end
90
90
 
91
91
  # Builds or extends the current chain by adding a result.
@@ -111,6 +111,17 @@ module CMDx
111
111
  current
112
112
  end
113
113
 
114
+ private
115
+
116
+ # Returns the thread or fiber storage for the current execution context.
117
+ #
118
+ # @return [Hash] The thread or fiber storage
119
+ #
120
+ # @rbs () -> Hash
121
+ def thread_or_fiber
122
+ Fiber.respond_to?(:storage) ? Fiber.storage : Thread.current
123
+ end
124
+
114
125
  end
115
126
 
116
127
  # Returns whether the chain is running in dry-run mode.
@@ -5,7 +5,7 @@ module CMDx
5
5
  # Converts various input types to Array format
6
6
  #
7
7
  # Handles conversion from strings that look like JSON arrays and other
8
- # values that can be converted to arrays using Ruby's Array() method.
8
+ # values that can be wrapped in an array using Ruby's Array() method.
9
9
  module Array
10
10
 
11
11
  extend self
@@ -29,8 +29,11 @@ module CMDx
29
29
  #
30
30
  # @rbs (untyped value, ?Hash[Symbol, untyped] options) -> Array[untyped]
31
31
  def call(value, options = {})
32
- if value.is_a?(::String) && value.start_with?("[")
33
- JSON.parse(value)
32
+ if value.is_a?(::String) && (
33
+ value.start_with?("[") ||
34
+ value.strip == "null"
35
+ )
36
+ JSON.parse(value) || []
34
37
  else
35
38
  Array(value)
36
39
  end
@@ -9,6 +9,7 @@ module CMDx
9
9
  # - Hash objects (returned as-is)
10
10
  # - Array objects (converted using Hash[*array])
11
11
  # - JSON strings starting with "{" (parsed into Hash)
12
+ # - JSON strings that are "null" (parsed into empty Hash)
12
13
  # - Other types raise CoercionError
13
14
  module Hash
14
15
 
@@ -39,8 +40,11 @@ module CMDx
39
40
  value
40
41
  elsif value.is_a?(::Array)
41
42
  ::Hash[*value]
42
- elsif value.is_a?(::String) && value.start_with?("{")
43
- JSON.parse(value)
43
+ elsif value.is_a?(::String) && (
44
+ value.start_with?("{") ||
45
+ value.strip == "null"
46
+ )
47
+ JSON.parse(value) || {}
44
48
  elsif value.respond_to?(:to_h)
45
49
  value.to_h
46
50
  else
data/lib/cmdx/executor.rb CHANGED
@@ -66,6 +66,7 @@ module CMDx
66
66
  task.class.settings[:middlewares].call!(task) do
67
67
  pre_execution! unless @pre_execution
68
68
  execution!
69
+ verify_returns!
69
70
  rescue UndefinedMethodError => e
70
71
  raise(e) # No need to clear the Chain since exception is not being re-raised
71
72
  rescue Fault => e
@@ -97,6 +98,7 @@ module CMDx
97
98
  task.class.settings[:middlewares].call!(task) do
98
99
  pre_execution! unless @pre_execution
99
100
  execution!
101
+ verify_returns!
100
102
  rescue UndefinedMethodError => e
101
103
  raise_exception(e)
102
104
  rescue Fault => e
@@ -231,6 +233,27 @@ module CMDx
231
233
  task.work
232
234
  end
233
235
 
236
+ # Verifies that all declared returns are present in the context after execution.
237
+ #
238
+ # @rbs () -> void
239
+ def verify_returns!
240
+ return unless result.success?
241
+
242
+ returns = Array(task.class.settings[:returns])
243
+ missing = returns.reject { |name| task.context.key?(name) }
244
+ return if missing.empty?
245
+
246
+ missing.each { |name| task.errors.add(name, Locale.t("cmdx.returns.missing")) }
247
+
248
+ result.fail!(
249
+ Locale.t("cmdx.faults.invalid"),
250
+ errors: {
251
+ full_message: task.errors.to_s,
252
+ messages: task.errors.to_h
253
+ }
254
+ )
255
+ end
256
+
234
257
  # Performs post-execution tasks including callback invocation.
235
258
  #
236
259
  # @rbs () -> void
@@ -4,18 +4,18 @@ module CMDx
4
4
  module Middlewares
5
5
  # Middleware for correlating task executions with unique identifiers.
6
6
  #
7
- # The Correlate middleware provides thread-safe correlation ID management
8
- # for tracking task execution flows across different operations.
9
- # It automatically generates correlation IDs when none are provided and
10
- # stores them in task result metadata for traceability.
7
+ # The Correlate middleware provides thread and fiber safe correlation ID management
8
+ # for tracking task execution flows across different operations. It automatically
9
+ # generates correlation IDs when none are provided and stores them in task result
10
+ # metadata for traceability.
11
11
  module Correlate
12
12
 
13
13
  extend self
14
14
 
15
- # @rbs THREAD_KEY: Symbol
16
- THREAD_KEY = :cmdx_correlate
15
+ # @rbs CONCURRENCY_KEY: Symbol
16
+ CONCURRENCY_KEY = :cmdx_correlate
17
17
 
18
- # Retrieves the current correlation ID from thread-local storage.
18
+ # Retrieves the current correlation ID from local storage.
19
19
  #
20
20
  # @return [String, nil] The current correlation ID or nil if not set
21
21
  #
@@ -24,10 +24,10 @@ module CMDx
24
24
  #
25
25
  # @rbs () -> String?
26
26
  def id
27
- Thread.current[THREAD_KEY]
27
+ thread_or_fiber[CONCURRENCY_KEY]
28
28
  end
29
29
 
30
- # Sets the correlation ID in thread-local storage.
30
+ # Sets the correlation ID in local storage.
31
31
  #
32
32
  # @param id [String] The correlation ID to set
33
33
  # @return [String] The set correlation ID
@@ -37,10 +37,10 @@ module CMDx
37
37
  #
38
38
  # @rbs (String id) -> String
39
39
  def id=(id)
40
- Thread.current[THREAD_KEY] = id
40
+ thread_or_fiber[CONCURRENCY_KEY] = id
41
41
  end
42
42
 
43
- # Clears the current correlation ID from thread-local storage.
43
+ # Clears the current correlation ID from local storage.
44
44
  #
45
45
  # @return [nil] Always returns nil
46
46
  #
@@ -49,7 +49,7 @@ module CMDx
49
49
  #
50
50
  # @rbs () -> nil
51
51
  def clear
52
- Thread.current[THREAD_KEY] = nil
52
+ thread_or_fiber[CONCURRENCY_KEY] = nil
53
53
  end
54
54
 
55
55
  # Temporarily uses a new correlation ID for the duration of a block.
@@ -122,6 +122,17 @@ module CMDx
122
122
  use(correlation_id, &)
123
123
  end
124
124
 
125
+ private
126
+
127
+ # Returns the thread or fiber storage for the current execution context.
128
+ #
129
+ # @return [Hash] The thread or fiber storage
130
+ #
131
+ # @rbs () -> Hash
132
+ def thread_or_fiber
133
+ Fiber.respond_to?(:storage) ? Fiber.storage : Thread.current
134
+ end
135
+
125
136
  end
126
137
  end
127
138
  end
data/lib/cmdx/task.rb CHANGED
@@ -130,6 +130,7 @@ module CMDx
130
130
  end
131
131
 
132
132
  hash[:attributes] ||= AttributeRegistry.new
133
+ hash[:returns] ||= []
133
134
  hash[:tags] ||= []
134
135
 
135
136
  hash.merge!(options)
@@ -217,6 +218,33 @@ module CMDx
217
218
  end
218
219
  alias remove_attribute remove_attributes
219
220
 
221
+ # Declares expected context returns that must be set after task execution.
222
+ # If any declared return is missing from the context after {#work} completes
223
+ # successfully, the task will fail with a validation error.
224
+ #
225
+ # @param names [Array<Symbol, String>] Names of expected return keys in the context
226
+ #
227
+ # @example
228
+ # returns :user, :token
229
+ #
230
+ # @rbs (*untyped names) -> void
231
+ def returns(*names)
232
+ settings[:returns] |= names.map(&:to_sym)
233
+ end
234
+
235
+ # Removes declared returns from the task.
236
+ #
237
+ # @param names [Array<Symbol>] Names of returns to remove
238
+ #
239
+ # @example
240
+ # remove_returns :old_return
241
+ #
242
+ # @rbs (*Symbol names) -> void
243
+ def remove_returns(*names)
244
+ settings[:returns] -= names.map(&:to_sym)
245
+ end
246
+ alias remove_return remove_returns
247
+
220
248
  # @return [Hash] Hash of attribute names to their configurations
221
249
  #
222
250
  # @example
@@ -231,8 +259,8 @@ module CMDx
231
259
  #
232
260
  # @rbs () -> Hash[Symbol, Hash[Symbol, untyped]]
233
261
  def attributes_schema
234
- Array(settings[:attributes]).each_with_object({}) do |attr, schema|
235
- schema[attr.method_name] = attr.to_h
262
+ Array(settings[:attributes]).to_h do |attr|
263
+ [attr.method_name, attr.to_h]
236
264
  end
237
265
  end
238
266
 
@@ -323,6 +351,10 @@ module CMDx
323
351
  raise UndefinedMethodError, "undefined method #{self.class.name}#work"
324
352
  end
325
353
 
354
+ # Returns a logger for this task. When a custom log_level or
355
+ # log_formatter is configured, the shared logger is duplicated
356
+ # so the original instance is never mutated.
357
+ #
326
358
  # @return [Logger] The logger instance for this task
327
359
  #
328
360
  # @example
@@ -332,10 +364,17 @@ module CMDx
332
364
  # @rbs () -> Logger
333
365
  def logger
334
366
  @logger ||= begin
335
- logger = self.class.settings[:logger] || CMDx.configuration.logger
336
- logger.level = self.class.settings[:log_level] || logger.level
337
- logger.formatter = self.class.settings[:log_formatter] || logger.formatter
338
- logger
367
+ log_instance = self.class.settings[:logger] || CMDx.configuration.logger
368
+ log_level = self.class.settings[:log_level]
369
+ log_formatter = self.class.settings[:log_formatter]
370
+
371
+ if log_level || log_formatter
372
+ log_instance = log_instance.dup
373
+ log_instance.level = log_level if log_level
374
+ log_instance.formatter = log_formatter if log_formatter
375
+ end
376
+
377
+ log_instance
339
378
  end
340
379
  end
341
380
 
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.16.0"
8
+ VERSION = "1.18.0"
9
9
 
10
10
  end
data/lib/locales/af.yml CHANGED
@@ -1,13 +1,15 @@
1
1
  af:
2
2
  cmdx:
3
3
  attributes:
4
- required: "moet toeganklik wees via die bron"
4
+ required: "moet toeganklik wees via die %{method} bronmetode"
5
5
  undefined: "delegeer na ongedefinieerde metode %{method}"
6
6
  coercions:
7
7
  into_a: "kon nie na %{type} omskep word nie"
8
8
  into_an: "kon nie na %{type} omskep word nie"
9
9
  into_any: "kon nie na een van: %{types} omskep word nie"
10
10
  unknown: "onbekende %{type} omskep tipe"
11
+ returns:
12
+ missing: "moet in die konteks gestel word"
11
13
  faults:
12
14
  invalid: "Ongeldig"
13
15
  unspecified: "Ongespesifiseer"
data/lib/locales/ar.yml CHANGED
@@ -1,13 +1,15 @@
1
1
  ar:
2
2
  cmdx:
3
3
  attributes:
4
- required: "يجب أن يكون قابلاً للوصول عبر المصدر"
4
+ required: "يجب أن يكون قابلاً للوصول عبر طريقة المصدر %{method}"
5
5
  undefined: "يفوض إلى طريقة غير معرفة %{method}"
6
6
  coercions:
7
7
  into_a: "لا يمكن تحويله إلى %{type}"
8
8
  into_an: "لا يمكن تحويله إلى %{type}"
9
9
  into_any: "لا يمكن تحويله إلى واحد من: %{types}"
10
10
  unknown: "نوع تحويل %{type} غير معروف"
11
+ returns:
12
+ missing: "يجب تعيينه في السياق"
11
13
  faults:
12
14
  invalid: "غير صالح"
13
15
  unspecified: "غير محدد"
data/lib/locales/az.yml CHANGED
@@ -1,13 +1,15 @@
1
1
  az:
2
2
  cmdx:
3
3
  attributes:
4
- required: "mənbə vasitəsilə əlçatan olmalıdır"
4
+ required: "%{method} mənbə metodu vasitəsilə əlçatan olmalıdır"
5
5
  undefined: "müəyyən edilməmiş metoda %{method} təyin edir"
6
6
  coercions:
7
7
  into_a: "%{type} tipinə çevrilə bilmədi"
8
8
  into_an: "%{type} tipinə çevrilə bilmədi"
9
9
  into_any: "aşağıdakılardan birinə çevrilə bilmədi: %{types}"
10
10
  unknown: "naməlum %{type} çevrilmə tipi"
11
+ returns:
12
+ missing: "kontekstdə təyin edilməlidir"
11
13
  faults:
12
14
  invalid: "Etibarsız"
13
15
  unspecified: "Göstərilməyib"
data/lib/locales/be.yml CHANGED
@@ -1,13 +1,15 @@
1
1
  be:
2
2
  cmdx:
3
3
  attributes:
4
- required: "павінна быць даступна праз крыніцу"
4
+ required: "павінен быць даступны праз зыходны метад %{method}"
5
5
  undefined: "дэлегуе невызначанаму метаду %{method}"
6
6
  coercions:
7
7
  into_a: "не ўдалося пераўтварыць у %{type}"
8
8
  into_an: "не ўдалося пераўтварыць у %{type}"
9
9
  into_any: "не ўдалося пераўтварыць у адзін з: %{types}"
10
10
  unknown: "невядомы тып пераўтварэння %{type}"
11
+ returns:
12
+ missing: "павінна быць усталявана ў кантэксце"
11
13
  faults:
12
14
  invalid: "Няправільныя"
13
15
  unspecified: "Не паказана"
data/lib/locales/bg.yml CHANGED
@@ -1,13 +1,15 @@
1
1
  bg:
2
2
  cmdx:
3
3
  attributes:
4
- required: "трябва да бъде достъпен чрез източника"
4
+ required: "трябва да е достъпен чрез изходния метод %{method}"
5
5
  undefined: "делегира към недефиниран метод %{method}"
6
6
  coercions:
7
7
  into_a: "не може да бъде преобразуван в %{type}"
8
8
  into_an: "не може да бъде преобразуван в %{type}"
9
9
  into_any: "не може да бъде преобразуван в един от: %{types}"
10
10
  unknown: "неизвестен тип преобразуване %{type}"
11
+ returns:
12
+ missing: "трябва да бъде зададено в контекста"
11
13
  faults:
12
14
  invalid: "Невалидни"
13
15
  unspecified: "Не е посочена"
data/lib/locales/bn.yml CHANGED
@@ -1,13 +1,15 @@
1
1
  bn:
2
2
  cmdx:
3
3
  attributes:
4
- required: "উৎসের মাধ্যমে অ্যাক্সেসযোগ্য হতে হবে"
4
+ required: "%{method} সোর্স মেথডের মাধ্যমে অ্যাক্সেসযোগ্য হতে হবে"
5
5
  undefined: "অসংজ্ঞায়িত পদ্ধতিতে %{method} অর্পণ করে"
6
6
  coercions:
7
7
  into_a: "%{type} এ রূপান্তর করা যায়নি"
8
8
  into_an: "%{type} এ রূপান্তর করা যায়নি"
9
9
  into_any: "নিম্নলিখিতগুলির মধ্যে একটিতে রূপান্তর করা যায়নি: %{types}"
10
10
  unknown: "অজানা %{type} রূপান্তর প্রকার"
11
+ returns:
12
+ missing: "প্রসঙ্গে সেট করতে হবে"
11
13
  faults:
12
14
  invalid: "অবৈধ"
13
15
  unspecified: "অনির্দিষ্ট"
data/lib/locales/bs.yml CHANGED
@@ -1,13 +1,15 @@
1
1
  bs:
2
2
  cmdx:
3
3
  attributes:
4
- required: "mora biti dostupan kroz izvor"
4
+ required: "mora biti dostupan preko izvorne metode %{method}"
5
5
  undefined: "delegira nedefiniranoj metodi %{method}"
6
6
  coercions:
7
7
  into_a: "nije mogao biti pretvoren u %{type}"
8
8
  into_an: "nije mogao biti pretvoren u %{type}"
9
9
  into_any: "nije mogao biti pretvoren u jedan od: %{types}"
10
10
  unknown: "nepoznati tip pretvorbe %{type}"
11
+ returns:
12
+ missing: "mora biti postavljeno u kontekstu"
11
13
  faults:
12
14
  invalid: "Neispravni"
13
15
  unspecified: "Nije naveden"
data/lib/locales/ca.yml CHANGED
@@ -1,13 +1,15 @@
1
1
  ca:
2
2
  cmdx:
3
3
  attributes:
4
- required: "ha de ser accessible a través de la font"
4
+ required: "ha de ser accessible mitjançant el mètode font %{method}"
5
5
  undefined: "delega al mètode no definit %{method}"
6
6
  coercions:
7
7
  into_a: "no es va poder convertir a %{type}"
8
8
  into_an: "no es va poder convertir a %{type}"
9
9
  into_any: "no es va poder convertir a un de: %{types}"
10
10
  unknown: "tipus de conversió %{type} desconegut"
11
+ returns:
12
+ missing: "ha de ser establert en el context"
11
13
  faults:
12
14
  invalid: "No vàlides"
13
15
  unspecified: "No especificat"
data/lib/locales/cnr.yml CHANGED
@@ -1,13 +1,15 @@
1
1
  cnr:
2
2
  cmdx:
3
3
  attributes:
4
- required: "mora biti dostupan preko izvora"
4
+ required: "mora biti dostupan preko izvorne metode %{method}"
5
5
  undefined: "delegira na nedefinisani metod %{method}"
6
6
  coercions:
7
7
  into_a: "nije mogao konvertovati u %{type}"
8
8
  into_an: "nije mogao konvertovati u %{type}"
9
9
  into_any: "nije mogao konvertovati u jedan od: %{types}"
10
10
  unknown: "nepoznati %{type} tip konverzije"
11
+ returns:
12
+ missing: "mora biti postavljeno u kontekstu"
11
13
  faults:
12
14
  invalid: "Neispravni"
13
15
  unspecified: "Nije dat"
data/lib/locales/cs.yml CHANGED
@@ -1,13 +1,15 @@
1
1
  cs:
2
2
  cmdx:
3
3
  attributes:
4
- required: "musí být přístupné přes zdroj"
4
+ required: "musí být přístupné přes zdrojovou metodu %{method}"
5
5
  undefined: "deleguje na nedefinovanou metodu %{method}"
6
6
  coercions:
7
7
  into_a: "nelze převést na %{type}"
8
8
  into_an: "nelze převést na %{type}"
9
9
  into_any: "nelze převést na jeden z: %{types}"
10
10
  unknown: "neznámý typ převodu %{type}"
11
+ returns:
12
+ missing: "musí být nastaveno v kontextu"
11
13
  faults:
12
14
  invalid: "Neplatné"
13
15
  unspecified: "Neurčeno"