|
7 | 7 | require "contracts/support"
|
8 | 8 | require "contracts/engine"
|
9 | 9 | require "contracts/method_handler"
|
| 10 | +require "contracts/validators" |
| 11 | +require "contracts/call_with" |
10 | 12 |
|
11 | 13 | module Contracts
|
12 | 14 | def self.included(base)
|
@@ -58,7 +60,15 @@ def functype(funcname)
|
58 | 60 | # Contract [contract names] => return_value
|
59 | 61 | #
|
60 | 62 | # This class also provides useful callbacks and a validation method.
|
| 63 | +# |
| 64 | +# For #make_validator and related logic see file |
| 65 | +# lib/contracts/validators.rb |
| 66 | +# For #call_with and related logic see file |
| 67 | +# lib/contracts/call_with.rb |
61 | 68 | class Contract < Contracts::Decorator
|
| 69 | + extend Contracts::Validators |
| 70 | + include Contracts::CallWith |
| 71 | + |
62 | 72 | # Default implementation of failure_callback. Provided as a block to be able
|
63 | 73 | # to monkey patch #failure_callback only temporary and then switch it back.
|
64 | 74 | # First important usage - for specs.
|
@@ -209,54 +219,6 @@ def self.valid?(arg, contract)
|
209 | 219 | make_validator(contract)[arg]
|
210 | 220 | end
|
211 | 221 |
|
212 |
| - # This is a little weird. For each contract |
213 |
| - # we pre-make a proc to validate it so we |
214 |
| - # don't have to go through this decision tree every time. |
215 |
| - # Seems silly but it saves us a bunch of time (4.3sec vs 5.2sec) |
216 |
| - def self.make_validator(contract) |
217 |
| - # if is faster than case! |
218 |
| - klass = contract.class |
219 |
| - if klass == Proc |
220 |
| - # e.g. lambda {true} |
221 |
| - contract |
222 |
| - elsif klass == Array |
223 |
| - # e.g. [Num, String] |
224 |
| - # TODO: account for these errors too |
225 |
| - lambda do |arg| |
226 |
| - return false unless arg.is_a?(Array) && arg.length == contract.length |
227 |
| - arg.zip(contract).all? do |_arg, _contract| |
228 |
| - Contract.valid?(_arg, _contract) |
229 |
| - end |
230 |
| - end |
231 |
| - elsif klass == Hash |
232 |
| - # e.g. { :a => Num, :b => String } |
233 |
| - lambda do |arg| |
234 |
| - return false unless arg.is_a?(Hash) |
235 |
| - contract.keys.all? do |k| |
236 |
| - Contract.valid?(arg[k], contract[k]) |
237 |
| - end |
238 |
| - end |
239 |
| - elsif klass == Contracts::Args |
240 |
| - lambda do |arg| |
241 |
| - Contract.valid?(arg, contract.contract) |
242 |
| - end |
243 |
| - elsif klass == Contracts::Func |
244 |
| - lambda do |arg| |
245 |
| - arg.is_a?(Method) || arg.is_a?(Proc) |
246 |
| - end |
247 |
| - else |
248 |
| - # classes and everything else |
249 |
| - # e.g. Fixnum, Num |
250 |
| - if contract.respond_to? :valid? |
251 |
| - lambda { |arg| contract.valid?(arg) } |
252 |
| - elsif klass == Class || klass == Module |
253 |
| - lambda { |arg| arg.is_a?(contract) } |
254 |
| - else |
255 |
| - lambda { |arg| contract == arg } |
256 |
| - end |
257 |
| - end |
258 |
| - end |
259 |
| - |
260 | 222 | def [](*args, &blk)
|
261 | 223 | call(*args, &blk)
|
262 | 224 | end
|
@@ -287,99 +249,6 @@ def maybe_append_options! args, blk
|
287 | 249 | end
|
288 | 250 | end
|
289 | 251 |
|
290 |
| - def call_with(this, *args, &blk) |
291 |
| - args << blk if blk |
292 |
| - |
293 |
| - # Explicitly append blk=nil if nil != Proc contract violation anticipated |
294 |
| - maybe_append_block!(args, blk) |
295 |
| - |
296 |
| - # Explicitly append options={} if Hash contract is present |
297 |
| - maybe_append_options!(args, blk) |
298 |
| - |
299 |
| - # Loop forward validating the arguments up to the splat (if there is one) |
300 |
| - (@args_contract_index || args.size).times do |i| |
301 |
| - contract = args_contracts[i] |
302 |
| - arg = args[i] |
303 |
| - validator = @args_validators[i] |
304 |
| - |
305 |
| - unless validator && validator[arg] |
306 |
| - return unless Contract.failure_callback(:arg => arg, |
307 |
| - :contract => contract, |
308 |
| - :class => klass, |
309 |
| - :method => method, |
310 |
| - :contracts => self, |
311 |
| - :arg_pos => i+1, |
312 |
| - :total_args => args.size, |
313 |
| - :return_value => false) |
314 |
| - end |
315 |
| - |
316 |
| - if contract.is_a?(Contracts::Func) |
317 |
| - args[i] = Contract.new(klass, arg, *contract.contracts) |
318 |
| - end |
319 |
| - end |
320 |
| - |
321 |
| - # If there is a splat loop backwards to the lower index of the splat |
322 |
| - # Once we hit the splat in this direction set its upper index |
323 |
| - # Keep validating but use this upper index to get the splat validator. |
324 |
| - if @args_contract_index |
325 |
| - splat_upper_index = @args_contract_index |
326 |
| - (args.size - @args_contract_index).times do |i| |
327 |
| - arg = args[args.size - 1 - i] |
328 |
| - |
329 |
| - if args_contracts[args_contracts.size - 1 - i].is_a?(Contracts::Args) |
330 |
| - splat_upper_index = i |
331 |
| - end |
332 |
| - |
333 |
| - # Each arg after the spat is found must use the splat validator |
334 |
| - j = i < splat_upper_index ? i : splat_upper_index |
335 |
| - contract = args_contracts[args_contracts.size - 1 - j] |
336 |
| - validator = @args_validators[args_contracts.size - 1 - j] |
337 |
| - |
338 |
| - unless validator && validator[arg] |
339 |
| - return unless Contract.failure_callback(:arg => arg, |
340 |
| - :contract => contract, |
341 |
| - :class => klass, |
342 |
| - :method => method, |
343 |
| - :contracts => self, |
344 |
| - :arg_pos => i-1, |
345 |
| - :total_args => args.size, |
346 |
| - :return_value => false) |
347 |
| - end |
348 |
| - |
349 |
| - if contract.is_a?(Contracts::Func) |
350 |
| - args[args.size - 1 - i] = Contract.new(klass, arg, *contract.contracts) |
351 |
| - end |
352 |
| - end |
353 |
| - end |
354 |
| - |
355 |
| - # If we put the block into args for validating, restore the args |
356 |
| - args.slice!(-1) if blk |
357 |
| - result = if method.respond_to?(:call) |
358 |
| - # proc, block, lambda, etc |
359 |
| - method.call(*args, &blk) |
360 |
| - else |
361 |
| - # original method name referrence |
362 |
| - method.send_to(this, *args, &blk) |
363 |
| - end |
364 |
| - |
365 |
| - unless @ret_validator[result] |
366 |
| - Contract.failure_callback(:arg => result, |
367 |
| - :contract => ret_contract, |
368 |
| - :class => klass, |
369 |
| - :method => method, |
370 |
| - :contracts => self, |
371 |
| - :return_value => true) |
372 |
| - end |
373 |
| - |
374 |
| - this.verify_invariants!(method) if this.respond_to?(:verify_invariants!) |
375 |
| - |
376 |
| - if ret_contract.is_a?(Contracts::Func) |
377 |
| - result = Contract.new(klass, result, *ret_contract.contracts) |
378 |
| - end |
379 |
| - |
380 |
| - result |
381 |
| - end |
382 |
| - |
383 | 252 | # Used to determine type of failure exception this contract should raise in case of failure
|
384 | 253 | def failure_exception
|
385 | 254 | if @pattern_match
|
|
0 commit comments