stannum 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 74ab0240c5b4e242ba1f5dd6deb9724bb32ee6f310fbda553a2e0962fdfa7259
4
- data.tar.gz: e2eac42708bd5b00dbd9a633ab8a9ecd13d4198a34754cd3149349a2d34c267d
3
+ metadata.gz: b13aa2cda5a99f544b2e7c281147f51b4e4fa02e394e4b6d017dcc2b1f03705d
4
+ data.tar.gz: e84dc0c10ccfd8989dcec729c485136c099257079df57cb44e82befb42ebf9b8
5
5
  SHA512:
6
- metadata.gz: f2fd1eada7dd26a9fa0f62a93b59c781c89d0aa638efe27869f45d1421fdf2e83a50bcd2c75ed86e4e3350907c00c86f499c4663a93082c599e38147a8ea39a7
7
- data.tar.gz: 33060d922ae97025d104e372f1d42e6616bc30f03156eff19c904e407c9b4db56475d6acad7ff322edba6949616a163c3817c60cc7a539ef4d631a4a389bc99a
6
+ metadata.gz: bd159ef522b377cb71e4ebc58d8e46987906a1b3a36a5e905965a5cef9dfcf09b3d3b6818519659a05a19b894b516af8d37ee17a60beb4c72fe4a5df04b2b97d
7
+ data.tar.gz: 806b0a8e067afee2185ba5164d85127543f4e3e821a2ca5582afa13135e6f1b4469f9c5cfb6461bfe3f376e17ec9d2c4580780f3b89d574764856593a660b763
data/CHANGELOG.md CHANGED
@@ -1,9 +1,34 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Constraints
6
+
7
+ - Implemented `Stannum::Constraints::Properties::MatchProperty`
8
+ - Implemented `Stannum::Constraints::Properties::DoNotMatchProperty`
9
+
10
+ ### Contracts
11
+
12
+ - Added `#concat` to `Stannum::Contracts::Builder`.
13
+
14
+ ### Entities
15
+
16
+ Implemented `Stannum::Entity`, a replacement for the existing `Stannum::Struct`.
17
+
18
+ Entitise are largely identical to structs, except for the constructor signature - entities require properties to be passed as keyword parameters, rather than as an attributes hash. Entities (and now Structs) are defined using composable modules.
19
+
20
+ - Implemented `Stannum::Entities::Attributes`.
21
+ - Implemented `Stannum::Entities::Constraints`.
22
+ - Implemented `Stannum::Entities::Properties`.
23
+
24
+ `Stannum::Struct` is now deprecated, and will be removed in a future release.
25
+
3
26
  ## 0.2.0
4
27
 
5
28
  ### Constraints
6
29
 
30
+ #### Parameter Constraints
31
+
7
32
  - Implemented `Stannum::Constraints::Parameters::ExtraArguments`
8
33
  - Implemented `Stannum::Constraints::Parameters::ExtraKeywords`
9
34
 
