stannum 0.1.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 (79) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +21 -0
  3. data/CODE_OF_CONDUCT.md +132 -0
  4. data/DEVELOPMENT.md +105 -0
  5. data/LICENSE +22 -0
  6. data/README.md +1327 -0
  7. data/config/locales/en.rb +47 -0
  8. data/lib/stannum/attribute.rb +115 -0
  9. data/lib/stannum/constraint.rb +65 -0
  10. data/lib/stannum/constraints/absence.rb +42 -0
  11. data/lib/stannum/constraints/anything.rb +28 -0
  12. data/lib/stannum/constraints/base.rb +285 -0
  13. data/lib/stannum/constraints/boolean.rb +33 -0
  14. data/lib/stannum/constraints/delegator.rb +71 -0
  15. data/lib/stannum/constraints/enum.rb +64 -0
  16. data/lib/stannum/constraints/equality.rb +47 -0
  17. data/lib/stannum/constraints/hashes/extra_keys.rb +126 -0
  18. data/lib/stannum/constraints/hashes/indifferent_key.rb +74 -0
  19. data/lib/stannum/constraints/hashes.rb +11 -0
  20. data/lib/stannum/constraints/identity.rb +46 -0
  21. data/lib/stannum/constraints/nothing.rb +28 -0
  22. data/lib/stannum/constraints/presence.rb +42 -0
  23. data/lib/stannum/constraints/signature.rb +92 -0
  24. data/lib/stannum/constraints/signatures/map.rb +17 -0
  25. data/lib/stannum/constraints/signatures/tuple.rb +17 -0
  26. data/lib/stannum/constraints/signatures.rb +11 -0
  27. data/lib/stannum/constraints/tuples/extra_items.rb +84 -0
  28. data/lib/stannum/constraints/tuples.rb +10 -0
  29. data/lib/stannum/constraints/type.rb +113 -0
  30. data/lib/stannum/constraints/types/array_type.rb +148 -0
  31. data/lib/stannum/constraints/types/big_decimal_type.rb +16 -0
  32. data/lib/stannum/constraints/types/date_time_type.rb +16 -0
  33. data/lib/stannum/constraints/types/date_type.rb +16 -0
  34. data/lib/stannum/constraints/types/float_type.rb +14 -0
  35. data/lib/stannum/constraints/types/hash_type.rb +205 -0
  36. data/lib/stannum/constraints/types/hash_with_indifferent_keys.rb +21 -0
  37. data/lib/stannum/constraints/types/hash_with_string_keys.rb +21 -0
  38. data/lib/stannum/constraints/types/hash_with_symbol_keys.rb +21 -0
  39. data/lib/stannum/constraints/types/integer_type.rb +14 -0
  40. data/lib/stannum/constraints/types/nil_type.rb +20 -0
  41. data/lib/stannum/constraints/types/proc_type.rb +14 -0
  42. data/lib/stannum/constraints/types/string_type.rb +14 -0
  43. data/lib/stannum/constraints/types/symbol_type.rb +14 -0
  44. data/lib/stannum/constraints/types/time_type.rb +14 -0
  45. data/lib/stannum/constraints/types.rb +25 -0
  46. data/lib/stannum/constraints/union.rb +85 -0
  47. data/lib/stannum/constraints.rb +26 -0
  48. data/lib/stannum/contract.rb +243 -0
  49. data/lib/stannum/contracts/array_contract.rb +108 -0
  50. data/lib/stannum/contracts/base.rb +597 -0
  51. data/lib/stannum/contracts/builder.rb +72 -0
  52. data/lib/stannum/contracts/definition.rb +74 -0
  53. data/lib/stannum/contracts/hash_contract.rb +136 -0
  54. data/lib/stannum/contracts/indifferent_hash_contract.rb +78 -0
  55. data/lib/stannum/contracts/map_contract.rb +199 -0
  56. data/lib/stannum/contracts/parameters/arguments_contract.rb +185 -0
  57. data/lib/stannum/contracts/parameters/keywords_contract.rb +174 -0
  58. data/lib/stannum/contracts/parameters/signature_contract.rb +29 -0
  59. data/lib/stannum/contracts/parameters.rb +15 -0
  60. data/lib/stannum/contracts/parameters_contract.rb +530 -0
  61. data/lib/stannum/contracts/tuple_contract.rb +213 -0
  62. data/lib/stannum/contracts.rb +19 -0
  63. data/lib/stannum/errors.rb +730 -0
  64. data/lib/stannum/messages/default_strategy.rb +124 -0
  65. data/lib/stannum/messages.rb +25 -0
  66. data/lib/stannum/parameter_validation.rb +216 -0
  67. data/lib/stannum/rspec/match_errors.rb +17 -0
  68. data/lib/stannum/rspec/match_errors_matcher.rb +93 -0
  69. data/lib/stannum/rspec/validate_parameter.rb +23 -0
  70. data/lib/stannum/rspec/validate_parameter_matcher.rb +506 -0
  71. data/lib/stannum/rspec.rb +8 -0
  72. data/lib/stannum/schema.rb +131 -0
  73. data/lib/stannum/struct.rb +444 -0
  74. data/lib/stannum/support/coercion.rb +114 -0
  75. data/lib/stannum/support/optional.rb +69 -0
  76. data/lib/stannum/support.rb +8 -0
  77. data/lib/stannum/version.rb +57 -0
  78. data/lib/stannum.rb +27 -0
  79. metadata +216 -0
