validate-rb 0.1.0.alpha.1 → 0.1.0.pre
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/.gitignore +12 -0
- data/.rspec +3 -0
- data/.ruby-version +1 -0
- data/.travis.yml +6 -0
- data/Gemfile +7 -0
- data/README.md +4 -4
- data/Rakefile +6 -0
- data/bin/console +7 -0
- data/bin/setup +6 -0
- data/lib/validate/version.rb +1 -1
- data/lib/validate-rb.rb +3 -0
- data/lib/validate.rb +1 -75
- data/validate-rb.gemspec +30 -0
- metadata +21 -122
- data/LICENSE +0 -21
- data/lib/validate/arguments.rb +0 -93
- data/lib/validate/assertions.rb +0 -27
- data/lib/validate/ast.rb +0 -381
- data/lib/validate/compare.rb +0 -34
- data/lib/validate/constraint.rb +0 -217
- data/lib/validate/constraints/validation_context.rb +0 -167
- data/lib/validate/constraints.rb +0 -337
- data/lib/validate/errors.rb +0 -40
- data/lib/validate/helpers.rb +0 -11
- data/lib/validate/scope.rb +0 -48
- data/lib/validate/validators/dsl.rb +0 -44
- data/lib/validate/validators.rb +0 -7
data/lib/validate/ast.rb
DELETED
@@ -1,381 +0,0 @@
|
|
1
|
-
# typed: strict
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
module Validate
|
5
|
-
module AST
|
6
|
-
def self.build(*args, &block)
|
7
|
-
Generator.new
|
8
|
-
.generate(*args, &block)
|
9
|
-
.freeze
|
10
|
-
end
|
11
|
-
|
12
|
-
class DefinitionContext
|
13
|
-
module Builder
|
14
|
-
module_function
|
15
|
-
|
16
|
-
def all_constraints(*constraints)
|
17
|
-
Rules::Unanimous.new(constraints.map { |node| send(*node) })
|
18
|
-
end
|
19
|
-
|
20
|
-
def at_least_one_constraint(*constraints)
|
21
|
-
Rules::Affirmative.new(constraints.map { |node| send(*node) })
|
22
|
-
end
|
23
|
-
|
24
|
-
def no_constraints(*constraints)
|
25
|
-
Rules::Negative.new(constraints.map { |node| send(*node) })
|
26
|
-
end
|
27
|
-
|
28
|
-
def constraint(name, args, block, trace)
|
29
|
-
if defined?(Constraints) && Constraints.respond_to?(name)
|
30
|
-
begin
|
31
|
-
return Constraints.send(name, *(args.map { |node| send(*node) }), &block)
|
32
|
-
rescue => e
|
33
|
-
::Kernel.raise Error::ValidationRuleError, e.message, trace
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
Rules::Pending.new(name, args.map { |node| send(*node) }, block, trace)
|
38
|
-
end
|
39
|
-
|
40
|
-
def value(value)
|
41
|
-
value
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def self.create(*args, &block)
|
46
|
-
ast = AST.build(*args, &block)
|
47
|
-
context = new
|
48
|
-
ast.each { |node| context.add_constraint(Builder.send(*node)) }
|
49
|
-
context
|
50
|
-
end
|
51
|
-
|
52
|
-
def initialize
|
53
|
-
@constraints = {}
|
54
|
-
end
|
55
|
-
|
56
|
-
def add_constraint(constraint)
|
57
|
-
if @constraints.include?(constraint.name)
|
58
|
-
raise Error::ValidationRuleError,
|
59
|
-
"duplicate constraint #{constraint.name}"
|
60
|
-
end
|
61
|
-
|
62
|
-
@constraints[constraint.name] = constraint
|
63
|
-
self
|
64
|
-
end
|
65
|
-
|
66
|
-
def evaluate(ctx)
|
67
|
-
@constraints.each_value
|
68
|
-
.reject { |c| catch(:pending) { c.valid?(ctx.value, ctx) } }
|
69
|
-
.each { |c| ctx.add_violation(c) }
|
70
|
-
ctx
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
CORE_CONSTRAINTS = %i[
|
75
|
-
not_nil not_blank not_empty
|
76
|
-
is_a one_of validate
|
77
|
-
min max equal match
|
78
|
-
valid each_value unique
|
79
|
-
length
|
80
|
-
].freeze
|
81
|
-
|
82
|
-
class Generator < ::BasicObject
|
83
|
-
def initialize
|
84
|
-
@stack = []
|
85
|
-
end
|
86
|
-
|
87
|
-
def generate(*args, &block)
|
88
|
-
instance_exec(*args, &block)
|
89
|
-
|
90
|
-
if @stack.one? && @stack.first[0] == :all_constraints
|
91
|
-
return @stack.first[1..-1]
|
92
|
-
end
|
93
|
-
|
94
|
-
@stack
|
95
|
-
end
|
96
|
-
|
97
|
-
def &(other)
|
98
|
-
unless other == self
|
99
|
-
::Kernel.raise(
|
100
|
-
Error::ValidationRuleError,
|
101
|
-
'bad rule, only constraints and &, |, and ! operators allowed'
|
102
|
-
)
|
103
|
-
end
|
104
|
-
|
105
|
-
right = @stack.pop
|
106
|
-
left = @stack.pop
|
107
|
-
if right[0] == :all_constraints
|
108
|
-
right.insert(1, left)
|
109
|
-
@stack << right
|
110
|
-
else
|
111
|
-
@stack << [:all_constraints, left, right]
|
112
|
-
end
|
113
|
-
self
|
114
|
-
end
|
115
|
-
|
116
|
-
def |(other)
|
117
|
-
unless other == self
|
118
|
-
::Kernel.raise(
|
119
|
-
Error::ValidationRuleError,
|
120
|
-
'bad rule, only constraints and &, |, and ! operators allowed'
|
121
|
-
)
|
122
|
-
end
|
123
|
-
|
124
|
-
right = @stack.pop
|
125
|
-
left = @stack.pop
|
126
|
-
if right[0] == :at_least_one_constraint
|
127
|
-
right.insert(1, left)
|
128
|
-
@stack << right
|
129
|
-
else
|
130
|
-
@stack << [:at_least_one_constraint, left, right]
|
131
|
-
end
|
132
|
-
self
|
133
|
-
end
|
134
|
-
|
135
|
-
def !
|
136
|
-
prev = @stack.pop
|
137
|
-
if prev[0] == :no_constraints
|
138
|
-
@stack << prev[1]
|
139
|
-
elsif prev[0] == :all_constraints
|
140
|
-
prev[0] = :no_constraints
|
141
|
-
@stack << prev
|
142
|
-
else
|
143
|
-
@stack << [:no_constraints, prev]
|
144
|
-
end
|
145
|
-
self
|
146
|
-
end
|
147
|
-
|
148
|
-
private
|
149
|
-
|
150
|
-
def method_missing(method, *args, &block)
|
151
|
-
return super unless respond_to_missing?(method)
|
152
|
-
|
153
|
-
@stack << [
|
154
|
-
:constraint,
|
155
|
-
method,
|
156
|
-
args.map { |arg| [:value, arg] },
|
157
|
-
block,
|
158
|
-
::Kernel.caller
|
159
|
-
.reject { |line| line.include?(__FILE__) }
|
160
|
-
]
|
161
|
-
self
|
162
|
-
end
|
163
|
-
|
164
|
-
def respond_to_missing?(method, _ = false)
|
165
|
-
(defined?(Constraints) && Constraints.respond_to?(method)) || CORE_CONSTRAINTS.include?(method)
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
module Combinator
|
170
|
-
extend Forwardable
|
171
|
-
def_delegators :@constraints, :[]
|
172
|
-
|
173
|
-
def respond_to_missing?(_, _ = false)
|
174
|
-
false
|
175
|
-
end
|
176
|
-
|
177
|
-
private
|
178
|
-
|
179
|
-
def constraint_message(index)
|
180
|
-
@constraints[index].message % Hash.new do |_, key|
|
181
|
-
if key.to_s.start_with?('constraint')
|
182
|
-
"%{#{key.to_s.gsub('constraint', "constraint[#{index}]")}}"
|
183
|
-
else
|
184
|
-
"%{#{key}}"
|
185
|
-
end
|
186
|
-
end
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
module Rules
|
191
|
-
class Pending < Constraint
|
192
|
-
include MonitorMixin
|
193
|
-
|
194
|
-
def initialize(name, args, block, caller)
|
195
|
-
@name = name
|
196
|
-
@args = args
|
197
|
-
@block = block
|
198
|
-
@caller = caller
|
199
|
-
@constraint = nil
|
200
|
-
|
201
|
-
extend SingleForwardable
|
202
|
-
mon_initialize
|
203
|
-
end
|
204
|
-
|
205
|
-
def name
|
206
|
-
load_constraint { return @name }.name
|
207
|
-
end
|
208
|
-
|
209
|
-
def valid?(value, ctx = Constraints::ValidationContext.none)
|
210
|
-
load_constraint { throw(:pending, true) }.valid?(value, ctx)
|
211
|
-
end
|
212
|
-
|
213
|
-
def to_s
|
214
|
-
load_constraint { return "[pending #{@name}]" }.to_s
|
215
|
-
end
|
216
|
-
|
217
|
-
def inspect
|
218
|
-
load_constraint { return "[pending #{@name}]" }.inspect
|
219
|
-
end
|
220
|
-
|
221
|
-
def ==(other)
|
222
|
-
load_constraint { return false } == other
|
223
|
-
end
|
224
|
-
|
225
|
-
def method_missing(method, *args)
|
226
|
-
load_constraint { return NameError }.__send__(method, *args)
|
227
|
-
end
|
228
|
-
|
229
|
-
def respond_to_missing?(method, pvt = false)
|
230
|
-
load_constraint { return false }.__send__(:respond_to_missing?, method, pvt)
|
231
|
-
end
|
232
|
-
|
233
|
-
private
|
234
|
-
|
235
|
-
def load_constraint
|
236
|
-
yield unless defined?(Constraints) && Constraints.respond_to?(@name)
|
237
|
-
|
238
|
-
synchronize do
|
239
|
-
return @constraint if @constraint
|
240
|
-
|
241
|
-
begin
|
242
|
-
@constraint = Constraints.send(@name, *@args, &@block)
|
243
|
-
rescue => e
|
244
|
-
::Kernel.raise Error::ValidationRuleError, e.message, @caller
|
245
|
-
end
|
246
|
-
|
247
|
-
def_delegators(:@constraint, :name, :valid?, :to_s,
|
248
|
-
:inspect, :==, :message)
|
249
|
-
|
250
|
-
@name = @args = @block = @caller = nil
|
251
|
-
@constraint
|
252
|
-
end
|
253
|
-
end
|
254
|
-
end
|
255
|
-
|
256
|
-
class Unanimous < Constraint
|
257
|
-
include Combinator
|
258
|
-
include Arguments
|
259
|
-
|
260
|
-
arg(:constraints) do
|
261
|
-
not_nil
|
262
|
-
length(min: 2)
|
263
|
-
each_value { is_a(Constraint) }
|
264
|
-
unique(:name)
|
265
|
-
end
|
266
|
-
def initialize(constraints)
|
267
|
-
@constraints = constraints.freeze
|
268
|
-
end
|
269
|
-
|
270
|
-
def valid?(value, _ = Constraints::ValidationContext.none)
|
271
|
-
ctx = Constraints::ValidationContext.root(value)
|
272
|
-
@constraints.all? do |c|
|
273
|
-
c.valid?(value, ctx) && !ctx.has_violations?
|
274
|
-
end
|
275
|
-
end
|
276
|
-
|
277
|
-
def name
|
278
|
-
'both_' + @constraints.map(&:name).sort.join('_and_')
|
279
|
-
end
|
280
|
-
|
281
|
-
def inspect
|
282
|
-
return @constraints.first.inspect if @constraints.one?
|
283
|
-
|
284
|
-
"(#{@constraints.map(&:inspect).join(' & ')})"
|
285
|
-
end
|
286
|
-
|
287
|
-
def message
|
288
|
-
'both ' + @constraints
|
289
|
-
.size
|
290
|
-
.times
|
291
|
-
.map { |i| "[#{constraint_message(i)}]" }
|
292
|
-
.join(', and ')
|
293
|
-
end
|
294
|
-
end
|
295
|
-
|
296
|
-
class Affirmative < Constraint
|
297
|
-
include Combinator
|
298
|
-
include Arguments
|
299
|
-
|
300
|
-
arg(:constraints) do
|
301
|
-
not_nil
|
302
|
-
length(min: 2)
|
303
|
-
each_value { is_a(Constraint) }
|
304
|
-
unique(:name)
|
305
|
-
end
|
306
|
-
def initialize(constraints)
|
307
|
-
@constraints = constraints.freeze
|
308
|
-
end
|
309
|
-
|
310
|
-
def valid?(value, _ = Constraints::ValidationContext.none)
|
311
|
-
ctx = Constraints::ValidationContext.root(value)
|
312
|
-
@constraints.any? do |c|
|
313
|
-
ctx.clear_violations
|
314
|
-
c.valid?(value, ctx) && !ctx.has_violations?
|
315
|
-
end
|
316
|
-
end
|
317
|
-
|
318
|
-
def name
|
319
|
-
'either_' + @constraints.map(&:name).sort.join('_or_')
|
320
|
-
end
|
321
|
-
|
322
|
-
def inspect
|
323
|
-
return @constraints.first.inspect if @constraints.one?
|
324
|
-
|
325
|
-
"(#{@constraints.map(&:inspect).join(' | ')})"
|
326
|
-
end
|
327
|
-
|
328
|
-
def message
|
329
|
-
'either ' + @constraints
|
330
|
-
.size
|
331
|
-
.times
|
332
|
-
.map { |i| "[#{constraint_message(i)}]" }
|
333
|
-
.join(', or ')
|
334
|
-
end
|
335
|
-
end
|
336
|
-
|
337
|
-
class Negative < Constraint
|
338
|
-
include Combinator
|
339
|
-
include Arguments
|
340
|
-
|
341
|
-
arg(:constraints) do
|
342
|
-
not_nil
|
343
|
-
not_empty
|
344
|
-
each_value { is_a(Constraint) }
|
345
|
-
unique(:name)
|
346
|
-
end
|
347
|
-
def initialize(constraints)
|
348
|
-
@constraints = constraints.freeze
|
349
|
-
end
|
350
|
-
|
351
|
-
def valid?(value, _ = Constraints::ValidationContext.none)
|
352
|
-
ctx = Constraints::ValidationContext.root(value)
|
353
|
-
@constraints.none? do |c|
|
354
|
-
ctx.clear_violations
|
355
|
-
c.valid?(value, ctx) && !ctx.has_violations?
|
356
|
-
end
|
357
|
-
end
|
358
|
-
|
359
|
-
def name
|
360
|
-
'neither_' + @constraints.map(&:name).sort.join('_nor_')
|
361
|
-
end
|
362
|
-
|
363
|
-
def message
|
364
|
-
return "not [#{constraint_message(0)}]" if @constraints.one?
|
365
|
-
|
366
|
-
'neither ' + @constraints
|
367
|
-
.size
|
368
|
-
.times
|
369
|
-
.map { |i| "[#{constraint_message(i)}]" }
|
370
|
-
.join(', nor ')
|
371
|
-
end
|
372
|
-
|
373
|
-
def inspect
|
374
|
-
return "!#{@constraints.first.inspect}" if @constraints.one?
|
375
|
-
|
376
|
-
"!(#{@constraints.map(&:inspect).join(' & ')})"
|
377
|
-
end
|
378
|
-
end
|
379
|
-
end
|
380
|
-
end
|
381
|
-
end
|
data/lib/validate/compare.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Validate
|
4
|
-
module Compare
|
5
|
-
class ByAttributes
|
6
|
-
include Comparable
|
7
|
-
|
8
|
-
def initialize(attributes)
|
9
|
-
@attributes = attributes
|
10
|
-
end
|
11
|
-
|
12
|
-
def <=>(other)
|
13
|
-
@attributes.map { |attribute, value| value <=> other.send(attribute) }
|
14
|
-
.find { |result| !result.zero? } || 0
|
15
|
-
end
|
16
|
-
|
17
|
-
def method_missing(symbol, *args)
|
18
|
-
return super unless args.empty? && respond_to_missing?(symbol)
|
19
|
-
|
20
|
-
@attributes[symbol]
|
21
|
-
end
|
22
|
-
|
23
|
-
def respond_to_missing?(attribute, _ = false)
|
24
|
-
@attributes.include?(attribute)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
module_function
|
29
|
-
|
30
|
-
def attributes(**attributes)
|
31
|
-
ByAttributes.new(attributes)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
data/lib/validate/constraint.rb
DELETED
@@ -1,217 +0,0 @@
|
|
1
|
-
# typed: strict
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
module Validate
|
5
|
-
class Constraint
|
6
|
-
class Violation
|
7
|
-
attr_reader :value, :path, :constraint
|
8
|
-
|
9
|
-
def initialize(value, path, constraint)
|
10
|
-
@value = value
|
11
|
-
@path = path
|
12
|
-
@constraint = constraint
|
13
|
-
end
|
14
|
-
|
15
|
-
def message(template = @constraint.message)
|
16
|
-
(template % parameters).strip
|
17
|
-
end
|
18
|
-
|
19
|
-
alias to_s message
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
def parameters
|
24
|
-
@parameters ||= Hash.new do |_, key|
|
25
|
-
String(instance_eval(key.to_s))
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
class Option
|
31
|
-
attr_reader :name
|
32
|
-
|
33
|
-
def initialize(name, default:, assertion: nil, &assert_block)
|
34
|
-
@name = name
|
35
|
-
@default = default.is_a?(Proc) ? default : -> { default }
|
36
|
-
@assertion = assertion || assert_block && Assertions.create(&assert_block)
|
37
|
-
end
|
38
|
-
|
39
|
-
def replace_default(default)
|
40
|
-
Option.new(@name, default: default, assertion: @assertion)
|
41
|
-
end
|
42
|
-
|
43
|
-
def get_or_default(options)
|
44
|
-
value = options.delete(@name) { return @default&.call }
|
45
|
-
@assertion&.assert(value, message: "invalid option #{@name}")
|
46
|
-
value
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def self.inherited(child)
|
51
|
-
child.extend DSL
|
52
|
-
end
|
53
|
-
|
54
|
-
def self.create_class(name, **defaults, &constraint_block)
|
55
|
-
Class.new(self) do
|
56
|
-
@supported_options = common_options.transform_values do |option|
|
57
|
-
defaults.include?(option.name) ? option.replace_default(defaults[option.name]) : option
|
58
|
-
end
|
59
|
-
include(@constraint_user_methods = Module.new)
|
60
|
-
@constraint_user_methods.define_method(:name) { name.to_s }
|
61
|
-
class_eval(&constraint_block)
|
62
|
-
if instance_variable_defined?(:@supported_options)
|
63
|
-
initialize { |**options| options }
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
module DSL
|
69
|
-
def constraint_name
|
70
|
-
@constraint_name ||= Assertions.create do
|
71
|
-
not_nil(message: 'constraint name must not be nil')
|
72
|
-
is_a(Symbol, message: 'constraint name must be a Symbol')
|
73
|
-
end
|
74
|
-
end
|
75
|
-
module_function :constraint_name
|
76
|
-
|
77
|
-
def common_options
|
78
|
-
@common_options ||= {
|
79
|
-
message: Option.new(:message, default: 'be %{constraint.name}') do
|
80
|
-
not_blank
|
81
|
-
is_a(String)
|
82
|
-
end
|
83
|
-
}.freeze
|
84
|
-
end
|
85
|
-
module_function :common_options
|
86
|
-
|
87
|
-
def option(
|
88
|
-
name,
|
89
|
-
default: lambda do
|
90
|
-
raise Error::KeyError,
|
91
|
-
"option #{name.inspect} is required for #{self.name}"
|
92
|
-
end,
|
93
|
-
&assert_block
|
94
|
-
)
|
95
|
-
constraint_name.assert(name)
|
96
|
-
if @supported_options.include?(name)
|
97
|
-
raise Error::ArgumentError, "duplicate option :#{name}"
|
98
|
-
end
|
99
|
-
|
100
|
-
@supported_options[name] = Option.new(
|
101
|
-
name,
|
102
|
-
default: default,
|
103
|
-
&assert_block
|
104
|
-
)
|
105
|
-
self
|
106
|
-
end
|
107
|
-
|
108
|
-
def initialize(&initialize_block)
|
109
|
-
supported_options = @supported_options
|
110
|
-
expects_kwargs = false
|
111
|
-
initialize_block.parameters.each do |(kind, name)|
|
112
|
-
if %i(keyreq key).include?(kind) && supported_options.include?(name)
|
113
|
-
raise Error::ArgumentError,
|
114
|
-
"key name #{name}: conflicts with an existing option"
|
115
|
-
end
|
116
|
-
expects_kwargs = true if kind == :keyrest
|
117
|
-
end
|
118
|
-
|
119
|
-
define_constraint_method(:initialize, initialize_block) do |*args, **kwargs, &block|
|
120
|
-
known_options, extra_kwargs =
|
121
|
-
kwargs.partition { |k, _| supported_options.include?(k) }
|
122
|
-
.map { |h| Hash[h] }
|
123
|
-
args << extra_kwargs if expects_kwargs || !extra_kwargs.empty?
|
124
|
-
|
125
|
-
merged_options = {}.merge!(super(*args, &block), known_options)
|
126
|
-
|
127
|
-
options = supported_options.each_with_object({}) do |(n, opt), opts|
|
128
|
-
opts[n] = opt.get_or_default(merged_options)
|
129
|
-
end
|
130
|
-
|
131
|
-
unless merged_options.empty?
|
132
|
-
raise Error::ArgumentError,
|
133
|
-
"undefined options #{merged_options.inspect}"
|
134
|
-
end
|
135
|
-
|
136
|
-
@options = options.freeze
|
137
|
-
end
|
138
|
-
remove_instance_variable(:@supported_options)
|
139
|
-
end
|
140
|
-
|
141
|
-
def evaluate(&validation_block)
|
142
|
-
define_constraint_method(:valid?, validation_block) do |*args|
|
143
|
-
catch(:result) do
|
144
|
-
super(*args[0...validation_block.arity])
|
145
|
-
:pass
|
146
|
-
end == :pass
|
147
|
-
end
|
148
|
-
self
|
149
|
-
end
|
150
|
-
|
151
|
-
def describe(&describe_block)
|
152
|
-
define_method(:to_s, &describe_block)
|
153
|
-
self
|
154
|
-
end
|
155
|
-
|
156
|
-
def key(&key_block)
|
157
|
-
define_method(:name, &key_block)
|
158
|
-
self
|
159
|
-
end
|
160
|
-
|
161
|
-
private
|
162
|
-
|
163
|
-
def define_constraint_method(name, body, &override)
|
164
|
-
@constraint_user_methods.__send__(:define_method, name, &body)
|
165
|
-
define_method(name, &override)
|
166
|
-
self
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
attr_reader :options
|
171
|
-
protected :options
|
172
|
-
|
173
|
-
def initialize(**options)
|
174
|
-
@options = options
|
175
|
-
end
|
176
|
-
|
177
|
-
def name
|
178
|
-
raise ::NotImplementedError
|
179
|
-
end
|
180
|
-
|
181
|
-
def valid?(value, ctx = Constraints::ValidationContext.none)
|
182
|
-
raise ::NotImplementedError
|
183
|
-
end
|
184
|
-
|
185
|
-
def to_s
|
186
|
-
name.to_s.gsub('_', ' ')
|
187
|
-
end
|
188
|
-
|
189
|
-
def inspect
|
190
|
-
"#<#{self.class.name} #{@options.map { |name, value| "#{name}: #{value.inspect}" }.join(', ')}>"
|
191
|
-
end
|
192
|
-
|
193
|
-
def ==(other)
|
194
|
-
other.is_a?(Constraint) && other.name == name && other.options == options
|
195
|
-
end
|
196
|
-
|
197
|
-
def respond_to_missing?(method, _ = false)
|
198
|
-
@options.include?(method)
|
199
|
-
end
|
200
|
-
|
201
|
-
def method_missing(method, *args)
|
202
|
-
return super unless args.empty? || respond_to_missing?(method)
|
203
|
-
|
204
|
-
@options[method]
|
205
|
-
end
|
206
|
-
|
207
|
-
private
|
208
|
-
|
209
|
-
def fail
|
210
|
-
throw(:result, :fail)
|
211
|
-
end
|
212
|
-
|
213
|
-
def pass
|
214
|
-
throw(:result, :pass)
|
215
|
-
end
|
216
|
-
end
|
217
|
-
end
|