stannum 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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