data/README.md CHANGED
@@ -7,7 +7,7 @@ Stannum defines the following objects:
7
7
  - [Constraints](#constraints): A validator object that responds to `#match`, `#matches?` and `#errors_for` for a given object.
8
8
  - [Contracts](#contracts): A collection of constraints about an object or its properties. Obeys the `Constraint` interface.
9
9
  - [Errors](#errors): Data object for storing validation errors. Supports arbitrary nesting of errors.
10
- - [Structs](#structs): Defines a mutable data object with a specified set of typed attributes.
10
+ - [Entities](#entities): Defines a mutable data object with a specified set of typed attributes.
11
11
 
12
12
  ## About
13
13
 
@@ -17,11 +17,11 @@ First and foremost, Stannum provides you with the tools to validate your data. U
17
17
 
18
18
  Finally, you can combine your constraints into a `Stannum::Contract` to combine multiple validations of your object and its properties. Stannum provides pre-defined contracts for asserting on objects, `Array`s, `Hash`es, and even method parameters.
19
19
 
20
- Stannum also defines the `Stannum::Struct` module for defining structured data entities that are not tied to any framework or datastore. Stannum structs have more functionality and a friendlier interface than a core library `Struct`, provide more structure than a `Hash` or hash-like object (such as an `OpenStruct` or `Hashie::Mash`), and are completely independent from the source of the data. Need to load seed data from a YAML configuration file, perform operations in a SQL database, cross-reference with a MongoDB data store, and use an in-memory data array for lightning-fast tests? A `Stannum::Struct` won't fight you every step of the way.
20
+ Stannum also defines the `Stannum::Entity` module for defining structured data entities that are not tied to any framework or datastore. Stannum entities have more functionality and a friendlier interface than a core library `Struct`, provide more structure than a `Hash` or hash-like object (such as an `OpenStruct` or `Hashie::Mash`), and are completely independent from the source of the data. Need to load seed data from a YAML configuration file, perform operations in a SQL database, cross-reference with a MongoDB data store, and use an in-memory data array for lightning-fast tests? A `Stannum::Entity` won't fight you every step of the way.
21
21
 
22
22
  ### Why Stannum?
23
23
 
24
- Stannum is not tied to any framework. You can create constraints and contracts to validate Ruby objects and Structs, data structures such as Arrays, Hashes, and Sets, and even framework objects such as `ActiveRecord::Model`s and `Mongoid::Document`s.
24
+ Stannum is not tied to any framework. You can create constraints and contracts to validate Ruby objects and Entities, data structures such as Arrays, Hashes, and Sets, and even framework objects such as `ActiveRecord::Model`s and `Mongoid::Document`s.
25
25
 
26
26
  Still, most projects and applications use one framework to handle their data. Why use Stannum constraints?
27
27
 
@@ -33,7 +33,7 @@ Still, most projects and applications use one framework to handle their data. Wh
33
33
 
34
34
  ### Compatibility
35
35
 
36
- Stannum is tested against Ruby (MRI) 2.6 through 3.0.
36
+ Stannum is tested against Ruby (MRI) 2.7 through 3.2.
37
37
 
38
38
  ### Documentation
39
39
 
@@ -331,7 +331,7 @@ contract.does_not_match?({ color: 'blue', shape: 'square'})
331
331
 
332
332
  Note that for an object that partially matches the contract, both `#matches?` and `#does_not_match?` methods will return false. If you want to check whether **any** of the constraints do not match the object, use the `#matches?` method and apply the `!` boolean negation operator (or switch from an `if` to an `unless`).
333
333
 
334
- #### Property Constraints
334
+ #### Constraining Properties
335
335
 
336
336
  Constraints can also define constraints on the *properties* of the matched object. This is a powerful feature for defining validations on objects and nested data structures. To define a property constraint, use the `property` macro in a contract constructor block, or use the `#add_property_constraint` method on an existing contract.
337
337
 
@@ -725,16 +725,20 @@ errors.first.message
725
725
 
726
726
  Stannum uses the strategy pattern to determine how error messages are generated. You can pass the `strategy:` keyword to `#with_messages` to force Stannum to use the specified strategy, or set the `Stannum::Messages.strategy` property to define the default for your application. The default strategy for Stannum uses an I18n-like configuration file to define messages based on the type and optionally the data for each error.
727
727
 
728
- <a id="structs"></a>
728
+ <a id="entities"></a>
729
729
 
730
- ### Structs
730
+ ### Entities
731
731
 
732
- While constraints and contracts are used to validate data, structs are used to define and structure that data. Each `Stannum::Struct` contains a specific set of attributes, and each attribute has a type definition that is a `Class` or `Module` or the name of a Class or Module.
732
+ While constraints and contracts are used to validate data, entities are used to define and structure that data. Each `Stannum::Entity` contains a specific set of attributes, and each attribute has a type definition that is a `Class` or `Module` or the name of a Class or Module.
733
733
 
734
- Structs are defined by creating a new class and including `Stannum::Struct`:
734
+ Entities are defined by creating a new class and including `Stannum::Entity`:
735
735
 
736
736
  ```ruby
737
+ require 'stannum'
738
+
737
739
  class Gadget
740
+ include Stannum::Entity
741
+
738
742
  attribute :name, String
739
743
  attribute :description, String, optional: true
740
744
  attribute :quantity, Integer, default: 0
@@ -763,21 +767,21 @@ gadget[:description]
763
767
 
764
768
  Our `Gadget` class has three attributes: `#name`, `#description`, and `#quantity`, which we are defining using the `.attribute` class method.
765
769
 
766
- We can initialize a gadget with values by passing the desired attributes to `.new`. We can read or write the attributes using either dot `.` notation or `#[]` notation. Finally, we can access all of a struct's attributes and values using the `#attributes` method.
770
+ We can initialize a gadget with values by passing the desired attributes to `.new`. We can read or write the attributes using either dot `.` notation or `#[]` notation. Finally, we can access all of a entity's attributes and values using the `#attributes` method.
767
771
 
768
- `Stannum::Struct` defines a number of helper methods for interacting with a struct's attributes:
772
+ `Stannum::Entity` defines a number of helper methods for interacting with a entity's attributes:
769
773
 
770
774
  - `#[](attribute)`: Returns the value of the given attribute.
771
775
  - `#[]=(attribute, value)`: Writes the given value to the given attribute.
772
- - `#assign_attributes(values)`: Updates the struct's attributes using the given values. If an attribute is not given, that value is unchanged.
776
+ - `#assign_attributes(values)`: Updates the entity's attributes using the given values. If an attribute is not given, that value is unchanged.
773
777
  - `#attributes`: Returns a hash containing the attribute keys and values.
774
- - `#attributes=(values)`: Sets the struct's attributes to the given values. If an attribute is not given, that attribute is set to `nil`.
778
+ - `#attributes=(values)`: Sets the entity's attributes to the given values. If an attribute is not given, that attribute is set to `nil`.
775
779
 
776
- For all of the above methods, if a given attribute is invalid or the attribute is not defined on the struct, an `ArgumentError` will be raised.
780
+ For all of the above methods, if a given attribute is invalid or the attribute is not defined on the entity, an `ArgumentError` will be raised.
777
781
 
778
782
  #### Attributes
779
783
 
780
- A struct's attributes are defined using the `.attribute` class method, and can be accessed and enumerated using the `.attributes` class method on the struct class or via the `::Attributes` constant. Internally, each attribute is represented by a `Stannum::Attribute` instance, which stores the attribute's `:name`, `:type`, and `:attributes`.
784
+ A entity's attributes are defined using the `.attribute` class method, and can be accessed and enumerated using the `.attributes` class method on the entity class or via the `::Attributes` constant. Internally, each attribute is represented by a `Stannum::Attribute` instance, which stores the attribute's `:name`, `:type`, and `:attributes`.
781
785
 
782
786
  ```ruby
783
787
  Gadget::Attributes
@@ -796,11 +800,11 @@ Gadget.attributes[:quantity].options
796
800
 
797
801
  ##### Default Values
798
802
 
799
- Structs can define default values for attributes by passing a `:default` value to the `.attribute` call.
803
+ Entities can define default values for attributes by passing a `:default` value to the `.attribute` call.
800
804
 
801
805
  ```ruby
802
806
  class LightsCounter
803
- include Stannum::Struct
807
+ include Stannum::Entity
804
808
 
805
809
  attribute :count, Integer, default: 4
806
810
  end
@@ -811,11 +815,11 @@ LightsCounter.new.count
811
815
 
812
816
  ##### Optional Attributes
813
817
 
814
- Struct classes can also mark attributes as `optional`. When a struct is validated (see [Validation](#structs-validation), below), optional attributes will pass with a value of `nil`.
818
+ Entity classes can also mark attributes as `optional`. When an entity is validated (see [Validation](#entities-validation), below), optional attributes will pass with a value of `nil`.
815
819
 
816
820
  ```ruby
817
821
  class WhereWeAreGoing
818
- include Stannum::Struct
822
+ include Stannum::Entity
819
823
 
820
824
  attribute :roads, Object, optional: true
821
825
  end
@@ -823,11 +827,11 @@ end
823
827
 
824
828
  `Stannum` supports both `:optional` and `:required` as keys. Passing either `optional: true` or `required: false` will mark the attribute as optional. Attributes are required by default.
825
829
 
826
- <a id="structs-validation"></a>
830
+ <a id="entities-validation"></a>
827
831
 
828
832
  #### Validation
829
833
 
830
- Each `Stannum::Struct` automatically generates a contract that can be used to validate instances of the struct class. The contract can be accessed using the `.contract` class method or via the `::Contract` constant.
834
+ Each `Stannum::Entity` automatically generates a contract that can be used to validate instances of the entity class. The contract can be accessed using the `.contract` class method or via the `::Contract` constant.
831
835
 
832
836
  ```ruby
833
837
  class Gadget
@@ -1091,6 +1095,66 @@ constraint.matches?(:a_symbol)
1091
1095
  #=> true
1092
1096
  ```
1093
1097
 
1098
+ #### Property Constraints
1099
+
1100
+ Property constraints match against the properties of the object.
1101
+
1102
+ **Do Not Match Property Constraint**
1103
+
1104
+ Matches if none of the values of the given properties are equal to the value of the expected property.
1105
+
1106
+ ```ruby
1107
+ UpdatePassword = Struct.new(:old_password, :new_password)
1108
+ constraint = Stannum::Constraints::Properties::DoNotMatchProperty.new(
1109
+ :old_password,
1110
+ :new_password
1111
+ )
1112
+
1113
+ params = UpdatePassword.new('tronlives', 'ifightfortheusers')
1114
+ constraint.matches?(params)
1115
+ #=> true
1116
+
1117
+ params = UpdatePassword.new('tronlives', 'tronlives')
1118
+ constraint.matches?(params)
1119
+ #=> false
1120
+ constraint.errors_for(params)
1121
+ #=> [
1122
+ {
1123
+ path: [:confirmation],
1124
+ type: 'stannum.constraints.is_equal_to',
1125
+ data: { expected: '[FILTERED]', actual: '[FILTERED]' }
1126
+ }
1127
+ ]
1128
+ ```
1129
+
1130
+ **Match Property Constraint**
1131
+
1132
+ Matches if all the values of the given properties are equal to the value of the expected property.
1133
+
1134
+ ```ruby
1135
+ ConfirmPassword = Struct.new(:password, :confirmation)
1136
+ constraint = Stannum::Constraints::Properties::MatchProperty.new(
1137
+ :password,
1138
+ :confirmation
1139
+ )
1140
+
1141
+ params = ConfirmPassword.new('tronlives', 'ifightfortheusers')
1142
+ constraint.matches?(params)
1143
+ #=> false
1144
+ constraint.errors_for(params)
1145
+ #=> [
1146
+ {
1147
+ path: [:confirmation],
1148
+ type: 'stannum.constraints.is_not_equal_to',
1149
+ data: { expected: '[FILTERED]', actual: '[FILTERED]' }
1150
+ }
1151
+ ]
1152
+
1153
+ params = ConfirmPassword.new('tronlives', 'tronlives')
1154
+ constraint.matches?(params)
1155
+ #=> true
1156
+ ```
1157
+
1094
1158
  #### Type Constraints
1095
1159
 
1096
1160
  Stannum also defines a set of built-in type constraints. Unless otherwise noted, these are identical to a [Type Constraint](#builtin-constraints-type) with the given Class.
@@ -6,12 +6,17 @@ require 'stannum/support/coercion'
6
6
  module Stannum::Constraints::Hashes
7
7
  # Constraint for validating the keys of a hash-like object.
8
8
  #
9
+ # When using this constraint, the keys must be strings or symbols, and the
10
+ # hash keys must be of the same type. A constraint configured with string keys
11
+ # will not match a hash with symbol keys, and vice versa.
12
+ #
9
13
  # @example
10
- # keys = %[fuel mass size]
14
+ # keys = %i[fuel mass size]
11
15
  # constraint = Stannum::Constraints::Hashes::ExpectedKeys.new(keys)
12
16
  #
13
17
  # constraint.matches?({}) #=> true
14
18
  # constraint.matches?({ fuel: 'Monopropellant' }) #=> true
19
+ # constraint.matches?({ 'fuel' => 'Monopropellant' }) #=> false
15
20
  # constraint.matches?({ electric: true, fuel: 'Xenon' }) #=> false
16
21
  # constraint.matches?({ fuel: 'LF/O', mass: '1 ton', size: 'Medium' })
17
22
  # #=> true
@@ -68,7 +73,7 @@ module Stannum::Constraints::Hashes
68
73
  errors
69
74
  end
70
75
 
71
- # @return [Array] the expected keys.
76
+ # @return [Set] the expected keys.
72
77
  def expected_keys
73
78
  keys = options[:expected_keys]
74
79
 
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stannum/constraints/hashes'
4
+ require 'stannum/constraints/hashes/extra_keys'
5
+
6
+ module Stannum::Constraints::Hashes
7
+ # Constraint for validating the keys of an indifferent hash-like object.
8
+ #
9
+ # When using this constraint, the keys must be strings or symbols, but it does
10
+ # not matter which - a constraint configured with string keys will match a
11
+ # hash with symbol keys, and vice versa.
12
+ #
13
+ # @example
14
+ # keys = %i[fuel mass size]
15
+ # constraint = Stannum::Constraints::Hashes::ExpectedKeys.new(keys)
16
+ #
17
+ # constraint.matches?({}) #=> true
18
+ # constraint.matches?({ fuel: 'Monopropellant' }) #=> true
19
+ # constraint.matches?({ 'fuel' => 'Monopropellant' }) #=> true
20
+ # constraint.matches?({ electric: true, fuel: 'Xenon' }) #=> false
21
+ # constraint.matches?({ fuel: 'LF/O', mass: '1 ton', size: 'Medium' })
22
+ # #=> true
23
+ # constraint.matches?(
24
+ # { fuel: 'LF', mass: '2 tons', nuclear: true, size: 'Medium' }
25
+ # )
26
+ # #=> false
27
+ class IndifferentExtraKeys < Stannum::Constraints::Hashes::ExtraKeys
28
+ # @return [Set] the expected keys.
29
+ def expected_keys
30
+ keys = options[:expected_keys]
31
+
32
+ return indifferent_keys_for(keys) unless keys.is_a?(Proc)
33
+
34
+ indifferent_keys_for(keys.call)
35
+ end
36
+
37
+ private
38
+
39
+ def indifferent_keys_for(keys)
40
+ Set.new(
41
+ keys.reduce([]) do |ary, key|
42
+ ary << key.to_s << key.intern
43
+ end
44
+ )
45
+ end
46
+ end
47
+ end
@@ -5,7 +5,11 @@ require 'stannum/constraints'
5
5
  module Stannum::Constraints
6
6
  # Namespace for Hash-specific constraints.
7
7
  module Hashes
8
- autoload :ExtraKeys, 'stannum/constraints/hashes/extra_keys'
9
- autoload :IndifferentKey, 'stannum/constraints/hashes/indifferent_key'
8
+ autoload :ExtraKeys,
9
+ 'stannum/constraints/hashes/extra_keys'
10
+ autoload :IndifferentExtraKeys,
11
+ 'stannum/constraints/hashes/indifferent_extra_keys'
12
+ autoload :IndifferentKey,
13
+ 'stannum/constraints/hashes/indifferent_key'
10
14
  end
11
15
  end
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sleeping_king_studios/tools/toolbelt'
4
+
5
+ require 'stannum/constraints/properties'
6
+
7
+ module Stannum::Constraints::Properties
8
+ # Abstract base class for property constraints.
9
+ class Base < Stannum::Constraints::Base
10
+ # Default parameter names to filter out of errors.
11
+ FILTERED_PARAMETERS = %i[
12
+ passw
13
+ secret
14
+ token
15
+ _key
16
+ crypt
17
+ salt
18
+ certificate
19
+ otp
20
+ ssn
21
+ ].freeze
22
+
23
+ # @param property_names [Array<String, Symbol>] the name or names of the
24
+ # properties to match.
25
+ # @param options [Hash<Symbol, Object>] configuration options for the
26
+ # constraint. Defaults to an empty Hash.
27
+ #
28
+ # @option options allow_empty [true, false] if true, will match against an
29
+ # object with empty property values, such as an empty string.
30
+ # @option options allow_nil [true, false] if true, will match against an
31
+ # object with nil property values.
32
+ def initialize(*property_names, **options)
33
+ @property_names = property_names
34
+
35
+ validate_property_names
36
+
37
+ super(
38
+ allow_empty: !!options[:allow_empty],
39
+ allow_nil: !!options[:allow_nil],
40
+ property_names: property_names,
41
+ **options
42
+ )
43
+ end
44
+
45
+ # @return [Array<String, Symbol>] the name or names of the properties to
46
+ # match.
47
+ attr_reader :property_names
48
+
49
+ # @return [true, false] if true, will match against an object with empty
50
+ # property values, such as an empty string.
51
+ def allow_empty?
52
+ options[:allow_empty]
53
+ end
54
+
55
+ # @return [true, false] if true, will match against an object with nil
56
+ # property values.
57
+ def allow_nil?
58
+ options[:allow_nil]
59
+ end
60
+
61
+ private
62
+
63
+ def can_match_properties?(actual)
64
+ actual.respond_to?(:[])
65
+ end
66
+
67
+ def each_property(actual)
68
+ return to_enum(__method__, actual) unless block_given?
69
+
70
+ property_names.each do |property_name|
71
+ yield property_name, actual[property_name]
72
+ end
73
+ end
74
+
75
+ def empty?(value)
76
+ value.respond_to?(:empty?) && value.empty?
77
+ end
78
+
79
+ def filter_parameters?
80
+ return @filter_parameters unless @filter_parameters.nil?
81
+
82
+ filters = filtered_parameters.map { |param| Regexp.new(param.to_s) }
83
+
84
+ @filter_parameters =
85
+ property_names.any? do |property_name|
86
+ filters.any? { |filter| filter.match?(property_name.to_s) }
87
+ end
88
+ end
89
+
90
+ def filtered_parameters
91
+ return Rails.configuration.filter_parameters if defined?(Rails)
92
+
93
+ FILTERED_PARAMETERS
94
+ end
95
+
96
+ def invalid_object_errors(errors)
97
+ errors.add(
98
+ Stannum::Constraints::Signature::TYPE,
99
+ methods: %i[[]],
100
+ missing: %i[[]]
101
+ )
102
+ end
103
+
104
+ def skip_property?(value)
105
+ (allow_empty? && empty?(value)) || (allow_nil? && value.nil?)
106
+ end
107
+
108
+ def tools
109
+ SleepingKingStudios::Tools::Toolbelt.instance
110
+ end
111
+
112
+ def validate_property_names
113
+ if property_names.empty?
114
+ raise ArgumentError, "property names can't be empty"
115
+ end
116
+
117
+ property_names.each.with_index do |property_name, index|
118
+ tools
119
+ .assertions
120
+ .validate_name(property_name, as: "property name at #{index}")
121
+ end
122
+ end
123
+ end
124
+ end
@@ -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 none of the property values equal the expected value, the constraint will
10
+ # match the object; otherwise, if there are any matching values, the
11
+ # constraint will not match.
12
+ #
13
+ # @example Using an Properties::Match constraint
14
+ # UpdatePassword = Struct.new(:old_password, :new_password)
15
+ # constraint = Stannum::Constraints::Properties::DoNotMatchProperty.new(
16
+ # :old_password,
17
+ # :new_password
18
+ # )
19
+ #
20
+ # params = UpdatePassword.new('tronlives', 'ifightfortheusers')
21
+ # constraint.matches?(params)
22
+ # #=> true
23
+ #
24
+ # params = UpdatePassword.new('tronlives', 'tronlives')
25
+ # constraint.matches?(params)
26
+ # #=> false
27
+ # constraint.errors_for(params)
28
+ # #=> [
29
+ # {
30
+ # path: [:confirmation],
31
+ # type: 'stannum.constraints.is_equal_to',
32
+ # data: { expected: '[FILTERED]', actual: '[FILTERED]' }
33
+ # }
34
+ # ]
35
+ class DoNotMatchProperty < Stannum::Constraints::Properties::Matching
36
+ # The :type of the error generated for a matching object.
37
+ NEGATED_TYPE = Stannum::Constraints::Equality::TYPE
38
+
39
+ # The :type of the error generated for a non-matching object.
40
+ TYPE = Stannum::Constraints::Equality::NEGATED_TYPE
41
+
42
+ # @return [true, false] true if the property values match the reference
43
+ # property value; otherwise false.
44
+ def does_not_match?(actual)
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_non_matching_property(
52
+ actual: actual,
53
+ expected: 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)
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_matching_property(actual: actual, expected: expected)
67
+
68
+ return generic_errors(errors) if matching.count.zero?
69
+
70
+ matching.each do |property_name, _|
71
+ errors[property_name].add(type, message: message)
72
+ end
73
+
74
+ errors
75
+ end
76
+
77
+ # @return [true, false] false if any of the property values match the
78
+ # reference property value; otherwise true.
79
+ def matches?(actual)
80
+ return false unless can_match_properties?(actual)
81
+
82
+ expected = expected_value(actual)
83
+
84
+ return true if skip_property?(expected)
85
+
86
+ each_matching_property(actual: actual, expected: expected).none?
87
+ end
88
+ alias match? matches?
89
+
90
+ # (see Stannum::Constraints::Base#negated_errors_for)
91
+ def negated_errors_for(actual, errors: nil) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
92
+ errors ||= Stannum::Errors.new
93
+
94
+ return invalid_object_errors(errors) unless can_match_properties?(actual)
95
+
96
+ expected = expected_value(actual)
97
+ matching = each_non_matching_property(
98
+ actual: actual,
99
+ expected: expected,
100
+ include_all: true
101
+ )
102
+
103
+ return generic_errors(errors) if matching.count.zero?
104
+
105
+ matching.each do |property_name, value|
106
+ errors[property_name].add(
107
+ negated_type,
108
+ message: negated_message,
109
+ expected: filter_parameters? ? '[FILTERED]' : expected_value(actual),
110
+ actual: filter_parameters? ? '[FILTERED]' : value
111
+ )
112
+ end
113
+
114
+ errors
115
+ end
116
+ end
117
+ end