stannum 0.2.0 → 0.4.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/CHANGELOG.md +49 -0
- data/README.md +130 -1200
- data/config/locales/en.rb +4 -0
- data/lib/stannum/association.rb +293 -0
- data/lib/stannum/associations/many.rb +250 -0
- data/lib/stannum/associations/one.rb +106 -0
- data/lib/stannum/associations.rb +11 -0
- data/lib/stannum/attribute.rb +86 -8
- data/lib/stannum/constraints/base.rb +3 -5
- data/lib/stannum/constraints/enum.rb +1 -1
- data/lib/stannum/constraints/equality.rb +1 -1
- data/lib/stannum/constraints/format.rb +72 -0
- data/lib/stannum/constraints/hashes/extra_keys.rb +13 -13
- data/lib/stannum/constraints/hashes/indifferent_extra_keys.rb +47 -0
- data/lib/stannum/constraints/hashes.rb +6 -2
- data/lib/stannum/constraints/identity.rb +1 -1
- data/lib/stannum/constraints/properties/base.rb +124 -0
- data/lib/stannum/constraints/properties/do_not_match_property.rb +117 -0
- data/lib/stannum/constraints/properties/match_property.rb +117 -0
- data/lib/stannum/constraints/properties/matching.rb +112 -0
- data/lib/stannum/constraints/properties.rb +17 -0
- data/lib/stannum/constraints/signature.rb +2 -2
- data/lib/stannum/constraints/tuples/extra_items.rb +6 -6
- data/lib/stannum/constraints/type.rb +4 -4
- data/lib/stannum/constraints/types/array_type.rb +2 -2
- data/lib/stannum/constraints/types/hash_type.rb +4 -4
- data/lib/stannum/constraints/union.rb +1 -1
- data/lib/stannum/constraints/uuid.rb +30 -0
- data/lib/stannum/constraints.rb +3 -0
- data/lib/stannum/contract.rb +7 -7
- data/lib/stannum/contracts/array_contract.rb +2 -7
- data/lib/stannum/contracts/base.rb +15 -15
- data/lib/stannum/contracts/builder.rb +15 -4
- data/lib/stannum/contracts/hash_contract.rb +3 -9
- data/lib/stannum/contracts/indifferent_hash_contract.rb +15 -2
- data/lib/stannum/contracts/map_contract.rb +6 -10
- data/lib/stannum/contracts/parameters/arguments_contract.rb +1 -1
- data/lib/stannum/contracts/parameters/keywords_contract.rb +1 -1
- data/lib/stannum/contracts/parameters/signature_contract.rb +1 -1
- data/lib/stannum/contracts/parameters_contract.rb +4 -4
- data/lib/stannum/contracts/tuple_contract.rb +6 -6
- data/lib/stannum/entities/associations.rb +451 -0
- data/lib/stannum/entities/attributes.rb +316 -0
- data/lib/stannum/entities/constraints.rb +178 -0
- data/lib/stannum/entities/primary_key.rb +148 -0
- data/lib/stannum/entities/properties.rb +208 -0
- data/lib/stannum/entities.rb +16 -0
- data/lib/stannum/entity.rb +87 -0
- data/lib/stannum/errors.rb +12 -16
- data/lib/stannum/messages/default_strategy.rb +2 -2
- data/lib/stannum/parameter_validation.rb +10 -10
- data/lib/stannum/rspec/match_errors_matcher.rb +7 -7
- data/lib/stannum/rspec/validate_parameter.rb +2 -2
- data/lib/stannum/rspec/validate_parameter_matcher.rb +22 -20
- data/lib/stannum/schema.rb +117 -76
- data/lib/stannum/struct.rb +12 -346
- data/lib/stannum/support/optional.rb +1 -1
- data/lib/stannum/version.rb +4 -4
- data/lib/stannum.rb +6 -0
- metadata +26 -85
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'stannum/constraints/properties'
|
4
|
+
require 'stannum/constraints/properties/matching'
|
5
|
+
|
6
|
+
module Stannum::Constraints::Properties
|
7
|
+
# Compares the properties of the given object with the specified property.
|
8
|
+
#
|
9
|
+
# If all of the property values equal the expected value, the constraint will
|
10
|
+
# match the object; otherwise, if there are any non-matching values, the
|
11
|
+
# constraint will not match.
|
12
|
+
#
|
13
|
+
# @example Using an Properties::Match constraint
|
14
|
+
# ConfirmPassword = Struct.new(:password, :confirmation)
|
15
|
+
# constraint = Stannum::Constraints::Properties::MatchProperty.new(
|
16
|
+
# :password,
|
17
|
+
# :confirmation
|
18
|
+
# )
|
19
|
+
#
|
20
|
+
# params = ConfirmPassword.new('tronlives', 'ifightfortheusers')
|
21
|
+
# constraint.matches?(params)
|
22
|
+
# #=> false
|
23
|
+
# constraint.errors_for(params)
|
24
|
+
# #=> [
|
25
|
+
# {
|
26
|
+
# path: [:confirmation],
|
27
|
+
# type: 'stannum.constraints.is_not_equal_to',
|
28
|
+
# data: { expected: '[FILTERED]', actual: '[FILTERED]' }
|
29
|
+
# }
|
30
|
+
# ]
|
31
|
+
#
|
32
|
+
# params = ConfirmPassword.new('tronlives', 'tronlives')
|
33
|
+
# constraint.matches?(params)
|
34
|
+
# #=> true
|
35
|
+
class MatchProperty < Stannum::Constraints::Properties::Matching
|
36
|
+
# The :type of the error generated for a matching object.
|
37
|
+
NEGATED_TYPE = Stannum::Constraints::Equality::NEGATED_TYPE
|
38
|
+
|
39
|
+
# The :type of the error generated for a non-matching object.
|
40
|
+
TYPE = Stannum::Constraints::Equality::TYPE
|
41
|
+
|
42
|
+
# @return [true, false] false if any of the property values match the
|
43
|
+
# reference property value; otherwise true.
|
44
|
+
def does_not_match?(actual) # rubocop:disable Naming/PredicatePrefix
|
45
|
+
return false unless can_match_properties?(actual)
|
46
|
+
|
47
|
+
expected = expected_value(actual)
|
48
|
+
|
49
|
+
return false if skip_property?(expected)
|
50
|
+
|
51
|
+
each_matching_property(
|
52
|
+
actual:,
|
53
|
+
expected:,
|
54
|
+
include_all: true
|
55
|
+
)
|
56
|
+
.none?
|
57
|
+
end
|
58
|
+
|
59
|
+
# (see Stannum::Constraints::Base#errors_for)
|
60
|
+
def errors_for(actual, errors: nil) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
61
|
+
errors ||= Stannum::Errors.new
|
62
|
+
|
63
|
+
return invalid_object_errors(errors) unless can_match_properties?(actual)
|
64
|
+
|
65
|
+
expected = expected_value(actual)
|
66
|
+
matching = each_non_matching_property(actual:, expected:)
|
67
|
+
|
68
|
+
return generic_errors(errors) if matching.none?
|
69
|
+
|
70
|
+
matching.each do |property_name, value|
|
71
|
+
errors[property_name].add(
|
72
|
+
type,
|
73
|
+
message:,
|
74
|
+
expected: filter_parameters? ? '[FILTERED]' : expected_value(actual),
|
75
|
+
actual: filter_parameters? ? '[FILTERED]' : value
|
76
|
+
)
|
77
|
+
end
|
78
|
+
|
79
|
+
errors
|
80
|
+
end
|
81
|
+
|
82
|
+
# @return [true, false] true if the property values match the reference
|
83
|
+
# property value; otherwise false.
|
84
|
+
def matches?(actual)
|
85
|
+
return false unless can_match_properties?(actual)
|
86
|
+
|
87
|
+
expected = expected_value(actual)
|
88
|
+
|
89
|
+
return true if skip_property?(expected)
|
90
|
+
|
91
|
+
each_non_matching_property(actual:, expected:).none?
|
92
|
+
end
|
93
|
+
alias match? matches?
|
94
|
+
|
95
|
+
# (see Stannum::Constraints::Base#negated_errors_for)
|
96
|
+
def negated_errors_for(actual, errors: nil) # rubocop:disable Metrics/MethodLength
|
97
|
+
errors ||= Stannum::Errors.new
|
98
|
+
|
99
|
+
return invalid_object_errors(errors) unless can_match_properties?(actual)
|
100
|
+
|
101
|
+
expected = expected_value(actual)
|
102
|
+
matching = each_matching_property(
|
103
|
+
actual:,
|
104
|
+
expected:,
|
105
|
+
include_all: true
|
106
|
+
)
|
107
|
+
|
108
|
+
return generic_errors(errors) if matching.none?
|
109
|
+
|
110
|
+
matching.each do |property_name, _| # rubocop:disable Style/HashEachMethods
|
111
|
+
errors[property_name].add(negated_type, message: negated_message)
|
112
|
+
end
|
113
|
+
|
114
|
+
errors
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'stannum/constraints/properties'
|
4
|
+
require 'stannum/constraints/properties/base'
|
5
|
+
|
6
|
+
module Stannum::Constraints::Properties
|
7
|
+
# Abstract base class for property matching constraints.
|
8
|
+
class Matching < Stannum::Constraints::Properties::Base
|
9
|
+
# @param reference_name [String, Symbol] the name of the reference property
|
10
|
+
# to compare to.
|
11
|
+
# @param property_names [Array<String, Symbol>] the name or names of the
|
12
|
+
# properties to compare.
|
13
|
+
# @param options [Hash<Symbol, Object>] configuration options for the
|
14
|
+
# constraint. Defaults to an empty Hash.
|
15
|
+
#
|
16
|
+
# @option options allow_empty [true, false] if true, will match against an
|
17
|
+
# object with empty property values, such as an empty string.
|
18
|
+
# @option options allow_nil [true, false] if true, will match against an
|
19
|
+
# object with nil property values.
|
20
|
+
def initialize(reference_name, *property_names, **options)
|
21
|
+
@reference_name = reference_name
|
22
|
+
|
23
|
+
validate_reference_name
|
24
|
+
|
25
|
+
super(*property_names, reference_name:, **options)
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [String, Symbol] the name of the reference property to compare to.
|
29
|
+
attr_reader :reference_name
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def each_matching_property( # rubocop:disable Metrics/MethodLength
|
34
|
+
actual:,
|
35
|
+
expected:,
|
36
|
+
include_all: false,
|
37
|
+
&block
|
38
|
+
)
|
39
|
+
unless block_given?
|
40
|
+
return to_enum(
|
41
|
+
__method__,
|
42
|
+
actual:,
|
43
|
+
expected:,
|
44
|
+
include_all:
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
enumerator = each_property(actual)
|
49
|
+
|
50
|
+
unless include_all
|
51
|
+
enumerator = enumerator.reject { |_, value| skip_property?(value) }
|
52
|
+
end
|
53
|
+
|
54
|
+
enumerator = enumerator.select { |_, value| valid?(expected, value) }
|
55
|
+
|
56
|
+
block_given? ? enumerator.each(&block) : enumerator
|
57
|
+
end
|
58
|
+
|
59
|
+
def each_non_matching_property( # rubocop:disable Metrics/MethodLength
|
60
|
+
actual:,
|
61
|
+
expected:,
|
62
|
+
include_all: false,
|
63
|
+
&block
|
64
|
+
)
|
65
|
+
unless block_given?
|
66
|
+
return to_enum(
|
67
|
+
__method__,
|
68
|
+
actual:,
|
69
|
+
expected:,
|
70
|
+
include_all:
|
71
|
+
)
|
72
|
+
end
|
73
|
+
|
74
|
+
enumerator = each_property(actual)
|
75
|
+
|
76
|
+
unless include_all
|
77
|
+
enumerator = enumerator.reject { |_, value| skip_property?(value) }
|
78
|
+
end
|
79
|
+
|
80
|
+
enumerator = enumerator.reject { |_, value| valid?(expected, value) }
|
81
|
+
|
82
|
+
block_given? ? enumerator.each(&block) : enumerator
|
83
|
+
end
|
84
|
+
|
85
|
+
def expected_value(actual)
|
86
|
+
actual[reference_name]
|
87
|
+
end
|
88
|
+
|
89
|
+
def filter_parameters?
|
90
|
+
return @filter_parameters unless @filter_parameters.nil?
|
91
|
+
|
92
|
+
filters = filtered_parameters.map { |param| Regexp.new(param.to_s) }
|
93
|
+
|
94
|
+
@filter_parameters =
|
95
|
+
[reference_name, *property_names].any? do |property_name|
|
96
|
+
filters.any? { |filter| filter.match?(property_name.to_s) }
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def generic_errors(errors)
|
101
|
+
errors.add(Stannum::Constraints::Base::NEGATED_TYPE)
|
102
|
+
end
|
103
|
+
|
104
|
+
def valid?(expected, value)
|
105
|
+
value == expected
|
106
|
+
end
|
107
|
+
|
108
|
+
def validate_reference_name
|
109
|
+
tools.assertions.validate_name(reference_name, as: 'reference name')
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'stannum/constraints'
|
4
|
+
|
5
|
+
module Stannum::Constraints
|
6
|
+
# Namespace for Object property-specific constraints.
|
7
|
+
module Properties
|
8
|
+
autoload :Base,
|
9
|
+
'stannum/constraints/properties/base'
|
10
|
+
autoload :DoNotMatchProperty,
|
11
|
+
'stannum/constraints/properties/do_not_match_property'
|
12
|
+
autoload :MatchProperty,
|
13
|
+
'stannum/constraints/properties/match_property'
|
14
|
+
autoload :Matching,
|
15
|
+
'stannum/constraints/properties/matching'
|
16
|
+
end
|
17
|
+
end
|
@@ -27,7 +27,7 @@ module Stannum::Constraints
|
|
27
27
|
|
28
28
|
@expected_methods = expected_methods
|
29
29
|
|
30
|
-
super(expected_methods
|
30
|
+
super(expected_methods:, **options)
|
31
31
|
end
|
32
32
|
|
33
33
|
# @return [Array<String, Symbol>] the methods the object is expected to
|
@@ -36,7 +36,7 @@ module Stannum::Constraints
|
|
36
36
|
|
37
37
|
# @return [true, false] true if the object does not respond to any of the
|
38
38
|
# expected methods; otherwise false.
|
39
|
-
def does_not_match?(actual)
|
39
|
+
def does_not_match?(actual) # rubocop:disable Naming/PredicatePrefix
|
40
40
|
each_missing_method(actual).to_a == expected_methods
|
41
41
|
end
|
42
42
|
|
@@ -24,12 +24,12 @@ module Stannum::Constraints::Tuples
|
|
24
24
|
# @param options [Hash<Symbol, Object>] Configuration options for the
|
25
25
|
# constraint. Defaults to an empty Hash.
|
26
26
|
def initialize(expected_count, **options)
|
27
|
-
super(expected_count
|
27
|
+
super(expected_count:, **options)
|
28
28
|
end
|
29
29
|
|
30
30
|
# @return [true, false] true if the object responds to #size and the object
|
31
31
|
# size is greater than the number of expected items; otherwise false.
|
32
|
-
def does_not_match?(actual)
|
32
|
+
def does_not_match?(actual) # rubocop:disable Naming/PredicatePrefix
|
33
33
|
return false unless actual.respond_to?(:size)
|
34
34
|
|
35
35
|
actual.size > expected_count
|
@@ -40,7 +40,7 @@ module Stannum::Constraints::Tuples
|
|
40
40
|
errors ||= Stannum::Errors.new
|
41
41
|
|
42
42
|
unless actual.respond_to?(:size)
|
43
|
-
return add_invalid_tuple_error(actual
|
43
|
+
return add_invalid_tuple_error(actual:, errors:)
|
44
44
|
end
|
45
45
|
|
46
46
|
each_extra_item(actual) do |item, index|
|
@@ -72,13 +72,13 @@ module Stannum::Constraints::Tuples
|
|
72
72
|
def add_invalid_tuple_error(actual:, errors:)
|
73
73
|
Stannum::Constraints::Signature
|
74
74
|
.new(:size)
|
75
|
-
.errors_for(actual, errors:
|
75
|
+
.errors_for(actual, errors:)
|
76
76
|
end
|
77
77
|
|
78
|
-
def each_extra_item(actual, &
|
78
|
+
def each_extra_item(actual, &)
|
79
79
|
return if matches?(actual)
|
80
80
|
|
81
|
-
actual[expected_count
|
81
|
+
actual[expected_count..].each.with_index(expected_count, &)
|
82
82
|
end
|
83
83
|
end
|
84
84
|
end
|
@@ -47,10 +47,10 @@ module Stannum::Constraints
|
|
47
47
|
expected_type = resolve_expected_type(expected_type)
|
48
48
|
|
49
49
|
super(
|
50
|
-
expected_type
|
50
|
+
expected_type:,
|
51
51
|
**resolve_required_option(
|
52
|
-
optional
|
53
|
-
required
|
52
|
+
optional:,
|
53
|
+
required:,
|
54
54
|
**options
|
55
55
|
)
|
56
56
|
)
|
@@ -107,7 +107,7 @@ module Stannum::Constraints
|
|
107
107
|
|
108
108
|
raise ArgumentError,
|
109
109
|
'expected type must be a Class or Module',
|
110
|
-
caller[1
|
110
|
+
caller[1..]
|
111
111
|
end
|
112
112
|
end
|
113
113
|
end
|
@@ -60,7 +60,7 @@ module Stannum::Constraints::Types
|
|
60
60
|
# otherwise false.
|
61
61
|
#
|
62
62
|
# @see Stannum::Constraints::Types::ArrayType#matches?
|
63
|
-
def does_not_match?(actual)
|
63
|
+
def does_not_match?(actual) # rubocop:disable Naming/PredicatePrefix
|
64
64
|
!matches_type?(actual)
|
65
65
|
end
|
66
66
|
|
@@ -126,7 +126,7 @@ module Stannum::Constraints::Types
|
|
126
126
|
end
|
127
127
|
|
128
128
|
def error_properties
|
129
|
-
super
|
129
|
+
super.merge(allow_empty: allow_empty?)
|
130
130
|
end
|
131
131
|
|
132
132
|
def item_type_matches?(actual)
|
@@ -73,7 +73,7 @@ module Stannum::Constraints::Types
|
|
73
73
|
# false.
|
74
74
|
#
|
75
75
|
# @see Stannum::Constraints::Types::HashType#matches?
|
76
|
-
def does_not_match?(actual)
|
76
|
+
def does_not_match?(actual) # rubocop:disable Naming/PredicatePrefix
|
77
77
|
!matches_type?(actual)
|
78
78
|
end
|
79
79
|
|
@@ -85,9 +85,9 @@ module Stannum::Constraints::Types
|
|
85
85
|
|
86
86
|
return add_presence_error(errors) unless presence_matches?(actual)
|
87
87
|
|
88
|
-
update_key_errors_for(actual
|
88
|
+
update_key_errors_for(actual:, errors:)
|
89
89
|
|
90
|
-
update_value_errors_for(actual
|
90
|
+
update_value_errors_for(actual:, errors:)
|
91
91
|
|
92
92
|
errors
|
93
93
|
end
|
@@ -155,7 +155,7 @@ module Stannum::Constraints::Types
|
|
155
155
|
end
|
156
156
|
|
157
157
|
def error_properties
|
158
|
-
super
|
158
|
+
super.merge(allow_empty: allow_empty?)
|
159
159
|
end
|
160
160
|
|
161
161
|
def key_type_matches?(actual)
|
@@ -31,7 +31,7 @@ module Stannum::Constraints
|
|
31
31
|
def initialize(first, *rest, **options)
|
32
32
|
expected_constraints = rest.unshift(first)
|
33
33
|
|
34
|
-
super(expected_constraints
|
34
|
+
super(expected_constraints:, **options)
|
35
35
|
|
36
36
|
@expected_constraints = expected_constraints
|
37
37
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'stannum/constraints'
|
4
|
+
|
5
|
+
module Stannum::Constraints
|
6
|
+
# A UUID constraint asserts the value is a string in UUID format.
|
7
|
+
#
|
8
|
+
# @example Using a UUID constraint with a String format.
|
9
|
+
# constraint = Stannum::Constraints::Uuid.new
|
10
|
+
#
|
11
|
+
# constraint.matches?(nil) #=> false
|
12
|
+
# constraint.matches?('Hello, world') #=> false
|
13
|
+
# constraint.matches?('01234567-89ab-cdef-0123-456789abcdef') #=> true
|
14
|
+
class Uuid < Stannum::Constraints::Format
|
15
|
+
# The :type of the error generated for a matching object.
|
16
|
+
NEGATED_TYPE = 'stannum.constraints.is_a_uuid'
|
17
|
+
|
18
|
+
# The :type of the error generated for a non-matching object.
|
19
|
+
TYPE = 'stannum.constraints.is_not_a_uuid'
|
20
|
+
|
21
|
+
# Regular expression describing a valid upper- or lower-case UUID.
|
22
|
+
UUID_FORMAT = /\A\h{8}-\h{4}-\h{4}-\h{4}-\h{12}\z/
|
23
|
+
|
24
|
+
# @param options [Hash<Symbol, Object>] Configuration options for the
|
25
|
+
# constraint. Defaults to an empty Hash.
|
26
|
+
def initialize(**options)
|
27
|
+
super(UUID_FORMAT, **options)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/stannum/constraints.rb
CHANGED
@@ -12,16 +12,19 @@ module Stannum
|
|
12
12
|
autoload :Delegator, 'stannum/constraints/delegator'
|
13
13
|
autoload :Enum, 'stannum/constraints/enum'
|
14
14
|
autoload :Equality, 'stannum/constraints/equality'
|
15
|
+
autoload :Format, 'stannum/constraints/format'
|
15
16
|
autoload :Hashes, 'stannum/constraints/hashes'
|
16
17
|
autoload :Identity, 'stannum/constraints/identity'
|
17
18
|
autoload :Nothing, 'stannum/constraints/nothing'
|
18
19
|
autoload :Parameters, 'stannum/constraints/parameters'
|
19
20
|
autoload :Presence, 'stannum/constraints/presence'
|
21
|
+
autoload :Properties, 'stannum/constraints/properties'
|
20
22
|
autoload :Signature, 'stannum/constraints/signature'
|
21
23
|
autoload :Signatures, 'stannum/constraints/signatures'
|
22
24
|
autoload :Tuples, 'stannum/constraints/tuples'
|
23
25
|
autoload :Type, 'stannum/constraints/type'
|
24
26
|
autoload :Types, 'stannum/constraints/types'
|
25
27
|
autoload :Union, 'stannum/constraints/union'
|
28
|
+
autoload :Uuid, 'stannum/constraints/uuid'
|
26
29
|
end
|
27
30
|
end
|
data/lib/stannum/contract.rb
CHANGED
@@ -117,12 +117,12 @@ module Stannum
|
|
117
117
|
# @overload property(**options) { |value| }
|
118
118
|
# Creates a new Stannum::Constraint object with the given block, and
|
119
119
|
# adds that constraint to the contract for the property.
|
120
|
-
def property(property, constraint = nil, **options, &
|
120
|
+
def property(property, constraint = nil, **options, &)
|
121
121
|
self.constraint(
|
122
122
|
constraint,
|
123
|
-
property
|
123
|
+
property:,
|
124
124
|
**options,
|
125
|
-
&
|
125
|
+
&
|
126
126
|
)
|
127
127
|
end
|
128
128
|
end
|
@@ -138,12 +138,12 @@ module Stannum
|
|
138
138
|
# @see #add_property_constraint.
|
139
139
|
def add_constraint(constraint, property: nil, sanity: false, **options)
|
140
140
|
validate_constraint(constraint)
|
141
|
-
validate_property(property
|
141
|
+
validate_property(property:, **options)
|
142
142
|
|
143
143
|
@constraints << Stannum::Contracts::Definition.new(
|
144
|
-
constraint
|
144
|
+
constraint:,
|
145
145
|
contract: self,
|
146
|
-
options: options.merge(property
|
146
|
+
options: options.merge(property:, sanity:)
|
147
147
|
)
|
148
148
|
|
149
149
|
self
|
@@ -179,7 +179,7 @@ module Stannum
|
|
179
179
|
#
|
180
180
|
# @see #add_constraint.
|
181
181
|
def add_property_constraint(property, constraint, sanity: false, **options)
|
182
|
-
add_constraint(constraint, property
|
182
|
+
add_constraint(constraint, property:, sanity:, **options)
|
183
183
|
end
|
184
184
|
|
185
185
|
protected
|
@@ -75,12 +75,7 @@ module Stannum::Contracts
|
|
75
75
|
# @param options [Hash<Symbol, Object>] Configuration options for the
|
76
76
|
# contract. Defaults to an empty Hash.
|
77
77
|
def initialize(allow_extra_items: false, item_type: nil, **options, &block)
|
78
|
-
super
|
79
|
-
allow_extra_items: allow_extra_items,
|
80
|
-
item_type: item_type,
|
81
|
-
**options,
|
82
|
-
&block
|
83
|
-
)
|
78
|
+
super
|
84
79
|
end
|
85
80
|
|
86
81
|
# @return [Stannum::Constraints::Base, nil] the expected type for the items
|
@@ -100,7 +95,7 @@ module Stannum::Contracts
|
|
100
95
|
|
101
96
|
def add_type_constraint
|
102
97
|
add_constraint(
|
103
|
-
Stannum::Constraints::Types::ArrayType.new(item_type:
|
98
|
+
Stannum::Constraints::Types::ArrayType.new(item_type:),
|
104
99
|
sanity: true
|
105
100
|
)
|
106
101
|
end
|
@@ -104,13 +104,13 @@ module Stannum::Contracts
|
|
104
104
|
|
105
105
|
# @param options [Hash<Symbol, Object>] Configuration options for the
|
106
106
|
# contract. Defaults to an empty Hash.
|
107
|
-
def initialize(**options, &
|
107
|
+
def initialize(**options, &)
|
108
108
|
@constraints = []
|
109
109
|
@concatenated = []
|
110
110
|
|
111
111
|
super(**options)
|
112
112
|
|
113
|
-
define_constraints(&
|
113
|
+
define_constraints(&)
|
114
114
|
end
|
115
115
|
|
116
116
|
# Performs an equality comparison.
|
@@ -141,9 +141,9 @@ module Stannum::Contracts
|
|
141
141
|
validate_constraint(constraint)
|
142
142
|
|
143
143
|
@constraints << Stannum::Contracts::Definition.new(
|
144
|
-
constraint
|
144
|
+
constraint:,
|
145
145
|
contract: self,
|
146
|
-
options: options.merge(sanity:
|
146
|
+
options: options.merge(sanity:)
|
147
147
|
)
|
148
148
|
|
149
149
|
self
|
@@ -216,7 +216,7 @@ module Stannum::Contracts
|
|
216
216
|
# @see #each_pair
|
217
217
|
# @see #matches?
|
218
218
|
# @see #negated_match
|
219
|
-
def does_not_match?(actual)
|
219
|
+
def does_not_match?(actual) # rubocop:disable Naming/PredicatePrefix
|
220
220
|
each_pair(actual) do |definition, value|
|
221
221
|
if definition.contract.match_negated_constraint(definition, value)
|
222
222
|
next unless definition.sanity?
|
@@ -355,7 +355,7 @@ module Stannum::Contracts
|
|
355
355
|
#
|
356
356
|
# @param actual [Object] The object to match.
|
357
357
|
#
|
358
|
-
# @return [<
|
358
|
+
# @return [Array<Boolean, Stannum::Errors>] the status (true or false) and
|
359
359
|
# the generated errors object.
|
360
360
|
#
|
361
361
|
# @see #each_pair
|
@@ -459,7 +459,7 @@ module Stannum::Contracts
|
|
459
459
|
#
|
460
460
|
# @param actual [Object] The object to match.
|
461
461
|
#
|
462
|
-
# @return [<
|
462
|
+
# @return [Array<Boolean, Stannum::Errors>] the status (true or false) and
|
463
463
|
# the generated errors object.
|
464
464
|
#
|
465
465
|
# @see #does_not_match?
|
@@ -518,7 +518,7 @@ module Stannum::Contracts
|
|
518
518
|
self
|
519
519
|
end
|
520
520
|
|
521
|
-
def each_unscoped_constraint(&
|
521
|
+
def each_unscoped_constraint(&)
|
522
522
|
return enum_for(:each_unscoped_constraint) unless block_given?
|
523
523
|
|
524
524
|
each_concatenated_contract do |contract|
|
@@ -527,14 +527,14 @@ module Stannum::Contracts
|
|
527
527
|
end
|
528
528
|
end
|
529
529
|
|
530
|
-
@constraints.each(&
|
530
|
+
@constraints.each(&)
|
531
531
|
end
|
532
532
|
|
533
|
-
def match_constraint(definition, value)
|
533
|
+
def match_constraint(definition, value) # rubocop:disable Naming/PredicateMethod
|
534
534
|
definition.constraint.matches?(value)
|
535
535
|
end
|
536
536
|
|
537
|
-
def match_negated_constraint(definition, value)
|
537
|
+
def match_negated_constraint(definition, value) # rubocop:disable Naming/PredicateMethod
|
538
538
|
definition.constraint.does_not_match?(value)
|
539
539
|
end
|
540
540
|
|
@@ -548,14 +548,14 @@ module Stannum::Contracts
|
|
548
548
|
|
549
549
|
private
|
550
550
|
|
551
|
-
def define_constraints(&
|
552
|
-
self.class::Builder.new(self).instance_exec(&
|
551
|
+
def define_constraints(&)
|
552
|
+
self.class::Builder.new(self).instance_exec(&) if block_given?
|
553
553
|
end
|
554
554
|
|
555
|
-
def each_concatenated_contract(&
|
555
|
+
def each_concatenated_contract(&)
|
556
556
|
return enum_for(:each_concatenated_contract) unless block_given?
|
557
557
|
|
558
|
-
@concatenated.each(&
|
558
|
+
@concatenated.each(&)
|
559
559
|
end
|
560
560
|
|
561
561
|
def equal_definitions?(other) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
@@ -18,6 +18,17 @@ module Stannum::Contracts
|
|
18
18
|
# @return [Stannum::Contract] The contract to which constraints are added.
|
19
19
|
attr_reader :contract
|
20
20
|
|
21
|
+
# Concatenate the constraints from the given other contract.
|
22
|
+
#
|
23
|
+
# @param other [Stannum::Contract] the other contract.
|
24
|
+
#
|
25
|
+
# @return [self] the contract builder.
|
26
|
+
#
|
27
|
+
# @see Stannum::Contracts::Base#concat.
|
28
|
+
def concat(other)
|
29
|
+
contract.concat(other)
|
30
|
+
end
|
31
|
+
|
21
32
|
# Adds a constraint to the contract.
|
22
33
|
#
|
23
34
|
# @overload constraint(constraint, **options)
|
@@ -36,8 +47,8 @@ module Stannum::Contracts
|
|
36
47
|
# matching object.
|
37
48
|
# @option options type [String] The error type generated for a
|
38
49
|
# non-matching object.
|
39
|
-
def constraint(constraint = nil, **options, &
|
40
|
-
constraint = resolve_constraint(constraint, **options, &
|
50
|
+
def constraint(constraint = nil, **options, &)
|
51
|
+
constraint = resolve_constraint(constraint, **options, &)
|
41
52
|
|
42
53
|
contract.add_constraint(constraint, **options)
|
43
54
|
|
@@ -47,8 +58,8 @@ module Stannum::Contracts
|
|
47
58
|
private
|
48
59
|
|
49
60
|
def ambiguous_values_error(constraint)
|
50
|
-
'expected either a block or a constraint instance, but received both a' \
|
51
|
-
"
|
61
|
+
'expected either a block or a constraint instance, but received both a ' \
|
62
|
+
"block and #{constraint.inspect}"
|
52
63
|
end
|
53
64
|
|
54
65
|
def resolve_constraint(constraint = nil, **options, &block)
|
@@ -100,13 +100,7 @@ module Stannum::Contracts
|
|
100
100
|
**options,
|
101
101
|
&block
|
102
102
|
)
|
103
|
-
super
|
104
|
-
allow_extra_keys: allow_extra_keys,
|
105
|
-
key_type: key_type,
|
106
|
-
value_type: value_type,
|
107
|
-
**options,
|
108
|
-
&block
|
109
|
-
)
|
103
|
+
super
|
110
104
|
end
|
111
105
|
|
112
106
|
# @return [Stannum::Constraints::Base, Class, nil] the expected type for the
|
@@ -140,8 +134,8 @@ module Stannum::Contracts
|
|
140
134
|
def add_type_constraint
|
141
135
|
add_constraint(
|
142
136
|
Stannum::Constraints::Types::HashType.new(
|
143
|
-
key_type
|
144
|
-
value_type:
|
137
|
+
key_type:,
|
138
|
+
value_type:
|
145
139
|
),
|
146
140
|
sanity: true
|
147
141
|
)
|