strict 1.4.0 → 2.0.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 +4 -4
- data/.agents/resume +4 -0
- data/.agents/setup +105 -0
- data/.rspec +2 -0
- data/.rubocop.yml +14 -4
- data/.tool-versions +1 -1
- data/API.md +335 -0
- data/CHANGELOG.md +110 -1
- data/Gemfile +13 -0
- data/Gemfile.lock +118 -42
- data/README.md +278 -4
- data/Rakefile +13 -7
- data/benchmark/baseline.rb +204 -0
- data/lib/strict/assignment_error.rb +5 -2
- data/lib/strict/attribute.rb +8 -47
- data/lib/strict/attributes/class.rb +0 -6
- data/lib/strict/attributes/coercer.rb +4 -3
- data/lib/strict/attributes/configuration.rb +15 -0
- data/lib/strict/attributes/dsl.rb +18 -10
- data/lib/strict/attributes/generated_methods.rb +113 -0
- data/lib/strict/attributes/instance.rb +32 -15
- data/lib/strict/configuration.rb +44 -0
- data/lib/strict/declaration.rb +109 -0
- data/lib/strict/detailed_validator.rb +9 -0
- data/lib/strict/error.rb +8 -1
- data/lib/strict/initialization_error.rb +11 -2
- data/lib/strict/interface.rb +39 -21
- data/lib/strict/interfaces/conformance.rb +125 -0
- data/lib/strict/interfaces/instance.rb +1 -49
- data/lib/strict/method.rb +72 -53
- data/lib/strict/method_call_error.rb +12 -2
- data/lib/strict/method_return_error.rb +2 -2
- data/lib/strict/methods/dsl.rb +16 -8
- data/lib/strict/methods/module.rb +27 -8
- data/lib/strict/methods/verifiable_method.rb +234 -70
- data/lib/strict/object.rb +1 -1
- data/lib/strict/parameter.rb +3 -52
- data/lib/strict/return.rb +14 -10
- data/lib/strict/rspec.rb +159 -0
- data/lib/strict/union.rb +180 -0
- data/lib/strict/unions/coercer.rb +55 -0
- data/lib/strict/validation.rb +40 -0
- data/lib/strict/validators/all_of.rb +11 -3
- data/lib/strict/validators/array_of.rb +18 -3
- data/lib/strict/validators/hash_of.rb +23 -3
- data/lib/strict/value.rb +18 -4
- data/lib/strict/version.rb +1 -1
- data/lib/strict/violation.rb +9 -0
- data/lib/strict.rb +42 -0
- data/sig/strict/rspec.rbs +10 -0
- data/sig/strict.rbs +213 -1
- data/strict.gemspec +1 -9
- metadata +20 -120
- data/lib/strict/accessor/attributes.rb +0 -15
- data/lib/strict/accessor/module.rb +0 -45
- data/lib/strict/reader/attributes.rb +0 -15
- data/lib/strict/reader/module.rb +0 -27
|
@@ -3,23 +3,42 @@
|
|
|
3
3
|
module Strict
|
|
4
4
|
module Methods
|
|
5
5
|
class Module < ::Module
|
|
6
|
-
|
|
6
|
+
def wrap(verifiable_method, invocation_target: :super)
|
|
7
|
+
case invocation_target
|
|
8
|
+
when :super
|
|
9
|
+
wrap_super(verifiable_method)
|
|
10
|
+
when :implementation
|
|
11
|
+
wrap_implementation(verifiable_method)
|
|
12
|
+
else
|
|
13
|
+
raise ArgumentError, "Unknown invocation target: #{invocation_target.inspect}"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
7
16
|
|
|
8
|
-
|
|
9
|
-
super()
|
|
17
|
+
private
|
|
10
18
|
|
|
11
|
-
|
|
19
|
+
def wrap_super(verifiable_method)
|
|
12
20
|
define_method verifiable_method.name do |*args, **kwargs, &block|
|
|
13
|
-
|
|
21
|
+
configuration = Strict.configuration
|
|
22
|
+
verified_arguments = verifiable_method.verify_parameters!(args, kwargs, configuration)
|
|
23
|
+
args, kwargs = verified_arguments if verified_arguments
|
|
14
24
|
|
|
15
25
|
super(*args, **kwargs, &block).tap do |value|
|
|
16
|
-
verifiable_method.verify_returns!(value)
|
|
26
|
+
verifiable_method.verify_returns!(value, configuration)
|
|
17
27
|
end
|
|
18
28
|
end
|
|
19
29
|
end
|
|
20
30
|
|
|
21
|
-
def
|
|
22
|
-
|
|
31
|
+
def wrap_implementation(verifiable_method)
|
|
32
|
+
method_name = verifiable_method.name
|
|
33
|
+
define_method method_name do |*args, **kwargs, &block|
|
|
34
|
+
configuration = Strict.configuration
|
|
35
|
+
verified_arguments = verifiable_method.verify_parameters!(args, kwargs, configuration)
|
|
36
|
+
args, kwargs = verified_arguments if verified_arguments
|
|
37
|
+
|
|
38
|
+
implementation.public_send(method_name, *args, **kwargs, &block).tap do |value|
|
|
39
|
+
verifiable_method.verify_returns!(value, configuration)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
23
42
|
end
|
|
24
43
|
end
|
|
25
44
|
end
|
|
@@ -1,11 +1,27 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "forwardable"
|
|
4
|
-
|
|
5
3
|
module Strict
|
|
6
4
|
module Methods
|
|
7
5
|
class VerifiableMethod # rubocop:disable Metrics/ClassLength
|
|
8
|
-
|
|
6
|
+
class << self
|
|
7
|
+
def from_method(method:, configuration:, instance:)
|
|
8
|
+
new(
|
|
9
|
+
method: method,
|
|
10
|
+
configuration: configuration,
|
|
11
|
+
instance: instance
|
|
12
|
+
)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def for_interface(owner:, name:, configuration:)
|
|
16
|
+
new(
|
|
17
|
+
method: nil,
|
|
18
|
+
owner: owner,
|
|
19
|
+
name: name,
|
|
20
|
+
configuration: configuration,
|
|
21
|
+
instance: true
|
|
22
|
+
)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
9
25
|
|
|
10
26
|
class UnknownParameterError < Error
|
|
11
27
|
attr_reader :parameter_name
|
|
@@ -24,25 +40,41 @@ module Strict
|
|
|
24
40
|
end
|
|
25
41
|
end
|
|
26
42
|
|
|
27
|
-
def_delegator :method, :name
|
|
28
|
-
|
|
29
43
|
attr_reader :parameters, :returns
|
|
30
44
|
|
|
31
|
-
def initialize(method:,
|
|
45
|
+
def initialize(method:, configuration:, instance:, owner: nil, name: nil)
|
|
32
46
|
@method = method
|
|
33
|
-
@parameters = parameters
|
|
34
|
-
@parameters_index = parameters.to_h { |
|
|
35
|
-
@returns = returns
|
|
47
|
+
@parameters = configuration.parameters
|
|
48
|
+
@parameters_index = parameters.to_h { |parameter| [parameter.name, parameter] }
|
|
49
|
+
@returns = configuration.returns
|
|
36
50
|
@instance = instance
|
|
51
|
+
compile_invocation(invocation_parameters_for(method))
|
|
52
|
+
return if method
|
|
53
|
+
|
|
54
|
+
@owner = owner
|
|
55
|
+
@name = name
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def compile_invocation(invocation_parameters)
|
|
59
|
+
@parameter_bindings = compile_parameter_bindings(invocation_parameters)
|
|
60
|
+
@keyword_parameter_names = parameter_bindings.filter_map do |binding|
|
|
61
|
+
binding.name if binding.kind == :keyword
|
|
62
|
+
end.freeze
|
|
63
|
+
@accepts_keyrest = parameter_bindings.any? { |binding| binding.kind == :keyrest }
|
|
64
|
+
end
|
|
65
|
+
private :compile_invocation
|
|
66
|
+
|
|
67
|
+
def name
|
|
68
|
+
method ? method.name : @name
|
|
37
69
|
end
|
|
38
70
|
|
|
39
71
|
def to_s
|
|
40
|
-
"#{
|
|
72
|
+
"#{owner}#{separator}#{name}"
|
|
41
73
|
end
|
|
42
74
|
|
|
43
75
|
def verify_definition!
|
|
44
76
|
expected_parameters = Set.new(parameters.map(&:name))
|
|
45
|
-
defined_parameters = Set.new(
|
|
77
|
+
defined_parameters = Set.new(parameter_bindings.map(&:name))
|
|
46
78
|
return if expected_parameters == defined_parameters
|
|
47
79
|
|
|
48
80
|
missing_parameters = expected_parameters - defined_parameters
|
|
@@ -54,97 +86,229 @@ module Strict
|
|
|
54
86
|
)
|
|
55
87
|
end
|
|
56
88
|
|
|
57
|
-
# rubocop:disable Metrics/AbcSize, Metrics/
|
|
58
|
-
|
|
59
|
-
def verify_parameters!(*args, **kwargs)
|
|
89
|
+
# rubocop:disable Metrics/AbcSize, Metrics/BlockLength, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
90
|
+
def verify_parameters!(args, kwargs, configuration)
|
|
60
91
|
invalid_parameters = nil
|
|
61
92
|
missing_parameters = nil
|
|
93
|
+
violations = nil
|
|
94
|
+
verified_args = nil
|
|
95
|
+
verified_kwargs = nil
|
|
96
|
+
positional_argument_count = args.length
|
|
97
|
+
positional_index = 0
|
|
62
98
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
99
|
+
parameter_bindings.each do |binding|
|
|
100
|
+
parameter = binding.parameter || parameter_named!(binding.name)
|
|
101
|
+
positional_start = positional_index
|
|
102
|
+
original_value = case binding.kind
|
|
103
|
+
when :positional
|
|
104
|
+
if positional_index < positional_argument_count
|
|
105
|
+
positional_index += 1
|
|
106
|
+
args.fetch(positional_start)
|
|
107
|
+
else
|
|
108
|
+
NOT_PROVIDED
|
|
109
|
+
end
|
|
110
|
+
when :optional_positional
|
|
111
|
+
if positional_argument_count - positional_index > binding.required_after
|
|
112
|
+
positional_index += 1
|
|
113
|
+
args.fetch(positional_start)
|
|
114
|
+
else
|
|
115
|
+
NOT_PROVIDED
|
|
116
|
+
end
|
|
117
|
+
when :rest
|
|
118
|
+
count = positional_argument_count - positional_index - binding.required_after
|
|
119
|
+
count = 0 if count.negative?
|
|
120
|
+
positional_index += count
|
|
121
|
+
if positional_start.zero? && count == positional_argument_count
|
|
122
|
+
args
|
|
123
|
+
else
|
|
124
|
+
args.slice(positional_start, count)
|
|
125
|
+
end
|
|
126
|
+
when :keyword
|
|
127
|
+
kwargs.key?(binding.name) ? kwargs.fetch(binding.name) : NOT_PROVIDED
|
|
128
|
+
when :keyrest
|
|
129
|
+
keyrest_value(kwargs)
|
|
130
|
+
end
|
|
85
131
|
|
|
86
|
-
|
|
87
|
-
if value.equal?(NOT_PROVIDED) && parameter.optional?
|
|
132
|
+
if original_value.equal?(NOT_PROVIDED) && parameter.optional?
|
|
88
133
|
value = parameter.default_generator.call
|
|
89
|
-
|
|
134
|
+
changed = true
|
|
135
|
+
elsif original_value.equal?(NOT_PROVIDED)
|
|
90
136
|
missing_parameters ||= []
|
|
91
137
|
missing_parameters << parameter.name
|
|
138
|
+
violations ||= []
|
|
139
|
+
violations << Validation.missing(parameter.validator, path: [parameter.name])
|
|
92
140
|
next
|
|
141
|
+
else
|
|
142
|
+
value = original_value
|
|
143
|
+
changed = false
|
|
93
144
|
end
|
|
94
145
|
|
|
95
146
|
value = parameter.coerce(value)
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
147
|
+
changed ||= !value.equal?(original_value)
|
|
148
|
+
parameter_violations = parameter.violations(value, configuration)
|
|
149
|
+
if parameter_violations.empty?
|
|
150
|
+
case binding.kind
|
|
151
|
+
when :positional, :optional_positional
|
|
152
|
+
if verified_args
|
|
153
|
+
verified_args << value
|
|
154
|
+
elsif changed
|
|
155
|
+
verified_args = args.take(positional_start)
|
|
156
|
+
verified_args << value
|
|
157
|
+
end
|
|
100
158
|
when :rest
|
|
101
|
-
|
|
159
|
+
if verified_args
|
|
160
|
+
verified_args.concat(value)
|
|
161
|
+
elsif rest_changed?(args, positional_start, positional_index, value, parameter.coercer)
|
|
162
|
+
verified_args = args.take(positional_start)
|
|
163
|
+
verified_args.concat(value)
|
|
164
|
+
end
|
|
102
165
|
when :keyword
|
|
103
|
-
|
|
166
|
+
if changed
|
|
167
|
+
verified_kwargs ||= kwargs.dup
|
|
168
|
+
verified_kwargs[binding.name] = value
|
|
169
|
+
end
|
|
104
170
|
when :keyrest
|
|
105
|
-
|
|
171
|
+
if keyrest_changed?(kwargs, value, parameter.coercer)
|
|
172
|
+
verified_kwargs = merge_keyrest(verified_kwargs, kwargs, value)
|
|
173
|
+
end
|
|
106
174
|
end
|
|
107
175
|
else
|
|
108
176
|
invalid_parameters ||= {}
|
|
109
177
|
invalid_parameters[parameter] = value
|
|
178
|
+
violations ||= []
|
|
179
|
+
violations.concat(Validation.prepend_path(parameter_violations, parameter.name))
|
|
110
180
|
end
|
|
111
181
|
end
|
|
112
182
|
|
|
113
|
-
if
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
183
|
+
if positional_index == positional_argument_count && no_additional_keywords?(kwargs) &&
|
|
184
|
+
invalid_parameters.nil? && missing_parameters.nil?
|
|
185
|
+
return if verified_args.nil? && verified_kwargs.nil?
|
|
186
|
+
|
|
187
|
+
return [verified_args || args, verified_kwargs || kwargs]
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
remaining_args = args.slice(positional_index, positional_argument_count - positional_index) || []
|
|
191
|
+
remaining_kwargs = remaining_kwargs_from(kwargs)
|
|
192
|
+
violations ||= []
|
|
193
|
+
remaining_args.each_with_index do |argument, offset|
|
|
194
|
+
violations << Validation.unexpected(argument, path: [positional_index + offset])
|
|
195
|
+
end
|
|
196
|
+
remaining_kwargs.each do |name, value|
|
|
197
|
+
violations << Validation.unexpected(value, path: [name])
|
|
123
198
|
end
|
|
199
|
+
raise Strict::MethodCallError.new(
|
|
200
|
+
verifiable_method: self,
|
|
201
|
+
remaining_args: remaining_args,
|
|
202
|
+
remaining_kwargs: remaining_kwargs,
|
|
203
|
+
invalid_parameters: invalid_parameters,
|
|
204
|
+
missing_parameters: missing_parameters,
|
|
205
|
+
violations: violations
|
|
206
|
+
)
|
|
124
207
|
end
|
|
125
|
-
# rubocop:enable Metrics/AbcSize, Metrics/
|
|
208
|
+
# rubocop:enable Metrics/AbcSize, Metrics/BlockLength, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
126
209
|
|
|
127
|
-
def verify_returns!(value)
|
|
128
|
-
|
|
129
|
-
return if
|
|
210
|
+
def verify_returns!(value, configuration)
|
|
211
|
+
violations = returns.violations(value, configuration)
|
|
212
|
+
return if violations.empty?
|
|
130
213
|
|
|
131
|
-
raise Strict::MethodReturnError.new(verifiable_method: self, value: value)
|
|
214
|
+
raise Strict::MethodReturnError.new(verifiable_method: self, value: value, violations: violations)
|
|
132
215
|
end
|
|
133
216
|
|
|
134
217
|
private
|
|
135
218
|
|
|
136
|
-
|
|
137
|
-
private_constant :
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
219
|
+
ParameterBinding = Data.define(:kind, :name, :parameter, :required_after)
|
|
220
|
+
private_constant :ParameterBinding
|
|
221
|
+
PARAMETER_KINDS = {
|
|
222
|
+
req: :positional,
|
|
223
|
+
opt: :optional_positional,
|
|
224
|
+
rest: :rest,
|
|
225
|
+
keyreq: :keyword,
|
|
226
|
+
key: :keyword,
|
|
227
|
+
keyrest: :keyrest
|
|
228
|
+
}.freeze
|
|
229
|
+
private_constant :PARAMETER_KINDS
|
|
144
230
|
NOT_PROVIDED = ::Object.new.freeze
|
|
145
231
|
private_constant :NOT_PROVIDED
|
|
146
232
|
|
|
147
|
-
attr_reader :method, :parameters_index
|
|
233
|
+
attr_reader :method, :parameters_index, :parameter_bindings, :keyword_parameter_names
|
|
234
|
+
|
|
235
|
+
def owner
|
|
236
|
+
method ? method.owner : @owner
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def invocation_parameters_for(method)
|
|
240
|
+
return method.parameters if method
|
|
241
|
+
|
|
242
|
+
parameters.map { |parameter| [:keyreq, parameter.name] }
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# rubocop:disable Metrics/MethodLength
|
|
246
|
+
def compile_parameter_bindings(invocation_parameters)
|
|
247
|
+
required_after = 0
|
|
248
|
+
invocation_parameters.reverse_each.filter_map do |kind, name|
|
|
249
|
+
compiled_kind = PARAMETER_KINDS[kind]
|
|
250
|
+
next unless compiled_kind
|
|
251
|
+
|
|
252
|
+
binding = ParameterBinding.new(
|
|
253
|
+
kind: compiled_kind,
|
|
254
|
+
name: name,
|
|
255
|
+
parameter: parameters_index[name],
|
|
256
|
+
required_after: required_after
|
|
257
|
+
)
|
|
258
|
+
required_after += 1 if kind == :req
|
|
259
|
+
binding
|
|
260
|
+
end.reverse.freeze
|
|
261
|
+
end
|
|
262
|
+
# rubocop:enable Metrics/MethodLength
|
|
263
|
+
|
|
264
|
+
def keyrest_value(kwargs)
|
|
265
|
+
return kwargs if keyword_parameter_names.empty?
|
|
266
|
+
|
|
267
|
+
kwargs.except(*keyword_parameter_names)
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def rest_changed?(args, start, finish, value, coercer)
|
|
271
|
+
return true if coercer
|
|
272
|
+
return false if value.equal?(args)
|
|
273
|
+
return true unless value.length == finish - start
|
|
274
|
+
|
|
275
|
+
value.each_index.any? { |index| !value.fetch(index).equal?(args.fetch(start + index)) }
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def keyrest_changed?(kwargs, value, coercer)
|
|
279
|
+
return true if coercer
|
|
280
|
+
return false if value.equal?(kwargs)
|
|
281
|
+
|
|
282
|
+
original_size = 0
|
|
283
|
+
kwargs.each do |name, original_value|
|
|
284
|
+
next if keyword_parameter_names.include?(name)
|
|
285
|
+
|
|
286
|
+
original_size += 1
|
|
287
|
+
return true unless value.key?(name) && value.fetch(name).equal?(original_value)
|
|
288
|
+
end
|
|
289
|
+
value.size != original_size
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def merge_keyrest(verified_kwargs, kwargs, value)
|
|
293
|
+
return value if keyword_parameter_names.empty? && !value.equal?(kwargs)
|
|
294
|
+
return verified_kwargs if keyword_parameter_names.empty?
|
|
295
|
+
|
|
296
|
+
merged_kwargs = verified_kwargs || kwargs.dup
|
|
297
|
+
merged_kwargs.delete_if do |name, _value|
|
|
298
|
+
!keyword_parameter_names.include?(name)
|
|
299
|
+
end
|
|
300
|
+
merged_kwargs.merge!(value) { |_name, explicit_value, _keyrest_value| explicit_value }
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
def no_additional_keywords?(kwargs)
|
|
304
|
+
@accepts_keyrest || kwargs.none? { |name, _value| !keyword_parameter_names.include?(name) }
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def remaining_kwargs_from(kwargs)
|
|
308
|
+
return {} if @accepts_keyrest
|
|
309
|
+
|
|
310
|
+
kwargs.except(*keyword_parameter_names)
|
|
311
|
+
end
|
|
148
312
|
|
|
149
313
|
def instance?
|
|
150
314
|
@instance
|
data/lib/strict/object.rb
CHANGED
data/lib/strict/parameter.rb
CHANGED
|
@@ -1,64 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Strict
|
|
4
|
-
class Parameter
|
|
5
|
-
NOT_PROVIDED = ::Object.new.freeze
|
|
6
|
-
|
|
4
|
+
class Parameter < Declaration
|
|
7
5
|
class << self
|
|
8
|
-
def make(name, validator = Validators::Anything.instance, coerce: false, **defaults)
|
|
9
|
-
unless valid_defaults?(**defaults)
|
|
10
|
-
raise ArgumentError, "Only one of 'default', 'default_value', or 'default_generator' can be provided"
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
new(
|
|
14
|
-
name: name.to_sym,
|
|
15
|
-
validator: validator,
|
|
16
|
-
default_generator: make_default_generator(**defaults),
|
|
17
|
-
coercer: coerce
|
|
18
|
-
)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
6
|
private
|
|
22
7
|
|
|
23
|
-
def
|
|
24
|
-
|
|
25
|
-
!default_option.equal?(NOT_PROVIDED)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
defaults_provided <= 1
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def make_default_generator(default: NOT_PROVIDED, default_value: NOT_PROVIDED, default_generator: NOT_PROVIDED)
|
|
32
|
-
if !default.equal?(NOT_PROVIDED)
|
|
33
|
-
default.respond_to?(:call) ? default : -> { default }
|
|
34
|
-
elsif !default_value.equal?(NOT_PROVIDED)
|
|
35
|
-
-> { default_value }
|
|
36
|
-
elsif !default_generator.equal?(NOT_PROVIDED)
|
|
37
|
-
default_generator
|
|
38
|
-
else
|
|
39
|
-
NOT_PROVIDED
|
|
40
|
-
end
|
|
8
|
+
def coercer_supported?(coercer)
|
|
9
|
+
super || coercer.respond_to?(:call)
|
|
41
10
|
end
|
|
42
11
|
end
|
|
43
12
|
|
|
44
|
-
attr_reader :name, :validator, :default_generator, :coercer
|
|
45
|
-
|
|
46
|
-
def initialize(name:, validator:, default_generator:, coercer:)
|
|
47
|
-
@name = name.to_sym
|
|
48
|
-
@validator = validator
|
|
49
|
-
@default_generator = default_generator
|
|
50
|
-
@coercer = coercer
|
|
51
|
-
@optional = !default_generator.equal?(NOT_PROVIDED)
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def optional?
|
|
55
|
-
@optional
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def valid?(value)
|
|
59
|
-
validator === value
|
|
60
|
-
end
|
|
61
|
-
|
|
62
13
|
def coerce(value)
|
|
63
14
|
return value unless coercer
|
|
64
15
|
|
data/lib/strict/return.rb
CHANGED
|
@@ -3,26 +3,30 @@
|
|
|
3
3
|
module Strict
|
|
4
4
|
class Return
|
|
5
5
|
class << self
|
|
6
|
-
def make(validator = Validators::Anything.instance,
|
|
7
|
-
|
|
6
|
+
def make(validator = Validators::Anything.instance, **unsupported)
|
|
7
|
+
raise ArgumentError, "Unsupported return options: #{unsupported.keys.join(', ')}" if unsupported.any?
|
|
8
|
+
|
|
9
|
+
new(validator: validator)
|
|
8
10
|
end
|
|
9
11
|
end
|
|
10
12
|
|
|
11
|
-
attr_reader :validator
|
|
13
|
+
attr_reader :validator
|
|
12
14
|
|
|
13
|
-
def initialize(validator
|
|
15
|
+
def initialize(validator:)
|
|
14
16
|
@validator = validator
|
|
15
|
-
@
|
|
17
|
+
@detailed_validator = DetailedValidator === validator
|
|
16
18
|
end
|
|
17
19
|
|
|
18
|
-
def valid?(value)
|
|
19
|
-
|
|
20
|
+
def valid?(value, configuration = Strict.configuration)
|
|
21
|
+
violations(value, configuration).empty?
|
|
20
22
|
end
|
|
21
23
|
|
|
22
|
-
def
|
|
23
|
-
return
|
|
24
|
+
def violations(value, configuration = Strict.configuration)
|
|
25
|
+
return Validation::NONE unless configuration.validate?
|
|
26
|
+
return validator.violations(value) if @detailed_validator
|
|
27
|
+
return Validation::NONE if validator === value
|
|
24
28
|
|
|
25
|
-
|
|
29
|
+
Validation.invalid(validator, value)
|
|
26
30
|
end
|
|
27
31
|
end
|
|
28
32
|
end
|