stannum 0.3.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 +25 -1
- data/README.md +129 -1263
- 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 +7 -12
- data/lib/stannum/constraints/identity.rb +1 -1
- data/lib/stannum/constraints/properties/base.rb +1 -1
- data/lib/stannum/constraints/properties/do_not_match_property.rb +11 -11
- data/lib/stannum/constraints/properties/match_property.rb +11 -11
- data/lib/stannum/constraints/properties/matching.rb +7 -7
- data/lib/stannum/constraints/signature.rb +2 -2
- data/lib/stannum/constraints/tuples/extra_items.rb +6 -6
- data/lib/stannum/constraints/type.rb +3 -3
- 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 +2 -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 +2 -2
- data/lib/stannum/contracts/hash_contract.rb +3 -9
- data/lib/stannum/contracts/indifferent_hash_contract.rb +2 -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 +5 -5
- data/lib/stannum/entities/associations.rb +451 -0
- data/lib/stannum/entities/attributes.rb +116 -18
- data/lib/stannum/entities/constraints.rb +3 -2
- data/lib/stannum/entities/primary_key.rb +148 -0
- data/lib/stannum/entities/properties.rb +30 -8
- data/lib/stannum/entities.rb +5 -2
- data/lib/stannum/entity.rb +4 -0
- data/lib/stannum/errors.rb +9 -13
- 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 +1 -1
- data/lib/stannum/rspec/validate_parameter.rb +2 -2
- data/lib/stannum/rspec/validate_parameter_matcher.rb +15 -13
- data/lib/stannum/schema.rb +62 -62
- data/lib/stannum/support/optional.rb +1 -1
- data/lib/stannum/version.rb +4 -4
- data/lib/stannum.rb +3 -0
- metadata +14 -79
data/lib/stannum/attribute.rb
CHANGED
@@ -4,16 +4,71 @@ require 'stannum'
|
|
4
4
|
require 'stannum/support/optional'
|
5
5
|
|
6
6
|
module Stannum
|
7
|
-
# Data object representing an attribute on
|
7
|
+
# Data object representing an attribute on an entity.
|
8
8
|
class Attribute
|
9
9
|
include Stannum::Support::Optional
|
10
10
|
|
11
|
+
# Builder class for defining attribute methods on an entity.
|
12
|
+
class Builder
|
13
|
+
# @param schema [Stannum::Schema] the attributes schema on which to define
|
14
|
+
# methods.
|
15
|
+
def initialize(schema)
|
16
|
+
@schema = schema
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Stannum::Schema] the attributes schema on which to define
|
20
|
+
# methods.
|
21
|
+
attr_reader :schema
|
22
|
+
|
23
|
+
# Defines the reader and writer methods for the attribute.
|
24
|
+
#
|
25
|
+
# @param attribute [Stannum::Attribute]
|
26
|
+
def call(attribute)
|
27
|
+
define_reader(attribute)
|
28
|
+
define_writer(attribute)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def define_reader(attribute)
|
34
|
+
schema.define_method(attribute.reader_name) do
|
35
|
+
read_attribute(attribute.name, safe: false)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def define_writer(attribute) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
40
|
+
assoc_name = attribute.association_name
|
41
|
+
|
42
|
+
schema.define_method(attribute.writer_name) do |value|
|
43
|
+
previous_value = read_attribute(attribute.name, safe: false)
|
44
|
+
|
45
|
+
return if previous_value == value
|
46
|
+
|
47
|
+
if attribute.foreign_key? && !previous_value.nil?
|
48
|
+
self
|
49
|
+
.class
|
50
|
+
.associations[assoc_name]
|
51
|
+
.remove_value(self, previous_value)
|
52
|
+
end
|
53
|
+
|
54
|
+
value = attribute.default_value_for(self) if value.nil?
|
55
|
+
|
56
|
+
write_attribute(attribute.name, value, safe: false)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
11
61
|
# @param name [String, Symbol] The name of the attribute. Converted to a
|
12
62
|
# String.
|
13
63
|
# @param options [Hash, nil] Options for the attribute. Converted to a Hash
|
14
64
|
# with Symbol keys. Defaults to an empty Hash.
|
15
65
|
# @param type [Class, Module, String] The type of the attribute. Can be a
|
16
66
|
# Class, a Module, or the name of a class or module.
|
67
|
+
#
|
68
|
+
# @option options [Object] :default The default value for the attribute.
|
69
|
+
# Defaults to nil.
|
70
|
+
# @option options [Boolean] :primary_key true if the attribute represents
|
71
|
+
# the primary key for the entity; otherwise false. Defaults to false.
|
17
72
|
def initialize(name:, options:, type:)
|
18
73
|
validate_name(name)
|
19
74
|
validate_options(options)
|
@@ -35,6 +90,12 @@ module Stannum
|
|
35
90
|
# @return [String] the name of the attribute type Class or Module.
|
36
91
|
attr_reader :type
|
37
92
|
|
93
|
+
# @return [String] the name of the association if the attribute is a foreign
|
94
|
+
# key; otherwise false.
|
95
|
+
def association_name
|
96
|
+
@options[:association_name]
|
97
|
+
end
|
98
|
+
|
38
99
|
# @return [Object] the default value for the attribute, if any.
|
39
100
|
def default
|
40
101
|
@options[:default]
|
@@ -46,6 +107,29 @@ module Stannum
|
|
46
107
|
!@options[:default].nil?
|
47
108
|
end
|
48
109
|
|
110
|
+
# @param context [Object] the context object used to determinet the default
|
111
|
+
# value.
|
112
|
+
#
|
113
|
+
# @return [Object] the value of the default attribute for the given context
|
114
|
+
# object, if any.
|
115
|
+
def default_value_for(context)
|
116
|
+
return default unless default.is_a?(Proc)
|
117
|
+
|
118
|
+
default.arity.zero? ? default.call : default.call(context)
|
119
|
+
end
|
120
|
+
|
121
|
+
# @return [Boolean] true if the attribute represents the foreign key for an
|
122
|
+
# association; otherwise false.
|
123
|
+
def foreign_key?
|
124
|
+
!!@options[:foreign_key]
|
125
|
+
end
|
126
|
+
|
127
|
+
# @return [Boolean] true if the attribute represents the primary key for the
|
128
|
+
# entity; otherwise false.
|
129
|
+
def primary_key?
|
130
|
+
!!@options[:primary_key]
|
131
|
+
end
|
132
|
+
|
49
133
|
# @return [Symbol] the name of the reader method for the attribute.
|
50
134
|
def reader_name
|
51
135
|
@reader_name ||= name.intern
|
@@ -82,13 +166,7 @@ module Stannum
|
|
82
166
|
end
|
83
167
|
|
84
168
|
def validate_name(name)
|
85
|
-
|
86
|
-
|
87
|
-
unless name.is_a?(String) || name.is_a?(Symbol)
|
88
|
-
raise ArgumentError, 'name must be a String or Symbol'
|
89
|
-
end
|
90
|
-
|
91
|
-
raise ArgumentError, "name can't be blank" if name.empty?
|
169
|
+
tools.assertions.validate_name(name, as: 'name')
|
92
170
|
end
|
93
171
|
|
94
172
|
def validate_options(options)
|
@@ -55,9 +55,7 @@ module Stannum::Constraints
|
|
55
55
|
#
|
56
56
|
# @return [Stannum::Constraints::Base] the cloned constraint.
|
57
57
|
def clone(freeze: nil)
|
58
|
-
|
59
|
-
|
60
|
-
super(freeze: freeze).copy_properties(self)
|
58
|
+
super.copy_properties(self)
|
61
59
|
end
|
62
60
|
|
63
61
|
# Checks that the given object does not match the constraint.
|
@@ -80,7 +78,7 @@ module Stannum::Constraints
|
|
80
78
|
# or behavior, otherwise true.
|
81
79
|
#
|
82
80
|
# @see #matches?
|
83
|
-
def does_not_match?(actual)
|
81
|
+
def does_not_match?(actual) # rubocop:disable Naming/PredicatePrefix
|
84
82
|
!matches?(actual)
|
85
83
|
end
|
86
84
|
|
@@ -118,7 +116,7 @@ module Stannum::Constraints
|
|
118
116
|
# @see #matches?
|
119
117
|
# @see #negated_errors_for
|
120
118
|
def errors_for(actual, errors: nil) # rubocop:disable Lint/UnusedMethodArgument
|
121
|
-
(errors || Stannum::Errors.new).add(type, message:
|
119
|
+
(errors || Stannum::Errors.new).add(type, message:)
|
122
120
|
end
|
123
121
|
|
124
122
|
# Checks the given object against the constraint and returns errors, if any.
|
@@ -26,7 +26,7 @@ module Stannum::Constraints
|
|
26
26
|
def initialize(first, *rest, **options)
|
27
27
|
expected_values = rest.unshift(first)
|
28
28
|
|
29
|
-
super(expected_values
|
29
|
+
super(expected_values:, **options)
|
30
30
|
|
31
31
|
@matching_values = Set.new(expected_values)
|
32
32
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'stannum/constraints'
|
4
|
+
|
5
|
+
module Stannum::Constraints
|
6
|
+
# A Format constraint asserts the value is a string matching the given format.
|
7
|
+
#
|
8
|
+
# @example Using a Format constraint with a String format.
|
9
|
+
# format = 'Greetings'
|
10
|
+
# constraint = Stannum::Constraints::Format.new(format)
|
11
|
+
#
|
12
|
+
# constraint.matches?(nil) #=> false
|
13
|
+
# constraint.matches?('Hello, world') #=> false
|
14
|
+
# constraint.matches?('Greetings, programs!') #=> true
|
15
|
+
#
|
16
|
+
# @example Using a Format constraint with a Regex format.
|
17
|
+
# format = /\AGreetings/
|
18
|
+
# constraint = Stannum::Constraints::Format.new(format)
|
19
|
+
#
|
20
|
+
# constraint.matches?(nil) #=> false
|
21
|
+
# constraint.matches?('Hello, world') #=> false
|
22
|
+
# constraint.matches?('Greetings, programs!') #=> true
|
23
|
+
class Format < Stannum::Constraints::Base
|
24
|
+
# The :type of the error generated for a matching object.
|
25
|
+
NEGATED_TYPE = 'stannum.constraints.matches_format'
|
26
|
+
|
27
|
+
# The :type of the error generated for a non-matching object.
|
28
|
+
TYPE = 'stannum.constraints.does_not_match_format'
|
29
|
+
|
30
|
+
# @param expected_format [Regex, String] The expected object.
|
31
|
+
# @param options [Hash<Symbol, Object>] Configuration options for the
|
32
|
+
# constraint. Defaults to an empty Hash.
|
33
|
+
def initialize(expected_format, **options)
|
34
|
+
@expected_format = expected_format
|
35
|
+
|
36
|
+
super(expected_format:, **options)
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [Regex, String] the expected format.
|
40
|
+
attr_reader :expected_format
|
41
|
+
|
42
|
+
# (see Stannum::Constraints::Base#errors_for)
|
43
|
+
def errors_for(actual, errors: nil)
|
44
|
+
return super if type_constraint.matches?(actual)
|
45
|
+
|
46
|
+
type_constraint.errors_for(actual, errors:)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Checks that the object is a string with the expected format.
|
50
|
+
#
|
51
|
+
# @return [true, false] true if the object is a string with the expected
|
52
|
+
# format, otherwise false.
|
53
|
+
#
|
54
|
+
# @see Stannum::Constraint#matches?
|
55
|
+
def matches?(actual)
|
56
|
+
return false unless type_constraint.matches?(actual)
|
57
|
+
|
58
|
+
if expected_format.is_a?(String)
|
59
|
+
actual.include?(expected_format)
|
60
|
+
else
|
61
|
+
actual.match?(expected_format)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
alias match? matches?
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def type_constraint
|
69
|
+
@type_constraint ||= Stannum::Constraints::Type.new(String)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -38,19 +38,14 @@ module Stannum::Constraints::Hashes
|
|
38
38
|
def initialize(expected_keys, **options)
|
39
39
|
validate_expected_keys(expected_keys)
|
40
40
|
|
41
|
-
expected_keys =
|
42
|
-
|
43
|
-
|
44
|
-
else
|
45
|
-
expected_keys
|
46
|
-
end
|
47
|
-
|
48
|
-
super(expected_keys: expected_keys, **options)
|
41
|
+
expected_keys = Set.new(expected_keys) if expected_keys.is_a?(Array)
|
42
|
+
|
43
|
+
super(expected_keys:, **options)
|
49
44
|
end
|
50
45
|
|
51
46
|
# @return [true, false] true if the object responds to #[] and #keys and the
|
52
47
|
# object has at least one key that is not in expected_keys.
|
53
|
-
def does_not_match?(actual)
|
48
|
+
def does_not_match?(actual) # rubocop:disable Naming/PredicatePrefix
|
54
49
|
return false unless hash?(actual)
|
55
50
|
|
56
51
|
!(Set.new(actual.keys) <= expected_keys) # rubocop:disable Style/InverseMethods
|
@@ -61,13 +56,13 @@ module Stannum::Constraints::Hashes
|
|
61
56
|
errors ||= Stannum::Errors.new
|
62
57
|
|
63
58
|
unless actual.respond_to?(:keys)
|
64
|
-
return add_invalid_hash_error(actual
|
59
|
+
return add_invalid_hash_error(actual:, errors:)
|
65
60
|
end
|
66
61
|
|
67
62
|
each_extra_key(actual) do |key, value|
|
68
63
|
key = Stannum::Support::Coercion.error_key(key)
|
69
64
|
|
70
|
-
errors[key].add(type, value:
|
65
|
+
errors[key].add(type, value:)
|
71
66
|
end
|
72
67
|
|
73
68
|
errors
|
@@ -96,7 +91,7 @@ module Stannum::Constraints::Hashes
|
|
96
91
|
def add_invalid_hash_error(actual:, errors:)
|
97
92
|
Stannum::Constraints::Signature
|
98
93
|
.new(:keys)
|
99
|
-
.errors_for(actual, errors:
|
94
|
+
.errors_for(actual, errors:)
|
100
95
|
end
|
101
96
|
|
102
97
|
def each_extra_key(actual)
|
@@ -41,7 +41,7 @@ module Stannum::Constraints::Properties
|
|
41
41
|
|
42
42
|
# @return [true, false] true if the property values match the reference
|
43
43
|
# property value; otherwise false.
|
44
|
-
def does_not_match?(actual)
|
44
|
+
def does_not_match?(actual) # rubocop:disable Naming/PredicatePrefix
|
45
45
|
return false unless can_match_properties?(actual)
|
46
46
|
|
47
47
|
expected = expected_value(actual)
|
@@ -49,8 +49,8 @@ module Stannum::Constraints::Properties
|
|
49
49
|
return false if skip_property?(expected)
|
50
50
|
|
51
51
|
each_non_matching_property(
|
52
|
-
actual
|
53
|
-
expected
|
52
|
+
actual:,
|
53
|
+
expected:,
|
54
54
|
include_all: true
|
55
55
|
)
|
56
56
|
.none?
|
@@ -63,12 +63,12 @@ module Stannum::Constraints::Properties
|
|
63
63
|
return invalid_object_errors(errors) unless can_match_properties?(actual)
|
64
64
|
|
65
65
|
expected = expected_value(actual)
|
66
|
-
matching = each_matching_property(actual
|
66
|
+
matching = each_matching_property(actual:, expected:)
|
67
67
|
|
68
|
-
return generic_errors(errors) if matching.
|
68
|
+
return generic_errors(errors) if matching.none?
|
69
69
|
|
70
|
-
matching.each do |property_name, _|
|
71
|
-
errors[property_name].add(type, message:
|
70
|
+
matching.each do |property_name, _| # rubocop:disable Style/HashEachMethods
|
71
|
+
errors[property_name].add(type, message:)
|
72
72
|
end
|
73
73
|
|
74
74
|
errors
|
@@ -83,7 +83,7 @@ module Stannum::Constraints::Properties
|
|
83
83
|
|
84
84
|
return true if skip_property?(expected)
|
85
85
|
|
86
|
-
each_matching_property(actual
|
86
|
+
each_matching_property(actual:, expected:).none?
|
87
87
|
end
|
88
88
|
alias match? matches?
|
89
89
|
|
@@ -95,12 +95,12 @@ module Stannum::Constraints::Properties
|
|
95
95
|
|
96
96
|
expected = expected_value(actual)
|
97
97
|
matching = each_non_matching_property(
|
98
|
-
actual
|
99
|
-
expected
|
98
|
+
actual:,
|
99
|
+
expected:,
|
100
100
|
include_all: true
|
101
101
|
)
|
102
102
|
|
103
|
-
return generic_errors(errors) if matching.
|
103
|
+
return generic_errors(errors) if matching.none?
|
104
104
|
|
105
105
|
matching.each do |property_name, value|
|
106
106
|
errors[property_name].add(
|
@@ -41,7 +41,7 @@ module Stannum::Constraints::Properties
|
|
41
41
|
|
42
42
|
# @return [true, false] false if any of the property values match the
|
43
43
|
# reference property value; otherwise true.
|
44
|
-
def does_not_match?(actual)
|
44
|
+
def does_not_match?(actual) # rubocop:disable Naming/PredicatePrefix
|
45
45
|
return false unless can_match_properties?(actual)
|
46
46
|
|
47
47
|
expected = expected_value(actual)
|
@@ -49,8 +49,8 @@ module Stannum::Constraints::Properties
|
|
49
49
|
return false if skip_property?(expected)
|
50
50
|
|
51
51
|
each_matching_property(
|
52
|
-
actual
|
53
|
-
expected
|
52
|
+
actual:,
|
53
|
+
expected:,
|
54
54
|
include_all: true
|
55
55
|
)
|
56
56
|
.none?
|
@@ -63,14 +63,14 @@ module Stannum::Constraints::Properties
|
|
63
63
|
return invalid_object_errors(errors) unless can_match_properties?(actual)
|
64
64
|
|
65
65
|
expected = expected_value(actual)
|
66
|
-
matching = each_non_matching_property(actual
|
66
|
+
matching = each_non_matching_property(actual:, expected:)
|
67
67
|
|
68
|
-
return generic_errors(errors) if matching.
|
68
|
+
return generic_errors(errors) if matching.none?
|
69
69
|
|
70
70
|
matching.each do |property_name, value|
|
71
71
|
errors[property_name].add(
|
72
72
|
type,
|
73
|
-
message
|
73
|
+
message:,
|
74
74
|
expected: filter_parameters? ? '[FILTERED]' : expected_value(actual),
|
75
75
|
actual: filter_parameters? ? '[FILTERED]' : value
|
76
76
|
)
|
@@ -88,7 +88,7 @@ module Stannum::Constraints::Properties
|
|
88
88
|
|
89
89
|
return true if skip_property?(expected)
|
90
90
|
|
91
|
-
each_non_matching_property(actual
|
91
|
+
each_non_matching_property(actual:, expected:).none?
|
92
92
|
end
|
93
93
|
alias match? matches?
|
94
94
|
|
@@ -100,14 +100,14 @@ module Stannum::Constraints::Properties
|
|
100
100
|
|
101
101
|
expected = expected_value(actual)
|
102
102
|
matching = each_matching_property(
|
103
|
-
actual
|
104
|
-
expected
|
103
|
+
actual:,
|
104
|
+
expected:,
|
105
105
|
include_all: true
|
106
106
|
)
|
107
107
|
|
108
|
-
return generic_errors(errors) if matching.
|
108
|
+
return generic_errors(errors) if matching.none?
|
109
109
|
|
110
|
-
matching.each do |property_name, _|
|
110
|
+
matching.each do |property_name, _| # rubocop:disable Style/HashEachMethods
|
111
111
|
errors[property_name].add(negated_type, message: negated_message)
|
112
112
|
end
|
113
113
|
|
@@ -22,7 +22,7 @@ module Stannum::Constraints::Properties
|
|
22
22
|
|
23
23
|
validate_reference_name
|
24
24
|
|
25
|
-
super(*property_names, reference_name
|
25
|
+
super(*property_names, reference_name:, **options)
|
26
26
|
end
|
27
27
|
|
28
28
|
# @return [String, Symbol] the name of the reference property to compare to.
|
@@ -39,9 +39,9 @@ module Stannum::Constraints::Properties
|
|
39
39
|
unless block_given?
|
40
40
|
return to_enum(
|
41
41
|
__method__,
|
42
|
-
actual
|
43
|
-
expected
|
44
|
-
include_all:
|
42
|
+
actual:,
|
43
|
+
expected:,
|
44
|
+
include_all:
|
45
45
|
)
|
46
46
|
end
|
47
47
|
|
@@ -65,9 +65,9 @@ module Stannum::Constraints::Properties
|
|
65
65
|
unless block_given?
|
66
66
|
return to_enum(
|
67
67
|
__method__,
|
68
|
-
actual
|
69
|
-
expected
|
70
|
-
include_all:
|
68
|
+
actual:,
|
69
|
+
expected:,
|
70
|
+
include_all:
|
71
71
|
)
|
72
72
|
end
|
73
73
|
|
@@ -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..].each.with_index(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
|
)
|
@@ -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,6 +12,7 @@ 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'
|
@@ -24,5 +25,6 @@ module Stannum
|
|
24
25
|
autoload :Type, 'stannum/constraints/type'
|
25
26
|
autoload :Types, 'stannum/constraints/types'
|
26
27
|
autoload :Union, 'stannum/constraints/union'
|
28
|
+
autoload :Uuid, 'stannum/constraints/uuid'
|
27
29
|
end
|
28
30
|
end
|