@@ -8,7 +8,7 @@ module Support
8
8
# keyword args of a given method.
9
9
#
10
10
# @private
11
- class MethodSignature
11
+ class MethodSignature # rubocop:disable ClassLength
12
12
attr_reader :min_non_kw_args , :max_non_kw_args , :optional_kw_args , :required_kw_args
13
13
14
14
def initialize ( method )
@@ -26,9 +26,11 @@ def non_kw_args_arity_description
26
26
end
27
27
end
28
28
29
- def valid_non_kw_args? ( positional_arg_count )
29
+ def valid_non_kw_args? ( positional_arg_count , optional_max_arg_count = positional_arg_count )
30
+ return true if positional_arg_count . nil?
31
+
30
32
min_non_kw_args <= positional_arg_count &&
31
- positional_arg_count <= max_non_kw_args
33
+ optional_max_arg_count <= max_non_kw_args
32
34
end
33
35
34
36
if RubyFeatures . optional_and_splat_args_supported?
@@ -74,6 +76,14 @@ def could_contain_kw_args?(args)
74
76
@allows_any_kw_args || @allowed_kw_args . any?
75
77
end
76
78
79
+ def arbitrary_kw_args?
80
+ @allows_any_kw_args
81
+ end
82
+
83
+ def unlimited_args?
84
+ @max_non_kw_args == INFINITY
85
+ end
86
+
77
87
def classify_parameters
78
88
optional_non_kw_args = @min_non_kw_args = 0
79
89
@allows_any_kw_args = false
@@ -119,6 +129,14 @@ def could_contain_kw_args?(*)
119
129
false
120
130
end
121
131
132
+ def arbitrary_kw_args?
133
+ false
134
+ end
135
+
136
+ def unlimited_args?
137
+ false
138
+ end
139
+
122
140
def classify_parameters
123
141
arity = @method . arity
124
142
if arity < 0
@@ -153,6 +171,50 @@ def classify_parameters
153
171
end
154
172
end
155
173
174
+ # Encapsulates expectations about the number of arguments and
175
+ # allowed/required keyword args of a given method.
176
+ #
177
+ # @api private
178
+ class MethodSignatureExpectation
179
+ def initialize
180
+ @min_count = nil
181
+ @max_count = nil
182
+ @keywords = [ ]
183
+
184
+ @expect_unlimited_arguments = false
185
+ @expect_arbitrary_keywords = false
186
+ end
187
+
188
+ attr_reader :min_count , :max_count , :keywords
189
+
190
+ attr_accessor :expect_unlimited_arguments , :expect_arbitrary_keywords
191
+
192
+ def max_count = ( number )
193
+ raise ArgumentError , 'must be a non-negative integer or nil' \
194
+ unless number . nil? || ( number . is_a? ( Integer ) && number >= 0 )
195
+
196
+ @max_count = number
197
+ end
198
+
199
+ def min_count = ( number )
200
+ raise ArgumentError , 'must be a non-negative integer or nil' \
201
+ unless number . nil? || ( number . is_a? ( Integer ) && number >= 0 )
202
+
203
+ @min_count = number
204
+ end
205
+
206
+ def empty?
207
+ @min_count . nil? &&
208
+ @keywords . to_a . empty? &&
209
+ !@expect_arbitrary_keywords &&
210
+ !@expect_unlimited_arguments
211
+ end
212
+
213
+ def keywords = ( values )
214
+ @keywords = values . to_a || [ ]
215
+ end
216
+ end
217
+
156
218
# Deals with the slightly different semantics of block arguments.
157
219
# For methods, arguments are required unless a default value is provided.
158
220
# For blocks, arguments are optional, even if no default value is provided.
@@ -175,17 +237,49 @@ def classify_parameters
175
237
#
176
238
# @api private
177
239
class MethodSignatureVerifier
178
- attr_reader :non_kw_args , :kw_args
240
+ attr_reader :non_kw_args , :kw_args , :min_non_kw_args , :max_non_kw_args
179
241
180
242
def initialize ( signature , args )
181
243
@signature = signature
182
244
@non_kw_args , @kw_args = split_args ( *args )
245
+ @min_non_kw_args = @max_non_kw_args = @non_kw_args
246
+ @arbitrary_kw_args = @unlimited_args = false
247
+ end
248
+
249
+ def with_expectation ( expectation ) # rubocop:disable MethodLength
250
+ return self unless MethodSignatureExpectation === expectation
251
+
252
+ if expectation . empty?
253
+ @min_non_kw_args = @max_non_kw_args = @non_kw_args = nil
254
+ @kw_args = [ ]
255
+ else
256
+ @min_non_kw_args = @non_kw_args = expectation . min_count || 0
257
+ @max_non_kw_args = expectation . max_count || @min_non_kw_args
258
+
259
+ if RubyFeatures . optional_and_splat_args_supported?
260
+ @unlimited_args = expectation . expect_unlimited_arguments
261
+ else
262
+ @unlimited_args = false
263
+ end
264
+
265
+ if RubyFeatures . kw_args_supported?
266
+ @kw_args = expectation . keywords
267
+ @arbitrary_kw_args = expectation . expect_arbitrary_keywords
268
+ else
269
+ @kw_args = [ ]
270
+ @arbitrary_kw_args = false
271
+ end
272
+ end
273
+
274
+ self
183
275
end
184
276
185
277
def valid?
186
278
missing_kw_args . empty? &&
187
279
invalid_kw_args . empty? &&
188
- valid_non_kw_args?
280
+ valid_non_kw_args? &&
281
+ arbitrary_kw_args? &&
282
+ unlimited_args?
189
283
end
190
284
191
285
def error_message
@@ -200,15 +294,15 @@ def error_message
200
294
elsif !valid_non_kw_args?
201
295
"Wrong number of arguments. Expected %s, got %s." % [
202
296
@signature . non_kw_args_arity_description ,
203
- non_kw_args . length
297
+ non_kw_args
204
298
]
205
299
end
206
300
end
207
301
208
302
private
209
303
210
304
def valid_non_kw_args?
211
- @signature . valid_non_kw_args? ( non_kw_args . length )
305
+ @signature . valid_non_kw_args? ( min_non_kw_args , max_non_kw_args )
212
306
end
213
307
214
308
def missing_kw_args
@@ -219,14 +313,22 @@ def invalid_kw_args
219
313
@signature . invalid_kw_args_from ( kw_args )
220
314
end
221
315
316
+ def arbitrary_kw_args?
317
+ !@arbitrary_kw_args || @signature . arbitrary_kw_args?
318
+ end
319
+
320
+ def unlimited_args?
321
+ !@unlimited_args || @signature . unlimited_args?
322
+ end
323
+
222
324
def split_args ( *args )
223
325
kw_args = if @signature . has_kw_args_in? ( args )
224
326
args . pop . keys
225
327
else
226
328
[ ]
227
329
end
228
330
229
- [ args , kw_args ]
331
+ [ args . length , kw_args ]
230
332
end
231
333
end
232
334
0 commit comments