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,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