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,530 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stannum/contracts'
4
+
5
+ require 'stannum/support/coercion'
6
+
7
+ module Stannum::Contracts
8
+ # A Parameters defines constraints on the parameters for a block or method.
9
+ #
10
+ # The ParametersContract requires that the actual object matched be a Hash
11
+ # with the following keys: :arguments, with an Array value; :keywords, with a
12
+ # Hash value; and :block, with a value of either a Proc or nil.
13
+ #
14
+ # For the arguments constraints, the contract verifies that the correct number
15
+ # of arguments are given and that each argument matches the type or constraint
16
+ # specified. If the contract has a variadic arguments constraint, then each
17
+ # variadic (or "splatted") argument is checked against the type or constraint.
18
+ #
19
+ # For the keywords constraints, the contract verifies that the expected
20
+ # keywords are given and that each keyword value matches the type or
21
+ # constraint specified. If the contract has a variadic keywords constraint,
22
+ # then each variadic keyword value is checked against the type or constraint.
23
+ #
24
+ # For the block constraint, the contract specifies that the block is present,
25
+ # the block is absent, or the block matches the given constraint.
26
+ #
27
+ # @example Defining A Parameters Contract With Arguments
28
+ # contract = Stannum::Contracts::ParametersContract.new do
29
+ # argument :name, String
30
+ # argument :size, String, default: true
31
+ # end
32
+ #
33
+ # contract.matches?(
34
+ # { arguments: [], keywords: {}, block: nil }
35
+ # )
36
+ # #=> false
37
+ #
38
+ # contract.matches?(
39
+ # { arguments: [:a_symbol], keywords: {}, block: nil }
40
+ # )
41
+ # #=> false
42
+ #
43
+ # contract.matches?(
44
+ # { arguments: ['Widget', 'Small', :extra], keywords: {}, block: nil }
45
+ # )
46
+ # #=> false
47
+ #
48
+ # contract.matches?(
49
+ # { arguments: ['Widget', 'Small', :extra], keywords: {}, block: nil }
50
+ # )
51
+ # #=> false
52
+ #
53
+ # contract.matches?(
54
+ # { arguments: ['Widget'], keywords: {}, block: nil }
55
+ # )
56
+ # #=> true
57
+ #
58
+ # contract.matches?(
59
+ # { arguments: ['Widget', nil], keywords: {}, block: nil }
60
+ # )
61
+ # #=> false
62
+ #
63
+ # contract.matches?(
64
+ # { arguments: ['Widget', 'Small'], keywords: {}, block: nil }
65
+ # )
66
+ # #=> true
67
+ #
68
+ # @example Defining A Parameters Contract With Keywords
69
+ # contract = Stannum::Contracts::ParametersContract.new do
70
+ # keyword :price, String
71
+ # keyword :quantity, Integer, optional: true
72
+ # end
73
+ #
74
+ # contract.matches?(
75
+ # { arguments: [], keywords: {}, block: nil }
76
+ # )
77
+ # #=> false
78
+ #
79
+ # contract.matches?(
80
+ # arguments: [],
81
+ # keywords: {
82
+ # price: 1_000_000,
83
+ # },
84
+ # block: nil
85
+ # )
86
+ # #=> false
87
+ #
88
+ # contract.matches?(
89
+ # arguments: [],
90
+ # keywords: {
91
+ # currency: 'USD',
92
+ # price: '$1_000_000',
93
+ # },
94
+ # block: nil
95
+ # )
96
+ # #=> false
97
+ #
98
+ # contract.matches?(
99
+ # arguments: [],
100
+ # keywords: {
101
+ # price: '$1_000_000',
102
+ # },
103
+ # block: nil
104
+ # )
105
+ #
106
+ # #=> true
107
+ # contract.matches?(
108
+ # arguments: [],
109
+ # keywords: {
110
+ # price: '$1_000_000',
111
+ # quantity: 50,
112
+ # },
113
+ # block: nil
114
+ # )
115
+ # #=> true
116
+ #
117
+ # @example Defining A Parameters Contract With Variadic Arguments
118
+ # contract = Stannum::Contracts::ParametersContract.new do
119
+ # arguments :words, String
120
+ # end
121
+ #
122
+ # contract.matches?(
123
+ # { arguments: [1, 2, 3], keywords: {}, block: nil }
124
+ # )
125
+ # #=> false
126
+ #
127
+ # contract.matches?(
128
+ # { arguments: [], keywords: {}, block: nil }
129
+ # )
130
+ # #=> true
131
+ #
132
+ # contract.matches?(
133
+ # { arguments: ['foo', 'bar', 'baz'], keywords: {}, block: nil }
134
+ # )
135
+ # #=> true
136
+ #
137
+ # @example Defining A Parameters Contract With Variadic Keywords
138
+ # contract = Stannum::Contracts::ParametersContract.new do
139
+ # keywords :options, Symbol
140
+ # end
141
+ #
142
+ # contract.matches?(
143
+ # arguments: [],
144
+ # keywords: {
145
+ # price: 1_000_000,
146
+ # },
147
+ # block: nil
148
+ # )
149
+ # #=> false
150
+ #
151
+ # contract.matches?(
152
+ # arguments: [],
153
+ # keywords: {},
154
+ # block: nil
155
+ # )
156
+ # #=> true
157
+ #
158
+ # contract.matches?(
159
+ # arguments: [],
160
+ # keywords: {
161
+ # color: :red,
162
+ # shape: :triangle
163
+ # },
164
+ # block: nil
165
+ # )
166
+ # #=> true
167
+ #
168
+ # @example Defining A Parameters Contract With A Block Constraint
169
+ # contract = Stannum::Contracts::ParametersContract.new do
170
+ # block true
171
+ # end
172
+ #
173
+ # contract.matches?(
174
+ # { arguments: [], keywords: {}, block: nil }
175
+ # )
176
+ # #=> false
177
+ #
178
+ # contract.matches?(
179
+ # { arguments: [], keywords: {}, block: Proc.new }
180
+ # )
181
+ # #=> true
182
+ class ParametersContract < Stannum::Contracts::HashContract
183
+ # Builder class for defining item constraints for a ParametersContract.
184
+ #
185
+ # This class should not be invoked directly. Instead, pass a block to the
186
+ # constructor for ParametersContract.
187
+ #
188
+ # @api private
189
+ class Builder < Stannum::Contract::Builder
190
+ # Adds an argument constraint to the contract.
191
+ #
192
+ # If the index is specified, then the constraint will be added for the
193
+ # argument at the specified index. If the index is not given, then the
194
+ # constraint will be applied to the next unconstrained argument. For
195
+ # example, the first argument constraint will be added for the argument at
196
+ # index 0, the second constraint for the argument at index 1, and so on.
197
+ #
198
+ # @overload argument(name, type, index: nil, **options)
199
+ # Generates an argument constraint based on the given type. If the type
200
+ # is a constraint, then the given constraint will be copied with the
201
+ # given options and added for the argument at the index. If the type is
202
+ # a Class or a Module, then a Stannum::Constraints::Type constraint will
203
+ # be created with the given type and options and added for the argument.
204
+ #
205
+ # @param name [String, Symbol] The name of the argument.
206
+ # @param type [Class, Module, Stannum::Constraints:Base] The expected
207
+ # type of the argument.
208
+ # @param index [Integer, nil] The index of the argument. If not given,
209
+ # then the next argument will be constrained with the type.
210
+ # @param options [Hash<Symbol, Object>] Configuration options for the
211
+ # constraint. Defaults to an empty Hash.
212
+ #
213
+ # @return [Stannum::Contracts::ParametersContract::Builder] the builder.
214
+ #
215
+ # @overload argument(name, index: nil, **options, &block
216
+ # Generates a new Stannum::Constraint using the block.
217
+ #
218
+ # @param name [String, Symbol] The name of the argument.
219
+ # @param index [Integer, nil] The index of the argument. If not given,
220
+ # then the next argument will be constrained with the type.
221
+ # @param options [Hash<Symbol, Object>] Configuration options for the
222
+ # constraint. Defaults to an empty Hash.
223
+ #
224
+ # @yield The definition for the constraint. Each time #matches? is
225
+ # called for this constraint, the given object will be passed to this
226
+ # block and the result of the block will be returned.
227
+ # @yieldparam actual [Object] The object to check against the
228
+ # constraint.
229
+ # @yieldreturn [true, false] true if the given object matches the
230
+ # constraint, otherwise false.
231
+ #
232
+ # @return [Stannum::Contracts::ParametersContract::Builder] the builder.
233
+ def argument(name, type = nil, index: nil, **options, &block)
234
+ type = resolve_constraint_or_type(type, **options, &block)
235
+
236
+ contract.add_argument_constraint(
237
+ index,
238
+ type,
239
+ property_name: name,
240
+ **options
241
+ )
242
+
243
+ self
244
+ end
245
+
246
+ # Sets the variadic arguments constraint for the contract.
247
+ #
248
+ # If the parameters includes variadic (or "splatted") arguments, then each
249
+ # item in the variadic arguments array must match the given type or
250
+ # constraint. If the type is a constraint, then the given constraint will
251
+ # be copied with the given options. If the type is a Class or a Module,
252
+ # then a Stannum::Constraints::Type constraint will be created with the
253
+ # given type.
254
+ #
255
+ # @param name [String, Symbol] a human-readable name for the variadic
256
+ # arguments; used in generating error messages.
257
+ # @param type [Class, Module, Stannum::Constraints:Base] The expected type
258
+ # of the variadic arguments items.
259
+ #
260
+ # @return [Stannum::Contracts::ParametersContract::Builder] the builder.
261
+ #
262
+ # @raise [RuntimeError] if there is already a variadic arguments
263
+ # constraint defined for the contract.
264
+ def arguments(name, type)
265
+ contract.set_arguments_item_constraint(name, type)
266
+
267
+ self
268
+ end
269
+
270
+ # Sets the block parameter constraint for the contract.
271
+ #
272
+ # If the expected presence is true, a block must be given as part of the
273
+ # parameters. If the expected presence is false, a block must not be
274
+ # given. If the presence is a constraint, then the block must match the
275
+ # constraint.
276
+ #
277
+ # @param present [true, false, Stannum::Constraint] The expected presence
278
+ # of the block.
279
+ #
280
+ # @return [Stannum::Contracts::ParametersContract] the contract.
281
+ #
282
+ # @raise [RuntimeError] if there is already a block constraint defined for
283
+ # the contract.
284
+ def block(present)
285
+ contract.set_block_constraint(present)
286
+
287
+ self
288
+ end
289
+
290
+ # Adds a keyword constraint to the contract.
291
+ #
292
+ # @overload keyword(name, type, **options)
293
+ # Generates a keyword constraint based on the given type. If the type is
294
+ # a constraint, then the given constraint will be copied with the given
295
+ # options and added for the given keyword. If the type is a Class or a
296
+ # Module, then a Stannum::Constraints::Type constraint will be created
297
+ # with the given type and options and added for the keyword.
298
+ #
299
+ # @param keyword [Symbol] The keyword to constrain.
300
+ # @param type [Class, Module, Stannum::Constraints:Base] The expected
301
+ # type of the keyword.
302
+ # @param options [Hash<Symbol, Object>] Configuration options for the
303
+ # constraint. Defaults to an empty Hash.
304
+ #
305
+ # @return [Stannum::Contracts::ParametersContract::Builder] the builder.
306
+ #
307
+ # @overload keyword(name, **options, &block)
308
+ # Generates a new Stannum::Constraint using the block.
309
+ #
310
+ # @param keyword [Symbol] The keyword to constrain.
311
+ # @param options [Hash<Symbol, Object>] Configuration options for the
312
+ # constraint. Defaults to an empty Hash.
313
+ #
314
+ # @yield The definition for the constraint. Each time #matches? is
315
+ # called for this constraint, the given object will be passed to this
316
+ # block and the result of the block will be returned.
317
+ # @yieldparam actual [Object] The object to check against the
318
+ # constraint.
319
+ # @yieldreturn [true, false] true if the given object matches the
320
+ # constraint, otherwise false.
321
+ #
322
+ # @return [Stannum::Contracts::ParametersContract::Builder] the builder.
323
+ def keyword(name, type = nil, **options, &block)
324
+ type = resolve_constraint_or_type(type, **options, &block)
325
+
326
+ contract.add_keyword_constraint(
327
+ name,
328
+ type,
329
+ **options
330
+ )
331
+
332
+ self
333
+ end
334
+
335
+ # Sets the variadic keywords constraint for the contract.
336
+ #
337
+ # If the parameters includes variadic (or "splatted") keywords, then each
338
+ # value in the variadic keywords hash must match the given type or
339
+ # constraint. If the type is a constraint, then the given constraint will
340
+ # be copied with the given options. If the type is a Class or a Module,
341
+ # then a Stannum::Constraints::Type constraint will be created with the
342
+ # given type.
343
+ #
344
+ # @param name [String, Symbol] a human-readable name for the variadic
345
+ # keywords; used in generating error messages.
346
+ # @param type [Class, Module, Stannum::Constraints:Base] The expected type
347
+ # of the variadic keywords values.
348
+ #
349
+ # @return [Stannum::Contracts::ParametersContract::Builder] the builder.
350
+ #
351
+ # @raise [RuntimeError] if there is already a variadic keywords constraint
352
+ # defined for the contract.
353
+ def keywords(name, type)
354
+ contract.set_keywords_value_constraint(name, type)
355
+
356
+ self
357
+ end
358
+
359
+ private
360
+
361
+ def resolve_constraint_or_type(type = nil, **options, &block)
362
+ if block_given? && type
363
+ raise ArgumentError, ambiguous_values_error(type), caller(1..-1)
364
+ end
365
+
366
+ return type if type.is_a?(Module) || valid_constraint?(type)
367
+
368
+ return Stannum::Constraint.new(**options, &block) if block
369
+
370
+ raise ArgumentError,
371
+ "invalid constraint #{type.inspect}",
372
+ caller(1..-1)
373
+ end
374
+ end
375
+
376
+ # Adds an argument constraint to the contract.
377
+ #
378
+ # Generates an argument constraint based on the given type. If the type is
379
+ # a constraint, then the given constraint will be copied with the given
380
+ # options and added for the argument at the index. If the type is a Class or
381
+ # a Module, then a Stannum::Constraints::Type constraint will be created
382
+ # with the given type and options and added for the argument.
383
+ #
384
+ # If the index is specified, then the constraint will be added for the
385
+ # argument at the specified index. If the index is not given, then the
386
+ # constraint will be applied to the next unconstrained argument. For
387
+ # example, the first argument constraint will be added for the argument at
388
+ # index 0, the second constraint for the argument at index 1, and so on.
389
+ #
390
+ # @param index [Integer, nil] The index of the argument. If not given, then
391
+ # the next argument will be constrained with the type.
392
+ # @param type [Class, Module, Stannum::Constraints:Base] The expected type
393
+ # of the argument.
394
+ # @param options [Hash<Symbol, Object>] Configuration options for the
395
+ # constraint. Defaults to an empty Hash.
396
+ #
397
+ # @return [Stannum::Contracts::ParametersContract] the contract.
398
+ def add_argument_constraint(index, type, **options)
399
+ arguments_contract.add_argument_constraint(index, type, **options)
400
+
401
+ self
402
+ end
403
+
404
+ # Adds a keyword constraint to the contract.
405
+ #
406
+ # Generates a keyword constraint based on the given type. If the type is
407
+ # a constraint, then the given constraint will be copied with the given
408
+ # options and added for the given keyword. If the type is a Class or a
409
+ # Module, then a Stannum::Constraints::Type constraint will be created with
410
+ # the given type and options and added for the keyword.
411
+ #
412
+ # @param keyword [Symbol] The keyword to constrain.
413
+ # @param type [Class, Module, Stannum::Constraints:Base] The expected type
414
+ # of the argument.
415
+ # @param options [Hash<Symbol, Object>] Configuration options for the
416
+ # constraint. Defaults to an empty Hash.
417
+ #
418
+ # @return [Stannum::Contracts::ParametersContract] the contract.
419
+ def add_keyword_constraint(keyword, type, **options)
420
+ keywords_contract.add_keyword_constraint(keyword, type, **options)
421
+
422
+ self
423
+ end
424
+
425
+ # Sets the variadic arguments constraint for the contract.
426
+ #
427
+ # If the parameters includes variadic (or "splatted") arguments, then each
428
+ # item in the variadic arguments array must match the given type or
429
+ # constraint. If the type is a constraint, then the given constraint will be
430
+ # copied with the given options. If the type is a Class or a Module, then a
431
+ # Stannum::Constraints::Type constraint will be created with the given type.
432
+ #
433
+ # @param name [String, Symbol] a human-readable name for the variadic
434
+ # arguments; used in generating error messages.
435
+ # @param type [Class, Module, Stannum::Constraints:Base] The expected type
436
+ # of the variadic arguments items.
437
+ #
438
+ # @return [Stannum::Contracts::ParametersContract] the contract.
439
+ #
440
+ # @raise [RuntimeError] if there is already a variadic arguments constraint
441
+ # defined for the contract.
442
+ def set_arguments_item_constraint(name, type)
443
+ arguments_contract.set_variadic_item_constraint(type, as: name)
444
+
445
+ self
446
+ end
447
+
448
+ # Sets the block parameter constraint for the contract.
449
+ #
450
+ # If the expected presence is true, a block must be given as part of the
451
+ # parameters. If the expected presence is false, a block must not be given.
452
+ # If the presence is a constraint, then the block must match the constraint.
453
+ #
454
+ # @param present [true, false, Stannum::Constraint] The expected presence of
455
+ # the block.
456
+ #
457
+ # @return [Stannum::Contracts::ParametersContract] the contract.
458
+ #
459
+ # @raise [RuntimeError] if there is already a block constraint defined for
460
+ # the contract.
461
+ def set_block_constraint(present) # rubocop:disable Naming/AccessorMethodName
462
+ raise 'block constraint is already set' if @block_constraint
463
+
464
+ @block_constraint = build_block_constraint(present)
465
+
466
+ add_key_constraint(:block, @block_constraint)
467
+ end
468
+
469
+ # Sets the variadic keywords constraint for the contract.
470
+ #
471
+ # If the parameters includes variadic (or "splatted") keywords, then each
472
+ # value in the variadic keywords hash must match the given type or
473
+ # constraint. If the type is a constraint, then the given constraint will be
474
+ # copied with the given options. If the type is a Class or a Module, then a
475
+ # Stannum::Constraints::Type constraint will be created with the given type.
476
+ #
477
+ # @param name [String, Symbol] a human-readable name for the variadic
478
+ # keywords; used in generating error messages.
479
+ # @param type [Class, Module, Stannum::Constraints:Base] The expected type
480
+ # of the variadic keywords values.
481
+ #
482
+ # @return [Stannum::Contracts::ParametersContract] the contract.
483
+ #
484
+ # @raise [RuntimeError] if there is already a variadic keywords constraint
485
+ # defined for the contract.
486
+ def set_keywords_value_constraint(name, type)
487
+ keywords_contract.set_variadic_value_constraint(type, as: name)
488
+
489
+ self
490
+ end
491
+
492
+ private
493
+
494
+ attr_reader :block_constraint
495
+
496
+ def add_extra_keys_constraint; end
497
+
498
+ def add_type_constraint
499
+ add_constraint \
500
+ Stannum::Contracts::Parameters::SignatureContract.new,
501
+ sanity: true
502
+ end
503
+
504
+ def arguments_contract
505
+ @arguments_contract ||=
506
+ Stannum::Contracts::Parameters::ArgumentsContract.new
507
+ end
508
+
509
+ def build_block_constraint(value)
510
+ Stannum::Support::Coercion.presence_constraint(value) \
511
+ do |present, **options|
512
+ next Stannum::Constraints::Types::ProcType.new(**options) if present
513
+
514
+ Stannum::Constraints::Types::NilType.new(**options)
515
+ end
516
+ end
517
+
518
+ def define_constraints(&block)
519
+ super(&block)
520
+
521
+ add_key_constraint :arguments, arguments_contract
522
+ add_key_constraint :keywords, keywords_contract
523
+ end
524
+
525
+ def keywords_contract
526
+ @keywords_contract ||=
527
+ Stannum::Contracts::Parameters::KeywordsContract.new
528
+ end
529
+ end
530
+ end