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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +49 -0
  3. data/README.md +130 -1200
  4. data/config/locales/en.rb +4 -0
  5. data/lib/stannum/association.rb +293 -0
  6. data/lib/stannum/associations/many.rb +250 -0
  7. data/lib/stannum/associations/one.rb +106 -0
  8. data/lib/stannum/associations.rb +11 -0
  9. data/lib/stannum/attribute.rb +86 -8
  10. data/lib/stannum/constraints/base.rb +3 -5
  11. data/lib/stannum/constraints/enum.rb +1 -1
  12. data/lib/stannum/constraints/equality.rb +1 -1
  13. data/lib/stannum/constraints/format.rb +72 -0
  14. data/lib/stannum/constraints/hashes/extra_keys.rb +13 -13
  15. data/lib/stannum/constraints/hashes/indifferent_extra_keys.rb +47 -0
  16. data/lib/stannum/constraints/hashes.rb +6 -2
  17. data/lib/stannum/constraints/identity.rb +1 -1
  18. data/lib/stannum/constraints/properties/base.rb +124 -0
  19. data/lib/stannum/constraints/properties/do_not_match_property.rb +117 -0
  20. data/lib/stannum/constraints/properties/match_property.rb +117 -0
  21. data/lib/stannum/constraints/properties/matching.rb +112 -0
  22. data/lib/stannum/constraints/properties.rb +17 -0
  23. data/lib/stannum/constraints/signature.rb +2 -2
  24. data/lib/stannum/constraints/tuples/extra_items.rb +6 -6
  25. data/lib/stannum/constraints/type.rb +4 -4
  26. data/lib/stannum/constraints/types/array_type.rb +2 -2
  27. data/lib/stannum/constraints/types/hash_type.rb +4 -4
  28. data/lib/stannum/constraints/union.rb +1 -1
  29. data/lib/stannum/constraints/uuid.rb +30 -0
  30. data/lib/stannum/constraints.rb +3 -0
  31. data/lib/stannum/contract.rb +7 -7
  32. data/lib/stannum/contracts/array_contract.rb +2 -7
  33. data/lib/stannum/contracts/base.rb +15 -15
  34. data/lib/stannum/contracts/builder.rb +15 -4
  35. data/lib/stannum/contracts/hash_contract.rb +3 -9
  36. data/lib/stannum/contracts/indifferent_hash_contract.rb +15 -2
  37. data/lib/stannum/contracts/map_contract.rb +6 -10
  38. data/lib/stannum/contracts/parameters/arguments_contract.rb +1 -1
  39. data/lib/stannum/contracts/parameters/keywords_contract.rb +1 -1
  40. data/lib/stannum/contracts/parameters/signature_contract.rb +1 -1
  41. data/lib/stannum/contracts/parameters_contract.rb +4 -4
  42. data/lib/stannum/contracts/tuple_contract.rb +6 -6
  43. data/lib/stannum/entities/associations.rb +451 -0
  44. data/lib/stannum/entities/attributes.rb +316 -0
  45. data/lib/stannum/entities/constraints.rb +178 -0
  46. data/lib/stannum/entities/primary_key.rb +148 -0
  47. data/lib/stannum/entities/properties.rb +208 -0
  48. data/lib/stannum/entities.rb +16 -0
  49. data/lib/stannum/entity.rb +87 -0
  50. data/lib/stannum/errors.rb +12 -16
  51. data/lib/stannum/messages/default_strategy.rb +2 -2
  52. data/lib/stannum/parameter_validation.rb +10 -10
  53. data/lib/stannum/rspec/match_errors_matcher.rb +7 -7
  54. data/lib/stannum/rspec/validate_parameter.rb +2 -2
  55. data/lib/stannum/rspec/validate_parameter_matcher.rb +22 -20
  56. data/lib/stannum/schema.rb +117 -76
  57. data/lib/stannum/struct.rb +12 -346
  58. data/lib/stannum/support/optional.rb +1 -1
  59. data/lib/stannum/version.rb +4 -4
  60. data/lib/stannum.rb +6 -0
  61. 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: expected_methods, **options)
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: expected_count, **options)
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: actual, errors: errors)
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: errors)
75
+ .errors_for(actual, errors:)
76
76
  end
77
77
 
78
- def each_extra_item(actual, &block)
78
+ def each_extra_item(actual, &)
79
79
  return if matches?(actual)
80
80
 
81
- actual[expected_count..-1].each.with_index(expected_count, &block)
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: expected_type,
50
+ expected_type:,
51
51
  **resolve_required_option(
52
- optional: optional,
53
- required: 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..-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().merge(allow_empty: allow_empty?)
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: actual, errors: errors)
88
+ update_key_errors_for(actual:, errors:)
89
89
 
90
- update_value_errors_for(actual: actual, errors: errors)
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().merge(allow_empty: allow_empty?)
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: expected_constraints, **options)
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
@@ -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
@@ -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, &block)
120
+ def property(property, constraint = nil, **options, &)
121
121
  self.constraint(
122
122
  constraint,
123
- property: property,
123
+ property:,
124
124
  **options,
125
- &block
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: property, **options)
141
+ validate_property(property:, **options)
142
142
 
143
143
  @constraints << Stannum::Contracts::Definition.new(
144
- constraint: constraint,
144
+ constraint:,
145
145
  contract: self,
146
- options: options.merge(property: property, sanity: sanity)
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: property, sanity: sanity, **options)
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: 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, &block)
107
+ def initialize(**options, &)
108
108
  @constraints = []
109
109
  @concatenated = []
110
110
 
111
111
  super(**options)
112
112
 
113
- define_constraints(&block)
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: constraint,
144
+ constraint:,
145
145
  contract: self,
146
- options: options.merge(sanity: 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 [<Array(Boolean, Stannum::Errors)>] the status (true or false) and
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 [<Array(Boolean, Stannum::Errors)>] the status (true or false) and
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(&block)
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(&block)
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(&block)
552
- self.class::Builder.new(self).instance_exec(&block) if block_given?
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(&block)
555
+ def each_concatenated_contract(&)
556
556
  return enum_for(:each_concatenated_contract) unless block_given?
557
557
 
558
- @concatenated.each(&block)
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, &block)
40
- constraint = resolve_constraint(constraint, **options, &block)
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
- " block and #{constraint.inspect}"
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: key_type,
144
- value_type: value_type
137
+ key_type:,
138
+ value_type:
145
139
  ),
146
140
  sanity: true
147
141
  )