strict 0.0.0 → 1.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/.rubocop.yml +14 -2
- data/Gemfile.lock +18 -1
- data/README.md +100 -6
- data/lib/strict/accessor/attributes.rb +15 -0
- data/lib/strict/accessor/module.rb +45 -0
- data/lib/strict/assignment_error.rb +25 -0
- data/lib/strict/attribute.rb +71 -0
- data/lib/strict/attributes/configuration.rb +46 -0
- data/lib/strict/attributes/configured.rb +13 -0
- data/lib/strict/attributes/dsl.rb +42 -0
- data/lib/strict/attributes/instance.rb +68 -0
- data/lib/strict/dsl/validatable.rb +28 -0
- data/lib/strict/initialization_error.rb +57 -0
- data/lib/strict/method.rb +52 -0
- data/lib/strict/method_call_error.rb +72 -0
- data/lib/strict/method_definition_error.rb +46 -0
- data/lib/strict/method_return_error.rb +25 -0
- data/lib/strict/methods/configuration.rb +14 -0
- data/lib/strict/methods/dsl.rb +55 -0
- data/lib/strict/methods/module.rb +26 -0
- data/lib/strict/methods/verifiable_method.rb +158 -0
- data/lib/strict/object.rb +9 -0
- data/lib/strict/parameter.rb +63 -0
- data/lib/strict/reader/attributes.rb +15 -0
- data/lib/strict/reader/module.rb +27 -0
- data/lib/strict/return.rb +28 -0
- data/lib/strict/validators/all_of.rb +24 -0
- data/lib/strict/validators/any_of.rb +24 -0
- data/lib/strict/validators/anything.rb +20 -0
- data/lib/strict/validators/array_of.rb +24 -0
- data/lib/strict/validators/boolean.rb +20 -0
- data/lib/strict/validators/hash_of.rb +25 -0
- data/lib/strict/validators/range_of.rb +24 -0
- data/lib/strict/value.rb +22 -0
- data/lib/strict/version.rb +1 -1
- data/lib/strict.rb +1 -0
- data/strict.gemspec +41 -0
- metadata +90 -2
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Strict
|
4
|
+
class MethodCallError < Error
|
5
|
+
attr_reader :verifiable_method, :remaining_args, :remaining_kwargs, :invalid_parameters, :missing_parameters
|
6
|
+
|
7
|
+
def initialize(verifiable_method:, remaining_args:, remaining_kwargs:, invalid_parameters:, missing_parameters:)
|
8
|
+
super(
|
9
|
+
message_from(verifiable_method:, remaining_args:, remaining_kwargs:, invalid_parameters:, missing_parameters:)
|
10
|
+
)
|
11
|
+
|
12
|
+
@verifiable_method = verifiable_method
|
13
|
+
@remaining_args = remaining_args
|
14
|
+
@remaining_kwargs = remaining_kwargs
|
15
|
+
@invalid_parameters = invalid_parameters
|
16
|
+
@missing_parameters = missing_parameters
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def message_from(verifiable_method:, remaining_args:, remaining_kwargs:, invalid_parameters:, missing_parameters:)
|
22
|
+
details = [
|
23
|
+
invalid_parameters_message_from(invalid_parameters),
|
24
|
+
missing_parameters_message_from(missing_parameters),
|
25
|
+
remaining_args_message_from(remaining_args),
|
26
|
+
remaining_kwargs_message_from(remaining_kwargs)
|
27
|
+
].compact.join("\n")
|
28
|
+
|
29
|
+
"Calling #{verifiable_method} failed because:\n#{details}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def invalid_parameters_message_from(invalid_parameters)
|
33
|
+
return nil unless invalid_parameters
|
34
|
+
|
35
|
+
details = invalid_parameters.map do |parameter, value|
|
36
|
+
" - #{parameter.name}: got #{value.inspect}, expected #{parameter.validator.inspect}"
|
37
|
+
end.join("\n")
|
38
|
+
|
39
|
+
" Some arguments were invalid:\n#{details}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def missing_parameters_message_from(missing_parameters)
|
43
|
+
return nil unless missing_parameters
|
44
|
+
|
45
|
+
details = missing_parameters.map do |parameter_name|
|
46
|
+
" - #{parameter_name}"
|
47
|
+
end.join("\n")
|
48
|
+
|
49
|
+
" Some arguments were missing:\n#{details}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def remaining_args_message_from(remaining_args)
|
53
|
+
return nil if remaining_args.none?
|
54
|
+
|
55
|
+
details = remaining_args.map do |arg|
|
56
|
+
" - #{arg.inspect}"
|
57
|
+
end.join("\n")
|
58
|
+
|
59
|
+
" Additional positional arguments were provided, but not defined:\n#{details}"
|
60
|
+
end
|
61
|
+
|
62
|
+
def remaining_kwargs_message_from(remaining_kwargs)
|
63
|
+
return nil if remaining_kwargs.none?
|
64
|
+
|
65
|
+
details = remaining_kwargs.map do |key, value|
|
66
|
+
" - #{key}: #{value.inspect}"
|
67
|
+
end.join("\n")
|
68
|
+
|
69
|
+
" Additional keyword arguments were provided, but not defined:\n#{details}"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Strict
|
4
|
+
class MethodDefinitionError < Error
|
5
|
+
attr_reader :verifiable_method, :missing_parameters, :additional_parameters
|
6
|
+
|
7
|
+
def initialize(verifiable_method:, missing_parameters:, additional_parameters:)
|
8
|
+
super(message_from(verifiable_method:, missing_parameters:, additional_parameters:))
|
9
|
+
|
10
|
+
@verifiable_method = verifiable_method
|
11
|
+
@missing_parameters = missing_parameters
|
12
|
+
@additional_parameters = additional_parameters
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def message_from(verifiable_method:, missing_parameters:, additional_parameters:)
|
18
|
+
details = [
|
19
|
+
missing_parameters_message_from(missing_parameters),
|
20
|
+
additional_parameters_message_from(additional_parameters)
|
21
|
+
].compact.join("\n")
|
22
|
+
|
23
|
+
"Defining #{verifiable_method} failed because:\n#{details}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def missing_parameters_message_from(missing_parameters)
|
27
|
+
return nil unless missing_parameters.any?
|
28
|
+
|
29
|
+
details = missing_parameters.map do |parameter_name|
|
30
|
+
" - #{parameter_name}"
|
31
|
+
end.join("\n")
|
32
|
+
|
33
|
+
" Some parameters were in the `sig`, but were not in the parameter list:\n#{details}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def additional_parameters_message_from(additional_parameters)
|
37
|
+
return nil unless additional_parameters.any?
|
38
|
+
|
39
|
+
details = additional_parameters.map do |parameter_name|
|
40
|
+
" - #{parameter_name}"
|
41
|
+
end.join("\n")
|
42
|
+
|
43
|
+
" Some parameters were not in the `sig`, but were in the parameter list:\n#{details}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Strict
|
4
|
+
class MethodReturnError < Error
|
5
|
+
attr_reader :verifiable_method, :value
|
6
|
+
|
7
|
+
def initialize(verifiable_method:, value:)
|
8
|
+
super(message_from(verifiable_method:, value:))
|
9
|
+
|
10
|
+
@verifiable_method = verifiable_method
|
11
|
+
@value = value
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def message_from(verifiable_method:, value:)
|
17
|
+
details = invalid_returns_message_from(verifiable_method, value)
|
18
|
+
"#{verifiable_method}'s return value was invalid because:\n#{details}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def invalid_returns_message_from(verifiable_method, value)
|
22
|
+
" - got #{value.inspect}, expected #{verifiable_method.returns.validator.inspect}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Strict
|
4
|
+
module Methods
|
5
|
+
class Dsl < BasicObject
|
6
|
+
class << self
|
7
|
+
def run(&)
|
8
|
+
dsl = new
|
9
|
+
dsl.instance_eval(&)
|
10
|
+
::Strict::Methods::Configuration.new(
|
11
|
+
parameters: dsl.__strict_dsl_internal_parameters.values,
|
12
|
+
returns: dsl.__strict_dsl_internal_returns
|
13
|
+
)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
include ::Strict::Dsl::Validatable
|
18
|
+
|
19
|
+
attr_reader :__strict_dsl_internal_parameters, :__strict_dsl_internal_returns
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
@__strict_dsl_internal_parameters = {}
|
23
|
+
@__strict_dsl_internal_returns = ::Strict::Return.make
|
24
|
+
end
|
25
|
+
|
26
|
+
def returns(*args, **kwargs)
|
27
|
+
self.__strict_dsl_internal_returns = ::Strict::Return.make(*args, **kwargs)
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def strict_parameter(*args, **kwargs)
|
32
|
+
parameter = ::Strict::Parameter.make(*args, **kwargs)
|
33
|
+
__strict_dsl_internal_parameters[parameter.name] = parameter
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def method_missing(name, *args, **kwargs)
|
38
|
+
if respond_to_missing?(name)
|
39
|
+
strict_parameter(name, *args, **kwargs)
|
40
|
+
else
|
41
|
+
super
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def respond_to_missing?(method_name, _include_private = nil)
|
46
|
+
first_letter = method_name.to_s.each_char.first
|
47
|
+
first_letter.eql?(first_letter.downcase)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
attr_writer :__strict_dsl_internal_returns
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Strict
|
4
|
+
module Methods
|
5
|
+
class Module < ::Module
|
6
|
+
attr_reader :verifiable_method
|
7
|
+
|
8
|
+
def initialize(verifiable_method)
|
9
|
+
super()
|
10
|
+
|
11
|
+
@verifiable_method = verifiable_method
|
12
|
+
define_method verifiable_method.name do |*args, **kwargs, &block|
|
13
|
+
args, kwargs = verifiable_method.verify_parameters!(*args, **kwargs)
|
14
|
+
|
15
|
+
super(*args, **kwargs, &block).tap do |value|
|
16
|
+
verifiable_method.verify_returns!(value)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def inspect
|
22
|
+
"#<#{self.class} (#{verifiable_method.name})>"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "forwardable"
|
4
|
+
|
5
|
+
module Strict
|
6
|
+
module Methods
|
7
|
+
class VerifiableMethod # rubocop:disable Metrics/ClassLength
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
class UnknownParameterError < Error
|
11
|
+
attr_reader :parameter_name
|
12
|
+
|
13
|
+
def initialize(parameter_name:)
|
14
|
+
super(message_from(parameter_name:))
|
15
|
+
|
16
|
+
@parameter_name = parameter_name
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def message_from(parameter_name:)
|
22
|
+
"Strict tried to find a parameter named #{parameter_name} but was unable. " \
|
23
|
+
"It's likely this in an internal bug, feel free to open an issue at #{Strict::ISSUE_TRACKER} for help."
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def_delegator :method, :name
|
28
|
+
|
29
|
+
attr_reader :parameters, :returns
|
30
|
+
|
31
|
+
def initialize(method:, parameters:, returns:, instance:)
|
32
|
+
@method = method
|
33
|
+
@parameters = parameters
|
34
|
+
@parameters_index = parameters.to_h { |p| [p.name, p] }
|
35
|
+
@returns = returns
|
36
|
+
@instance = instance
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_s
|
40
|
+
"#{method.owner}#{separator}#{name}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def verify_definition!
|
44
|
+
expected_parameters = Set.new(parameters.map(&:name))
|
45
|
+
defined_parameters = Set.new(method.parameters.filter_map { |kind, name| name unless kind == :block })
|
46
|
+
return if expected_parameters == defined_parameters
|
47
|
+
|
48
|
+
missing_parameters = expected_parameters - defined_parameters
|
49
|
+
additional_parameters = defined_parameters - expected_parameters
|
50
|
+
raise Strict::MethodDefinitionError.new(verifiable_method: self, missing_parameters:, additional_parameters:)
|
51
|
+
end
|
52
|
+
|
53
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/BlockLength
|
54
|
+
# TODO(kkt): clean this up- it's late, though, and the tests are passing
|
55
|
+
def verify_parameters!(*args, **kwargs)
|
56
|
+
invalid_parameters = nil
|
57
|
+
missing_parameters = nil
|
58
|
+
|
59
|
+
positional_arguments = []
|
60
|
+
keyword_arguments = {}
|
61
|
+
|
62
|
+
# TODO(kkt): doesn't handle oddly sorted optional positional parameters like def foo(opt = nil, req)
|
63
|
+
method.parameters.each do |kind, name|
|
64
|
+
case kind
|
65
|
+
when POSITIONAL
|
66
|
+
parameter_kind = :positional
|
67
|
+
value = args.any? ? args.shift : NOT_PROVIDED
|
68
|
+
when REST
|
69
|
+
parameter_kind = :rest
|
70
|
+
value = [*args]
|
71
|
+
args.clear
|
72
|
+
when KEYWORD
|
73
|
+
parameter_kind = :keyword
|
74
|
+
value = kwargs.key?(name) ? kwargs.delete(name) : NOT_PROVIDED
|
75
|
+
when KEYREST
|
76
|
+
parameter_kind = :keyrest
|
77
|
+
value = { **kwargs }
|
78
|
+
kwargs.clear
|
79
|
+
end
|
80
|
+
next unless parameter_kind
|
81
|
+
|
82
|
+
parameter = parameter_named!(name)
|
83
|
+
if value.equal?(NOT_PROVIDED) && parameter.optional?
|
84
|
+
value = parameter.default_generator.call
|
85
|
+
elsif value.equal?(NOT_PROVIDED)
|
86
|
+
missing_parameters ||= []
|
87
|
+
missing_parameters << parameter.name
|
88
|
+
next
|
89
|
+
end
|
90
|
+
|
91
|
+
value = parameter.coerce(value)
|
92
|
+
if parameter.valid?(value)
|
93
|
+
case parameter_kind
|
94
|
+
when :positional
|
95
|
+
positional_arguments << value
|
96
|
+
when :rest
|
97
|
+
positional_arguments.concat(value)
|
98
|
+
when :keyword
|
99
|
+
keyword_arguments[name] = value
|
100
|
+
when :keyrest
|
101
|
+
keyword_arguments.merge!(value)
|
102
|
+
end
|
103
|
+
else
|
104
|
+
invalid_parameters ||= {}
|
105
|
+
invalid_parameters[parameter] = value
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
if args.empty? && kwargs.empty? && invalid_parameters.nil? && missing_parameters.nil?
|
110
|
+
[positional_arguments, keyword_arguments]
|
111
|
+
else
|
112
|
+
raise Strict::MethodCallError.new(
|
113
|
+
verifiable_method: self,
|
114
|
+
remaining_args: args,
|
115
|
+
remaining_kwargs: kwargs,
|
116
|
+
invalid_parameters:,
|
117
|
+
missing_parameters:
|
118
|
+
)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/BlockLength
|
122
|
+
|
123
|
+
def verify_returns!(value)
|
124
|
+
value = returns.coerce(value)
|
125
|
+
return if returns.valid?(value)
|
126
|
+
|
127
|
+
raise Strict::MethodReturnError.new(verifiable_method: self, value:)
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
POSITIONAL = Set.new(%i[req opt])
|
133
|
+
private_constant :POSITIONAL
|
134
|
+
REST = :rest
|
135
|
+
private_constant :REST
|
136
|
+
KEYWORD = Set.new(%i[keyreq key])
|
137
|
+
private_constant :KEYWORD
|
138
|
+
KEYREST = :keyrest
|
139
|
+
private_constant :KEYREST
|
140
|
+
NOT_PROVIDED = ::Object.new.freeze
|
141
|
+
private_constant :NOT_PROVIDED
|
142
|
+
|
143
|
+
attr_reader :method, :parameters_index
|
144
|
+
|
145
|
+
def instance?
|
146
|
+
@instance
|
147
|
+
end
|
148
|
+
|
149
|
+
def separator
|
150
|
+
instance? ? "#" : "."
|
151
|
+
end
|
152
|
+
|
153
|
+
def parameter_named!(name)
|
154
|
+
parameters_index.fetch(name) { raise UnknownParameterError.new(parameter_name: name) }
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Strict
|
4
|
+
class Parameter
|
5
|
+
NOT_PROVIDED = ::Object.new.freeze
|
6
|
+
|
7
|
+
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(name: name.to_sym, validator:, default_generator: make_default_generator(**defaults), coercer: coerce)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def valid_defaults?(default: NOT_PROVIDED, default_value: NOT_PROVIDED, default_generator: NOT_PROVIDED)
|
19
|
+
defaults_provided = [default, default_value, default_generator].count do |default_option|
|
20
|
+
!default_option.equal?(NOT_PROVIDED)
|
21
|
+
end
|
22
|
+
|
23
|
+
defaults_provided <= 1
|
24
|
+
end
|
25
|
+
|
26
|
+
def make_default_generator(default: NOT_PROVIDED, default_value: NOT_PROVIDED, default_generator: NOT_PROVIDED)
|
27
|
+
if !default.equal?(NOT_PROVIDED)
|
28
|
+
default.respond_to?(:call) ? default : -> { default }
|
29
|
+
elsif !default_value.equal?(NOT_PROVIDED)
|
30
|
+
-> { default_value }
|
31
|
+
elsif !default_generator.equal?(NOT_PROVIDED)
|
32
|
+
default_generator
|
33
|
+
else
|
34
|
+
NOT_PROVIDED
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
attr_reader :name, :validator, :default_generator, :coercer
|
40
|
+
|
41
|
+
def initialize(name:, validator:, default_generator:, coercer:)
|
42
|
+
@name = name.to_sym
|
43
|
+
@validator = validator
|
44
|
+
@default_generator = default_generator
|
45
|
+
@coercer = coercer
|
46
|
+
@optional = !default_generator.equal?(NOT_PROVIDED)
|
47
|
+
end
|
48
|
+
|
49
|
+
def optional?
|
50
|
+
@optional
|
51
|
+
end
|
52
|
+
|
53
|
+
def valid?(value)
|
54
|
+
validator === value
|
55
|
+
end
|
56
|
+
|
57
|
+
def coerce(value)
|
58
|
+
return value unless coercer
|
59
|
+
|
60
|
+
coercer.call(value)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Strict
|
4
|
+
module Reader
|
5
|
+
module Attributes
|
6
|
+
def attributes(&block)
|
7
|
+
block ||= -> {}
|
8
|
+
configuration = Strict::Attributes::Dsl.run(&block)
|
9
|
+
include Module.new(configuration)
|
10
|
+
include Strict::Attributes::Instance
|
11
|
+
extend Strict::Attributes::Configured
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Strict
|
4
|
+
module Reader
|
5
|
+
class Module < ::Module
|
6
|
+
attr_reader :configuration
|
7
|
+
|
8
|
+
def initialize(configuration)
|
9
|
+
super()
|
10
|
+
|
11
|
+
@configuration = configuration
|
12
|
+
const_set(Strict::Attributes::Configured::CONSTANT, configuration)
|
13
|
+
configuration.attributes.each do |attribute|
|
14
|
+
module_eval(
|
15
|
+
"def #{attribute.name} = #{attribute.instance_variable}", # def name = @instance_variable
|
16
|
+
__FILE__,
|
17
|
+
__LINE__ - 2
|
18
|
+
)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def inspect
|
23
|
+
"#<#{self.class} (#{configuration.attributes.map(&:name).join(', ')})>"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Strict
|
4
|
+
class Return
|
5
|
+
class << self
|
6
|
+
def make(validator = Validators::Anything.instance, coerce: false)
|
7
|
+
new(validator:, coercer: coerce)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :validator, :coercer
|
12
|
+
|
13
|
+
def initialize(validator:, coercer:)
|
14
|
+
@validator = validator
|
15
|
+
@coercer = coercer
|
16
|
+
end
|
17
|
+
|
18
|
+
def valid?(value)
|
19
|
+
validator === value
|
20
|
+
end
|
21
|
+
|
22
|
+
def coerce(value)
|
23
|
+
return value unless coercer
|
24
|
+
|
25
|
+
coercer.call(value)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Strict
|
4
|
+
module Validators
|
5
|
+
class AllOf
|
6
|
+
attr_reader :subvalidators
|
7
|
+
|
8
|
+
def initialize(*subvalidators)
|
9
|
+
@subvalidators = subvalidators
|
10
|
+
end
|
11
|
+
|
12
|
+
def ===(value)
|
13
|
+
subvalidators.all? do |subvalidator|
|
14
|
+
subvalidator === value
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def inspect
|
19
|
+
"AllOf(#{subvalidators.map(&:inspect).join(', ')})"
|
20
|
+
end
|
21
|
+
alias to_s inspect
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Strict
|
4
|
+
module Validators
|
5
|
+
class AnyOf
|
6
|
+
attr_reader :subvalidators
|
7
|
+
|
8
|
+
def initialize(*subvalidators)
|
9
|
+
@subvalidators = subvalidators
|
10
|
+
end
|
11
|
+
|
12
|
+
def ===(value)
|
13
|
+
subvalidators.any? do |subvalidator|
|
14
|
+
subvalidator === value
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def inspect
|
19
|
+
"AnyOf(#{subvalidators.map(&:inspect).join(', ')})"
|
20
|
+
end
|
21
|
+
alias to_s inspect
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "singleton"
|
4
|
+
|
5
|
+
module Strict
|
6
|
+
module Validators
|
7
|
+
class Anything
|
8
|
+
include Singleton
|
9
|
+
|
10
|
+
def ===(_value)
|
11
|
+
true
|
12
|
+
end
|
13
|
+
|
14
|
+
def inspect
|
15
|
+
"Anything()"
|
16
|
+
end
|
17
|
+
alias to_s inspect
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Strict
|
4
|
+
module Validators
|
5
|
+
class ArrayOf
|
6
|
+
attr_reader :element_validator
|
7
|
+
|
8
|
+
def initialize(element_validator)
|
9
|
+
@element_validator = element_validator
|
10
|
+
end
|
11
|
+
|
12
|
+
def ===(value)
|
13
|
+
Array === value && value.all? do |v|
|
14
|
+
element_validator === v
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def inspect
|
19
|
+
"ArrayOf(#{element_validator.inspect})"
|
20
|
+
end
|
21
|
+
alias to_s inspect
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "singleton"
|
4
|
+
|
5
|
+
module Strict
|
6
|
+
module Validators
|
7
|
+
class Boolean
|
8
|
+
include Singleton
|
9
|
+
|
10
|
+
def ===(value)
|
11
|
+
value.equal?(true) || value.equal?(false)
|
12
|
+
end
|
13
|
+
|
14
|
+
def inspect
|
15
|
+
"Boolean()"
|
16
|
+
end
|
17
|
+
alias to_s inspect
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Strict
|
4
|
+
module Validators
|
5
|
+
class HashOf
|
6
|
+
attr_reader :key_validator, :value_validator
|
7
|
+
|
8
|
+
def initialize(key_validator, value_validator)
|
9
|
+
@key_validator = key_validator
|
10
|
+
@value_validator = value_validator
|
11
|
+
end
|
12
|
+
|
13
|
+
def ===(value)
|
14
|
+
Hash === value && value.all? do |k, v|
|
15
|
+
key_validator === k && value_validator === v
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def inspect
|
20
|
+
"HashOf(#{key_validator.inspect} => #{value_validator.inspect})"
|
21
|
+
end
|
22
|
+
alias to_s inspect
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|