cmdx 1.9.0 → 1.9.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/yardoc.md +1 -0
- data/CHANGELOG.md +6 -0
- data/LLM.md +9 -0
- data/README.md +6 -1
- data/docs/getting_started.md +9 -0
- data/docs/index.md +13 -1
- data/lib/cmdx/attribute.rb +82 -1
- data/lib/cmdx/attribute_registry.rb +20 -0
- data/lib/cmdx/attribute_value.rb +25 -0
- data/lib/cmdx/callback_registry.rb +19 -0
- data/lib/cmdx/chain.rb +34 -1
- data/lib/cmdx/coercion_registry.rb +18 -0
- data/lib/cmdx/coercions/array.rb +2 -0
- data/lib/cmdx/coercions/big_decimal.rb +3 -0
- data/lib/cmdx/coercions/boolean.rb +5 -0
- data/lib/cmdx/coercions/complex.rb +2 -0
- data/lib/cmdx/coercions/date.rb +4 -0
- data/lib/cmdx/coercions/date_time.rb +5 -0
- data/lib/cmdx/coercions/float.rb +2 -0
- data/lib/cmdx/coercions/hash.rb +2 -0
- data/lib/cmdx/coercions/integer.rb +2 -0
- data/lib/cmdx/coercions/rational.rb +2 -0
- data/lib/cmdx/coercions/string.rb +2 -0
- data/lib/cmdx/coercions/symbol.rb +2 -0
- data/lib/cmdx/coercions/time.rb +5 -0
- data/lib/cmdx/configuration.rb +111 -3
- data/lib/cmdx/context.rb +36 -0
- data/lib/cmdx/deprecator.rb +3 -0
- data/lib/cmdx/errors.rb +22 -0
- data/lib/cmdx/executor.rb +43 -0
- data/lib/cmdx/faults.rb +14 -0
- data/lib/cmdx/identifier.rb +2 -0
- data/lib/cmdx/locale.rb +3 -0
- data/lib/cmdx/log_formatters/json.rb +2 -0
- data/lib/cmdx/log_formatters/key_value.rb +2 -0
- data/lib/cmdx/log_formatters/line.rb +2 -0
- data/lib/cmdx/log_formatters/logstash.rb +2 -0
- data/lib/cmdx/log_formatters/raw.rb +2 -0
- data/lib/cmdx/middleware_registry.rb +20 -0
- data/lib/cmdx/middlewares/correlate.rb +11 -0
- data/lib/cmdx/middlewares/runtime.rb +4 -0
- data/lib/cmdx/middlewares/timeout.rb +4 -0
- data/lib/cmdx/pipeline.rb +20 -1
- data/lib/cmdx/railtie.rb +4 -0
- data/lib/cmdx/result.rb +123 -1
- data/lib/cmdx/task.rb +91 -1
- data/lib/cmdx/utils/call.rb +2 -0
- data/lib/cmdx/utils/condition.rb +3 -0
- data/lib/cmdx/utils/format.rb +5 -0
- data/lib/cmdx/validator_registry.rb +18 -0
- data/lib/cmdx/validators/exclusion.rb +2 -0
- data/lib/cmdx/validators/format.rb +2 -0
- data/lib/cmdx/validators/inclusion.rb +2 -0
- data/lib/cmdx/validators/length.rb +14 -0
- data/lib/cmdx/validators/numeric.rb +14 -0
- data/lib/cmdx/validators/presence.rb +2 -0
- data/lib/cmdx/version.rb +4 -1
- data/lib/cmdx/workflow.rb +10 -0
- data/lib/cmdx.rb +8 -0
- data/lib/generators/cmdx/locale_generator.rb +0 -1
- metadata +1 -1
|
@@ -10,6 +10,7 @@ module CMDx
|
|
|
10
10
|
|
|
11
11
|
extend self
|
|
12
12
|
|
|
13
|
+
# @rbs DEFAULT_PRECISION: Integer
|
|
13
14
|
DEFAULT_PRECISION = 14
|
|
14
15
|
|
|
15
16
|
# Converts a value to a BigDecimal
|
|
@@ -28,6 +29,8 @@ module CMDx
|
|
|
28
29
|
# @example Convert other numeric types
|
|
29
30
|
# BigDecimal.call(42) # => #<BigDecimal:7f8b8c0d8e0f '0.42E2',9(18)>
|
|
30
31
|
# BigDecimal.call(3.14159) # => #<BigDecimal:7f8b8c0d8e0f '0.314159E1',9(18)>
|
|
32
|
+
#
|
|
33
|
+
# @rbs (untyped value, ?Hash[Symbol, untyped] options) -> BigDecimal
|
|
31
34
|
def call(value, options = {})
|
|
32
35
|
BigDecimal(value, options[:precision] || DEFAULT_PRECISION)
|
|
33
36
|
rescue ArgumentError, TypeError
|
|
@@ -10,7 +10,10 @@ module CMDx
|
|
|
10
10
|
|
|
11
11
|
extend self
|
|
12
12
|
|
|
13
|
+
# @rbs FALSEY: Regexp
|
|
13
14
|
FALSEY = /^(false|f|no|n|0)$/i
|
|
15
|
+
|
|
16
|
+
# @rbs TRUTHY: Regexp
|
|
14
17
|
TRUTHY = /^(true|t|yes|y|1)$/i
|
|
15
18
|
|
|
16
19
|
# Converts a value to a Boolean
|
|
@@ -34,6 +37,8 @@ module CMDx
|
|
|
34
37
|
# @example Handle case-insensitive input
|
|
35
38
|
# Boolean.call("TRUE") # => true
|
|
36
39
|
# Boolean.call("False") # => false
|
|
40
|
+
#
|
|
41
|
+
# @rbs (untyped value, ?Hash[Symbol, untyped] options) -> bool
|
|
37
42
|
def call(value, options = {})
|
|
38
43
|
case value.to_s.downcase
|
|
39
44
|
when FALSEY then false
|
|
@@ -26,6 +26,8 @@ module CMDx
|
|
|
26
26
|
# Complex.call(5) # => (5+0i)
|
|
27
27
|
# Complex.call(3.14) # => (3.14+0i)
|
|
28
28
|
# Complex.call(Complex(1, 2)) # => (1+2i)
|
|
29
|
+
#
|
|
30
|
+
# @rbs (untyped value, ?Hash[Symbol, untyped] options) -> Complex
|
|
29
31
|
def call(value, options = {})
|
|
30
32
|
Complex(value)
|
|
31
33
|
rescue ArgumentError, TypeError
|
data/lib/cmdx/coercions/date.rb
CHANGED
|
@@ -12,6 +12,8 @@ module CMDx
|
|
|
12
12
|
extend self
|
|
13
13
|
|
|
14
14
|
# Types that are already date-like and don't need conversion
|
|
15
|
+
#
|
|
16
|
+
# @rbs ANALOG_TYPES: Array[String]
|
|
15
17
|
ANALOG_TYPES = %w[Date DateTime Time].freeze
|
|
16
18
|
|
|
17
19
|
# Converts a value to a Date object
|
|
@@ -33,6 +35,8 @@ module CMDx
|
|
|
33
35
|
# @example Return existing Date objects unchanged
|
|
34
36
|
# Date.call(Date.new(2023, 12, 25)) # => #<Date: 2023-12-25>
|
|
35
37
|
# Date.call(DateTime.new(2023, 12, 25)) # => #<Date: 2023-12-25>
|
|
38
|
+
#
|
|
39
|
+
# @rbs (untyped value, ?Hash[Symbol, untyped] options) -> Date
|
|
36
40
|
def call(value, options = {})
|
|
37
41
|
return value if ANALOG_TYPES.include?(value.class.name)
|
|
38
42
|
return ::Date.strptime(value, options[:strptime]) if options[:strptime]
|
|
@@ -11,6 +11,9 @@ 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]
|
|
14
17
|
ANALOG_TYPES = %w[Date DateTime Time].freeze
|
|
15
18
|
|
|
16
19
|
# Converts a value to a DateTime
|
|
@@ -32,6 +35,8 @@ module CMDx
|
|
|
32
35
|
# @example Convert existing date objects
|
|
33
36
|
# DateTime.call(Date.new(2023, 12, 25)) # => #<DateTime: 2023-12-25T00:00:00+00:00>
|
|
34
37
|
# DateTime.call(Time.new(2023, 12, 25)) # => #<DateTime: 2023-12-25T00:00:00+00:00>
|
|
38
|
+
#
|
|
39
|
+
# @rbs (untyped value, ?Hash[Symbol, untyped] options) -> DateTime
|
|
35
40
|
def call(value, options = {})
|
|
36
41
|
return value if ANALOG_TYPES.include?(value.class.name)
|
|
37
42
|
return ::DateTime.strptime(value, options[:strptime]) if options[:strptime]
|
data/lib/cmdx/coercions/float.rb
CHANGED
|
@@ -30,6 +30,8 @@ module CMDx
|
|
|
30
30
|
# Float.call(BigDecimal("123.456")) # => 123.456
|
|
31
31
|
# Float.call(Rational(3, 4)) # => 0.75
|
|
32
32
|
# Float.call(Complex(5.0, 0)) # => 5.0
|
|
33
|
+
#
|
|
34
|
+
# @rbs (untyped value, ?Hash[Symbol, untyped] options) -> Float
|
|
33
35
|
def call(value, options = {})
|
|
34
36
|
Float(value)
|
|
35
37
|
rescue ArgumentError, RangeError, TypeError
|
data/lib/cmdx/coercions/hash.rb
CHANGED
|
@@ -30,6 +30,8 @@ module CMDx
|
|
|
30
30
|
# Hash.call([:a, 1, :b, 2]) # => {a: 1, b: 2}
|
|
31
31
|
# @example Coerce from JSON string
|
|
32
32
|
# Hash.call('{"key": "value"}') # => {"key" => "value"}
|
|
33
|
+
#
|
|
34
|
+
# @rbs (untyped value, ?Hash[Symbol, untyped] options) -> Hash[untyped, untyped]
|
|
33
35
|
def call(value, options = {})
|
|
34
36
|
if value.nil?
|
|
35
37
|
{}
|
|
@@ -34,6 +34,8 @@ module CMDx
|
|
|
34
34
|
# Integer.call(nil) # => 0
|
|
35
35
|
# Integer.call(false) # => 0
|
|
36
36
|
# Integer.call(true) # => 1
|
|
37
|
+
#
|
|
38
|
+
# @rbs (untyped value, ?Hash[Symbol, untyped] options) -> Integer
|
|
37
39
|
def call(value, options = {})
|
|
38
40
|
Integer(value)
|
|
39
41
|
rescue ArgumentError, FloatDomainError, RangeError, TypeError
|
|
@@ -33,6 +33,8 @@ module CMDx
|
|
|
33
33
|
# Rational.call("") # => (0/1)
|
|
34
34
|
# Rational.call(nil) # => (0/1)
|
|
35
35
|
# Rational.call(0) # => (0/1)
|
|
36
|
+
#
|
|
37
|
+
# @rbs (untyped value, ?Hash[Symbol, untyped] options) -> Rational
|
|
36
38
|
def call(value, options = {})
|
|
37
39
|
Rational(value)
|
|
38
40
|
rescue ArgumentError, FloatDomainError, RangeError, TypeError, ZeroDivisionError
|
|
@@ -25,6 +25,8 @@ module CMDx
|
|
|
25
25
|
# Symbol.call("user_id") # => :user_id
|
|
26
26
|
# Symbol.call("") # => :""
|
|
27
27
|
# Symbol.call(:existing) # => :existing
|
|
28
|
+
#
|
|
29
|
+
# @rbs (untyped value, ?Hash[Symbol, untyped] options) -> Symbol
|
|
28
30
|
def call(value, options = {})
|
|
29
31
|
value.to_sym
|
|
30
32
|
rescue NoMethodError
|
data/lib/cmdx/coercions/time.rb
CHANGED
|
@@ -11,6 +11,9 @@ 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]
|
|
14
17
|
ANALOG_TYPES = %w[DateTime Time].freeze
|
|
15
18
|
|
|
16
19
|
# Converts a value to a Time object
|
|
@@ -34,6 +37,8 @@ module CMDx
|
|
|
34
37
|
# @example Convert strings with custom format
|
|
35
38
|
# Time.call("25/12/2023", strptime: "%d/%m/%Y") # => Time object
|
|
36
39
|
# Time.call("12-25-2023", strptime: "%m-%d-%Y") # => Time object
|
|
40
|
+
#
|
|
41
|
+
# @rbs (untyped value, ?Hash[Symbol, untyped] options) -> Time
|
|
37
42
|
def call(value, options = {})
|
|
38
43
|
return value if ANALOG_TYPES.include?(value.class.name)
|
|
39
44
|
return value.to_time if value.respond_to?(:to_time)
|
data/lib/cmdx/configuration.rb
CHANGED
|
@@ -6,11 +6,109 @@ module CMDx
|
|
|
6
6
|
# callbacks, coercions, validators, breakpoints, backtraces, and logging.
|
|
7
7
|
class Configuration
|
|
8
8
|
|
|
9
|
+
# @rbs DEFAULT_BREAKPOINTS: Array[String]
|
|
9
10
|
DEFAULT_BREAKPOINTS = %w[failed].freeze
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
# Returns the middleware registry for task execution.
|
|
13
|
+
#
|
|
14
|
+
# @return [MiddlewareRegistry] The middleware registry
|
|
15
|
+
#
|
|
16
|
+
# @example
|
|
17
|
+
# config.middlewares.register(CustomMiddleware)
|
|
18
|
+
#
|
|
19
|
+
# @rbs @middlewares: MiddlewareRegistry
|
|
20
|
+
attr_accessor :middlewares
|
|
21
|
+
|
|
22
|
+
# Returns the callback registry for task lifecycle hooks.
|
|
23
|
+
#
|
|
24
|
+
# @return [CallbackRegistry] The callback registry
|
|
25
|
+
#
|
|
26
|
+
# @example
|
|
27
|
+
# config.callbacks.register(:before_execution, :log_start)
|
|
28
|
+
#
|
|
29
|
+
# @rbs @callbacks: CallbackRegistry
|
|
30
|
+
attr_accessor :callbacks
|
|
31
|
+
|
|
32
|
+
# Returns the coercion registry for type conversions.
|
|
33
|
+
#
|
|
34
|
+
# @return [CoercionRegistry] The coercion registry
|
|
35
|
+
#
|
|
36
|
+
# @example
|
|
37
|
+
# config.coercions.register(:custom, CustomCoercion)
|
|
38
|
+
#
|
|
39
|
+
# @rbs @coercions: CoercionRegistry
|
|
40
|
+
attr_accessor :coercions
|
|
41
|
+
|
|
42
|
+
# Returns the validator registry for attribute validation.
|
|
43
|
+
#
|
|
44
|
+
# @return [ValidatorRegistry] The validator registry
|
|
45
|
+
#
|
|
46
|
+
# @example
|
|
47
|
+
# config.validators.register(:email, EmailValidator)
|
|
48
|
+
#
|
|
49
|
+
# @rbs @validators: ValidatorRegistry
|
|
50
|
+
attr_accessor :validators
|
|
51
|
+
|
|
52
|
+
# Returns the breakpoint statuses for task execution interruption.
|
|
53
|
+
#
|
|
54
|
+
# @return [Array<String>] Array of status names that trigger breakpoints
|
|
55
|
+
#
|
|
56
|
+
# @example
|
|
57
|
+
# config.task_breakpoints = ["failed", "skipped"]
|
|
58
|
+
#
|
|
59
|
+
# @rbs @task_breakpoints: Array[String]
|
|
60
|
+
attr_accessor :task_breakpoints
|
|
61
|
+
|
|
62
|
+
# Returns the breakpoint statuses for workflow execution interruption.
|
|
63
|
+
#
|
|
64
|
+
# @return [Array<String>] Array of status names that trigger breakpoints
|
|
65
|
+
#
|
|
66
|
+
# @example
|
|
67
|
+
# config.workflow_breakpoints = ["failed", "skipped"]
|
|
68
|
+
#
|
|
69
|
+
# @rbs @task_breakpoints: Array[String]
|
|
70
|
+
# @rbs @workflow_breakpoints: Array[String]
|
|
71
|
+
attr_accessor :workflow_breakpoints
|
|
72
|
+
|
|
73
|
+
# Returns the logger instance for CMDx operations.
|
|
74
|
+
#
|
|
75
|
+
# @return [Logger] The logger instance
|
|
76
|
+
#
|
|
77
|
+
# @example
|
|
78
|
+
# config.logger.level = Logger::DEBUG
|
|
79
|
+
#
|
|
80
|
+
# @rbs @logger: Logger
|
|
81
|
+
attr_accessor :logger
|
|
82
|
+
|
|
83
|
+
# Returns whether to log backtraces for failed tasks.
|
|
84
|
+
#
|
|
85
|
+
# @return [Boolean] true if backtraces should be logged
|
|
86
|
+
#
|
|
87
|
+
# @example
|
|
88
|
+
# config.backtrace = true
|
|
89
|
+
#
|
|
90
|
+
# @rbs @backtrace: bool
|
|
91
|
+
attr_accessor :backtrace
|
|
92
|
+
|
|
93
|
+
# Returns the proc used to clean backtraces before logging.
|
|
94
|
+
#
|
|
95
|
+
# @return [Proc, nil] The backtrace cleaner proc, or nil if not set
|
|
96
|
+
#
|
|
97
|
+
# @example
|
|
98
|
+
# config.backtrace_cleaner = ->(bt) { bt.first(5) }
|
|
99
|
+
#
|
|
100
|
+
# @rbs @backtrace_cleaner: (Proc | nil)
|
|
101
|
+
attr_accessor :backtrace_cleaner
|
|
102
|
+
|
|
103
|
+
# Returns the proc called when exceptions occur during execution.
|
|
104
|
+
#
|
|
105
|
+
# @return [Proc, nil] The exception handler proc, or nil if not set
|
|
106
|
+
#
|
|
107
|
+
# @example
|
|
108
|
+
# config.exception_handler = ->(task, error) { Sentry.capture_exception(error) }
|
|
109
|
+
#
|
|
110
|
+
# @rbs @exception_handler: (Proc | nil)
|
|
111
|
+
attr_accessor :exception_handler
|
|
14
112
|
|
|
15
113
|
# Initializes a new Configuration instance with default values.
|
|
16
114
|
#
|
|
@@ -23,6 +121,8 @@ module CMDx
|
|
|
23
121
|
# config = Configuration.new
|
|
24
122
|
# config.middlewares.class # => MiddlewareRegistry
|
|
25
123
|
# config.task_breakpoints # => ["failed"]
|
|
124
|
+
#
|
|
125
|
+
# @rbs () -> void
|
|
26
126
|
def initialize
|
|
27
127
|
@middlewares = MiddlewareRegistry.new
|
|
28
128
|
@callbacks = CallbackRegistry.new
|
|
@@ -52,6 +152,8 @@ module CMDx
|
|
|
52
152
|
# config = Configuration.new
|
|
53
153
|
# config.to_h
|
|
54
154
|
# # => { middlewares: #<MiddlewareRegistry>, callbacks: #<CallbackRegistry>, ... }
|
|
155
|
+
#
|
|
156
|
+
# @rbs () -> Hash[Symbol, untyped]
|
|
55
157
|
def to_h
|
|
56
158
|
{
|
|
57
159
|
middlewares: @middlewares,
|
|
@@ -78,6 +180,8 @@ module CMDx
|
|
|
78
180
|
# @example
|
|
79
181
|
# config = CMDx.configuration
|
|
80
182
|
# config.middlewares # => #<MiddlewareRegistry>
|
|
183
|
+
#
|
|
184
|
+
# @rbs () -> Configuration
|
|
81
185
|
def configuration
|
|
82
186
|
return @configuration if @configuration
|
|
83
187
|
|
|
@@ -99,6 +203,8 @@ module CMDx
|
|
|
99
203
|
# config.task_breakpoints = ["failed", "skipped"]
|
|
100
204
|
# config.logger.level = Logger::DEBUG
|
|
101
205
|
# end
|
|
206
|
+
#
|
|
207
|
+
# @rbs () { (Configuration) -> void } -> Configuration
|
|
102
208
|
def configure
|
|
103
209
|
raise ArgumentError, "block required" unless block_given?
|
|
104
210
|
|
|
@@ -114,6 +220,8 @@ module CMDx
|
|
|
114
220
|
# @example
|
|
115
221
|
# CMDx.reset_configuration!
|
|
116
222
|
# # Configuration is now reset to defaults
|
|
223
|
+
#
|
|
224
|
+
# @rbs () -> Configuration
|
|
117
225
|
def reset_configuration!
|
|
118
226
|
@configuration = Configuration.new
|
|
119
227
|
end
|
data/lib/cmdx/context.rb
CHANGED
|
@@ -11,6 +11,14 @@ module CMDx
|
|
|
11
11
|
|
|
12
12
|
extend Forwardable
|
|
13
13
|
|
|
14
|
+
# Returns the internal hash storing context data.
|
|
15
|
+
#
|
|
16
|
+
# @return [Hash{Symbol => Object}] The internal hash table
|
|
17
|
+
#
|
|
18
|
+
# @example
|
|
19
|
+
# context.table # => { name: "John", age: 30 }
|
|
20
|
+
#
|
|
21
|
+
# @rbs @table: Hash[Symbol, untyped]
|
|
14
22
|
attr_reader :table
|
|
15
23
|
alias to_h table
|
|
16
24
|
|
|
@@ -28,6 +36,8 @@ module CMDx
|
|
|
28
36
|
# @example
|
|
29
37
|
# context = Context.new(name: "John", age: 30)
|
|
30
38
|
# context[:name] # => "John"
|
|
39
|
+
#
|
|
40
|
+
# @rbs (untyped args) -> void
|
|
31
41
|
def initialize(args = {})
|
|
32
42
|
@table =
|
|
33
43
|
if args.respond_to?(:to_hash)
|
|
@@ -50,6 +60,8 @@ module CMDx
|
|
|
50
60
|
# existing = Context.new(name: "John")
|
|
51
61
|
# built = Context.build(existing) # reuses existing context
|
|
52
62
|
# built.object_id == existing.object_id # => true
|
|
63
|
+
#
|
|
64
|
+
# @rbs (untyped context) -> Context
|
|
53
65
|
def self.build(context = {})
|
|
54
66
|
if context.is_a?(self) && !context.frozen?
|
|
55
67
|
context
|
|
@@ -70,6 +82,8 @@ module CMDx
|
|
|
70
82
|
# context = Context.new(name: "John")
|
|
71
83
|
# context[:name] # => "John"
|
|
72
84
|
# context["name"] # => "John" (automatically converted to symbol)
|
|
85
|
+
#
|
|
86
|
+
# @rbs ((String | Symbol) key) -> untyped
|
|
73
87
|
def [](key)
|
|
74
88
|
table[key.to_sym]
|
|
75
89
|
end
|
|
@@ -85,6 +99,8 @@ module CMDx
|
|
|
85
99
|
# context = Context.new
|
|
86
100
|
# context.store(:name, "John")
|
|
87
101
|
# context[:name] # => "John"
|
|
102
|
+
#
|
|
103
|
+
# @rbs ((String | Symbol) key, untyped value) -> untyped
|
|
88
104
|
def store(key, value)
|
|
89
105
|
table[key.to_sym] = value
|
|
90
106
|
end
|
|
@@ -104,6 +120,8 @@ module CMDx
|
|
|
104
120
|
# context.fetch(:name) # => "John"
|
|
105
121
|
# context.fetch(:age, 25) # => 25
|
|
106
122
|
# context.fetch(:city) { |key| "Unknown #{key}" } # => "Unknown city"
|
|
123
|
+
#
|
|
124
|
+
# @rbs ((String | Symbol) key, *untyped) ?{ ((String | Symbol)) -> untyped } -> untyped
|
|
107
125
|
def fetch(key, ...)
|
|
108
126
|
table.fetch(key.to_sym, ...)
|
|
109
127
|
end
|
|
@@ -122,6 +140,8 @@ module CMDx
|
|
|
122
140
|
# context.fetch_or_store(:name, "Default") # => "John" (existing value)
|
|
123
141
|
# context.fetch_or_store(:age, 25) # => 25 (stored and returned)
|
|
124
142
|
# context.fetch_or_store(:city) { |key| "Unknown #{key}" } # => "Unknown city" (stored and returned)
|
|
143
|
+
#
|
|
144
|
+
# @rbs ((String | Symbol) key, ?untyped value) ?{ () -> untyped } -> untyped
|
|
125
145
|
def fetch_or_store(key, value = nil)
|
|
126
146
|
table.fetch(key.to_sym) do
|
|
127
147
|
table[key.to_sym] = block_given? ? yield : value
|
|
@@ -139,6 +159,8 @@ module CMDx
|
|
|
139
159
|
# context = Context.new(name: "John")
|
|
140
160
|
# context.merge!(age: 30, city: "NYC")
|
|
141
161
|
# context.to_h # => {name: "John", age: 30, city: "NYC"}
|
|
162
|
+
#
|
|
163
|
+
# @rbs (?untyped args) -> self
|
|
142
164
|
def merge!(args = {})
|
|
143
165
|
args.to_h.each { |key, value| self[key.to_sym] = value }
|
|
144
166
|
self
|
|
@@ -156,6 +178,8 @@ module CMDx
|
|
|
156
178
|
# context = Context.new(name: "John", age: 30)
|
|
157
179
|
# context.delete!(:age) # => 30
|
|
158
180
|
# context.delete!(:city) { |key| "Key #{key} not found" } # => "Key city not found"
|
|
181
|
+
#
|
|
182
|
+
# @rbs ((String | Symbol) key) ?{ ((String | Symbol)) -> untyped } -> untyped
|
|
159
183
|
def delete!(key, &)
|
|
160
184
|
table.delete(key.to_sym, &)
|
|
161
185
|
end
|
|
@@ -170,6 +194,8 @@ module CMDx
|
|
|
170
194
|
# context1 = Context.new(name: "John")
|
|
171
195
|
# context2 = Context.new(name: "John")
|
|
172
196
|
# context1 == context2 # => true
|
|
197
|
+
#
|
|
198
|
+
# @rbs (untyped other) -> bool
|
|
173
199
|
def eql?(other)
|
|
174
200
|
other.is_a?(self.class) && (to_h == other.to_h)
|
|
175
201
|
end
|
|
@@ -185,6 +211,8 @@ module CMDx
|
|
|
185
211
|
# context = Context.new(name: "John")
|
|
186
212
|
# context.key?(:name) # => true
|
|
187
213
|
# context.key?(:age) # => false
|
|
214
|
+
#
|
|
215
|
+
# @rbs ((String | Symbol) key) -> bool
|
|
188
216
|
def key?(key)
|
|
189
217
|
table.key?(key.to_sym)
|
|
190
218
|
end
|
|
@@ -200,6 +228,8 @@ module CMDx
|
|
|
200
228
|
# context = Context.new(user: {profile: {name: "John"}})
|
|
201
229
|
# context.dig(:user, :profile, :name) # => "John"
|
|
202
230
|
# context.dig(:user, :profile, :age) # => nil
|
|
231
|
+
#
|
|
232
|
+
# @rbs ((String | Symbol) key, *(String | Symbol) keys) -> untyped
|
|
203
233
|
def dig(key, *keys)
|
|
204
234
|
table.dig(key.to_sym, *keys)
|
|
205
235
|
end
|
|
@@ -211,6 +241,8 @@ module CMDx
|
|
|
211
241
|
# @example
|
|
212
242
|
# context = Context.new(name: "John", age: 30)
|
|
213
243
|
# context.to_s # => "name: John, age: 30"
|
|
244
|
+
#
|
|
245
|
+
# @rbs () -> String
|
|
214
246
|
def to_s
|
|
215
247
|
Utils::Format.to_str(to_h)
|
|
216
248
|
end
|
|
@@ -227,6 +259,8 @@ module CMDx
|
|
|
227
259
|
# @yield [Object] optional block
|
|
228
260
|
#
|
|
229
261
|
# @return [Object] the result of the method call
|
|
262
|
+
#
|
|
263
|
+
# @rbs (Symbol method_name, *untyped args, **untyped _kwargs) ?{ () -> untyped } -> untyped
|
|
230
264
|
def method_missing(method_name, *args, **_kwargs, &)
|
|
231
265
|
fetch(method_name) do
|
|
232
266
|
store(method_name[0..-2], args.first) if method_name.end_with?("=")
|
|
@@ -244,6 +278,8 @@ module CMDx
|
|
|
244
278
|
# context = Context.new(name: "John")
|
|
245
279
|
# context.respond_to?(:name) # => true
|
|
246
280
|
# context.respond_to?(:age) # => false
|
|
281
|
+
#
|
|
282
|
+
# @rbs (Symbol method_name, ?bool include_private) -> bool
|
|
247
283
|
def respond_to_missing?(method_name, include_private = false)
|
|
248
284
|
key?(method_name) || super
|
|
249
285
|
end
|
data/lib/cmdx/deprecator.rb
CHANGED
|
@@ -10,6 +10,7 @@ module CMDx
|
|
|
10
10
|
|
|
11
11
|
extend self
|
|
12
12
|
|
|
13
|
+
# @rbs EVAL: Proc
|
|
13
14
|
EVAL = proc do |target, callable|
|
|
14
15
|
case callable
|
|
15
16
|
when /raise|log|warn/ then callable
|
|
@@ -45,6 +46,8 @@ module CMDx
|
|
|
45
46
|
# end
|
|
46
47
|
#
|
|
47
48
|
# MyTask.new # => [MyTask] DEPRECATED: migrate to a replacement or discontinue use
|
|
49
|
+
#
|
|
50
|
+
# @rbs (Task task) -> void
|
|
48
51
|
def restrict(task)
|
|
49
52
|
type = EVAL.call(task, task.class.settings[:deprecate])
|
|
50
53
|
|
data/lib/cmdx/errors.rb
CHANGED
|
@@ -8,11 +8,21 @@ module CMDx
|
|
|
8
8
|
|
|
9
9
|
extend Forwardable
|
|
10
10
|
|
|
11
|
+
# Returns the internal hash of error messages by attribute.
|
|
12
|
+
#
|
|
13
|
+
# @return [Hash{Symbol => Set<String>}] Hash mapping attribute names to error message sets
|
|
14
|
+
#
|
|
15
|
+
# @example
|
|
16
|
+
# errors.messages # => { email: #<Set: ["must be valid", "is required"]> }
|
|
17
|
+
#
|
|
18
|
+
# @rbs @messages: Hash[Symbol, Set[String]]
|
|
11
19
|
attr_reader :messages
|
|
12
20
|
|
|
13
21
|
def_delegators :messages, :empty?
|
|
14
22
|
|
|
15
23
|
# Initialize a new error collection.
|
|
24
|
+
#
|
|
25
|
+
# @rbs () -> void
|
|
16
26
|
def initialize
|
|
17
27
|
@messages = {}
|
|
18
28
|
end
|
|
@@ -26,6 +36,8 @@ module CMDx
|
|
|
26
36
|
# errors = CMDx::Errors.new
|
|
27
37
|
# errors.add(:email, "must be valid format")
|
|
28
38
|
# errors.add(:email, "cannot be blank")
|
|
39
|
+
#
|
|
40
|
+
# @rbs (Symbol attribute, String message) -> void
|
|
29
41
|
def add(attribute, message)
|
|
30
42
|
return if message.empty?
|
|
31
43
|
|
|
@@ -42,6 +54,8 @@ module CMDx
|
|
|
42
54
|
# @example
|
|
43
55
|
# errors.for?(:email) # => true
|
|
44
56
|
# errors.for?(:name) # => false
|
|
57
|
+
#
|
|
58
|
+
# @rbs (Symbol attribute) -> bool
|
|
45
59
|
def for?(attribute)
|
|
46
60
|
return false unless messages.key?(attribute)
|
|
47
61
|
|
|
@@ -54,6 +68,8 @@ module CMDx
|
|
|
54
68
|
#
|
|
55
69
|
# @example
|
|
56
70
|
# errors.full_messages # => { email: ["email must be valid format", "email cannot be blank"] }
|
|
71
|
+
#
|
|
72
|
+
# @rbs () -> Hash[Symbol, Array[String]]
|
|
57
73
|
def full_messages
|
|
58
74
|
messages.each_with_object({}) do |(attribute, messages), hash|
|
|
59
75
|
hash[attribute] = messages.map { |message| "#{attribute} #{message}" }
|
|
@@ -66,6 +82,8 @@ module CMDx
|
|
|
66
82
|
#
|
|
67
83
|
# @example
|
|
68
84
|
# errors.to_h # => { email: ["must be valid format", "cannot be blank"] }
|
|
85
|
+
#
|
|
86
|
+
# @rbs () -> Hash[Symbol, Array[String]]
|
|
69
87
|
def to_h
|
|
70
88
|
messages.transform_values(&:to_a)
|
|
71
89
|
end
|
|
@@ -78,6 +96,8 @@ module CMDx
|
|
|
78
96
|
# @example
|
|
79
97
|
# errors.to_hash # => { email: ["must be valid format", "cannot be blank"] }
|
|
80
98
|
# errors.to_hash(true) # => { email: ["email must be valid format", "email cannot be blank"] }
|
|
99
|
+
#
|
|
100
|
+
# @rbs (?bool full) -> Hash[Symbol, Array[String]]
|
|
81
101
|
def to_hash(full = false)
|
|
82
102
|
full ? full_messages : to_h
|
|
83
103
|
end
|
|
@@ -88,6 +108,8 @@ module CMDx
|
|
|
88
108
|
#
|
|
89
109
|
# @example
|
|
90
110
|
# errors.to_s # => "email must be valid format. email cannot be blank"
|
|
111
|
+
#
|
|
112
|
+
# @rbs () -> String
|
|
91
113
|
def to_s
|
|
92
114
|
full_messages.values.flatten.join(". ")
|
|
93
115
|
end
|