@@ -0,0 +1,174 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stannum/contracts/indifferent_hash_contract'
4
+ require 'stannum/contracts/parameters'
5
+
6
+ module Stannum::Contracts::Parameters
7
+ # @api private
8
+ #
9
+ # A KeywordsContract constrains the keywords given for a method.
10
+ class KeywordsContract < Stannum::Contracts::IndifferentHashContract
11
+ # The :type of the error generated for extra keywords.
12
+ EXTRA_KEYWORDS_TYPE = 'stannum.constraints.parameters.extra_keywords'
13
+
14
+ # Value used when keywords hash does not have a value for the given key.
15
+ UNDEFINED = Object.new.freeze
16
+
17
+ # @param options [Hash<Symbol, Object>] Configuration options for the
18
+ # contract. Defaults to an empty Hash.
19
+ def initialize(**options)
20
+ super(
21
+ allow_extra_keys: false,
22
+ **options
23
+ )
24
+ end
25
+
26
+ # Adds a keyword constraint to the contract.
27
+ #
28
+ # Generates a keyword constraint based on the given type. If the type is
29
+ # a constraint, then the given constraint will be copied with the given
30
+ # options and added for the given keyword. If the type is a Class or a
31
+ # Module, then a Stannum::Constraints::Type constraint will be created with
32
+ # the given type and options and added for the keyword.
33
+ #
34
+ # @param keyword [Symbol] The keyword to constrain.
35
+ # @param type [Class, Module, Stannum::Constraints:Base] The expected type
36
+ # of the argument.
37
+ # @param default [Boolean] If true, the keyword has a default value, and
38
+ # the constraint will ignore keywords with no value at that key.
39
+ # @param options [Hash<Symbol, Object>] Configuration options for the
40
+ # constraint. Defaults to an empty Hash.
41
+ #
42
+ # @return [Stannum::Contracts::Parameters::KeywordsContract] the contract.
43
+ def add_keyword_constraint(keyword, type, default: false, **options)
44
+ unless keyword.is_a?(Symbol)
45
+ raise ArgumentError, 'keyword must be a symbol'
46
+ end
47
+
48
+ constraint = Stannum::Support::Coercion.type_constraint(type, **options)
49
+
50
+ add_key_constraint(keyword, constraint, default: !!default, **options)
51
+
52
+ self
53
+ end
54
+
55
+ # Sets a constraint for the variadic keywords.
56
+ #
57
+ # The given constraint must match the variadic keywords hash as a whole.
58
+ # To constraint each individual value, use #set_variadic_value_constraint.
59
+ #
60
+ # @param constraint [Stannum::Constraints::Base] The constraint to add.
61
+ # The variadic keywords (a hash) as a whole must match the given
62
+ # constraint.
63
+ # @param as [Symbol] A human-friendly reference for the additional
64
+ # keywords. Used when generating errors. Should be the same name used in
65
+ # the method definition.
66
+ #
67
+ # @return [self] the contract.
68
+ #
69
+ # @raise [RuntimeError] if the variadic keywords constraint is already set.
70
+ #
71
+ # @see #set_variadic_item_constraint
72
+ def set_variadic_constraint(constraint, as: nil)
73
+ raise 'variadic keywords constraint is already set' if allow_extra_keys?
74
+
75
+ options[:allow_extra_keys] = true
76
+
77
+ variadic_constraint.receiver = constraint
78
+
79
+ variadic_definition.options[:property_name] = as if as
80
+
81
+ self
82
+ end
83
+
84
+ # Sets a constraint for the variadic keyword values.
85
+ #
86
+ # The given type or constraint must individually match each value (if any)
87
+ # in the variadic keywords. To constrain the variadic keywords as a whole,
88
+ # use #set_variadic_constraint.
89
+ #
90
+ # @param value_type [Stannum::Constraints::Base, Class, Module] The type or
91
+ # constraint to add. If the type is a Class or Module, then it is
92
+ # converted to a Stannum::Constraints::Type. Each value in the variadic
93
+ # keywords must match the given constraint.
94
+ # @param as [Symbol] A human-friendly reference for the additional
95
+ # keywords. Used when generating errors. Should be the same name used in
96
+ # the method definition.
97
+ #
98
+ # @return [self] the contract.
99
+ #
100
+ # @raise [RuntimeError] if the variadic keywords constraint is already set.
101
+ #
102
+ # @see #set_variadic_constraint
103
+ def set_variadic_value_constraint(value_type, as: nil)
104
+ type = coerce_value_type(value_type)
105
+ constraint = Stannum::Constraints::Types::HashType.new(
106
+ key_type: Stannum::Constraints::Types::SymbolType.new,
107
+ value_type: type
108
+ )
109
+
110
+ set_variadic_constraint(constraint, as: as)
111
+ end
112
+
113
+ protected
114
+
115
+ def add_errors_for(definition, value, errors)
116
+ return super unless value == UNDEFINED
117
+
118
+ super(definition, nil, errors)
119
+ end
120
+
121
+ def add_negated_errors_for(definition, value, errors)
122
+ return super unless value == UNDEFINED
123
+
124
+ super(definition, nil, errors)
125
+ end
126
+
127
+ def map_value(actual, **options)
128
+ return super unless options[:property_type] == :key
129
+
130
+ return super unless actual.is_a?(Hash)
131
+
132
+ return super if actual.key?(options[:property])
133
+
134
+ UNDEFINED
135
+ end
136
+
137
+ def match_constraint(definition, value)
138
+ return super unless value == UNDEFINED
139
+
140
+ definition.options[:default] ? true : super(definition, nil)
141
+ end
142
+
143
+ def match_negated_constraint(definition, value)
144
+ return super unless value == UNDEFINED
145
+
146
+ definition.options[:default] ? false : super(definition, nil)
147
+ end
148
+
149
+ private
150
+
151
+ attr_reader :variadic_constraint
152
+
153
+ attr_reader :variadic_definition
154
+
155
+ def add_extra_keys_constraint
156
+ keys = -> { expected_keys }
157
+
158
+ @variadic_constraint = Stannum::Constraints::Delegator.new(
159
+ Stannum::Constraints::Hashes::ExtraKeys.new(
160
+ keys,
161
+ type: EXTRA_KEYWORDS_TYPE
162
+ )
163
+ )
164
+
165
+ add_constraint @variadic_constraint
166
+
167
+ @variadic_definition = @constraints.last
168
+ end
169
+
170
+ def coerce_value_type(value_type)
171
+ Stannum::Support::Coercion.type_constraint(value_type, as: 'value type')
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stannum/contracts/parameters'
4
+
5
+ module Stannum::Contracts::Parameters
6
+ # A SignatureContract defines a parameters object for a ParametersContract.
7
+ class SignatureContract < Stannum::Contracts::HashContract
8
+ # @param options [Hash<Symbol, Object>] Configuration options for the
9
+ # contract. Defaults to an empty Hash.
10
+ def initialize(**options)
11
+ super(key_type: Symbol, **options)
12
+ end
13
+
14
+ private
15
+
16
+ def define_constraints(&block)
17
+ super
18
+
19
+ add_key_constraint :arguments,
20
+ Stannum::Constraints::Types::ArrayType.new
21
+ add_key_constraint :keywords,
22
+ Stannum::Constraints::Types::HashType.new(
23
+ key_type: Stannum::Constraints::Types::SymbolType.new
24
+ )
25
+ add_key_constraint :block,
26
+ Stannum::Constraints::Types::ProcType.new(optional: true)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stannum/contracts'
4
+
5
+ module Stannum::Contracts
6
+ # Namespace for contracts that constrain method parameters.
7
+ module Parameters
8
+ autoload :ArgumentsContract,
9
+ 'stannum/contracts/parameters/arguments_contract'
10
+ autoload :KeywordsContract,
11
+ 'stannum/contracts/parameters/keywords_contract'
12
+ autoload :SignatureContract,
13
+ 'stannum/contracts/parameters/signature_contract'
14
+ end
15
+ end