contracts 0.9 → 0.10

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c872d0fc055f6b1ace99f6793c436ee222111992
4
- data.tar.gz: 0bc9d2a28a4dd77395932974fe46e9175818fa73
3
+ metadata.gz: bb8713bcd0175e6069a34ede6834e012395e11a4
4
+ data.tar.gz: 185ac772d51ee7a5fb80c3c4058155aae7094e08
5
5
  SHA512:
6
- metadata.gz: 2c2eb482b3c7b2e697ff29076734fa9ec74dc93b9f8344514fdfab3a999a1b40dff0d57bbf487dcd82eaf51b7b503c55cc55a6573745b36314f09f3ce2f5778c
7
- data.tar.gz: 3535f6d2ceea6e1382b9810679fcb619b94ed9788434022b597f120165a534383c306e6d5ce305ac0808a1b0d34dd0f1b1ca1ec53e3e61f3481a860197e8be5a
6
+ metadata.gz: bd78da1e9ed5f3af971fdabab93faa1924c9b74619b36298852ddbfb3b525f161678cd34d2d9a619aff0c958bf3da3694afcee05b0d1bf76b07bd9a69400561a
7
+ data.tar.gz: 6295dd08adbc49c431b8e2cc234f500592c035c1b9547c6c7fe6ab63b0b91420b323b8d57385d9685aee1510645b881bdd1b575637efa848f3e2175f4ccfca03
data/CHANGELOG.markdown CHANGED
@@ -2,14 +2,17 @@
2
2
  - MAJOR fix in pattern-matching: If the return contract for a pattern-matched function fails, it should NOT try the next pattern-match function. Pattern-matching is only for params, not return values.
3
3
  - raise an error if multiple defns have the same contract for pattern matching.
4
4
 
5
- - New syntax for functions with no input params.
5
+ - New syntax for functions with no input params (the old style still works)
6
6
  Old way:
7
+ ```ruby
7
8
  Contract nil => 1
8
9
  def one
9
-
10
+ ```
10
11
  New way:
12
+ ```
11
13
  Contract 1
12
14
  def one
15
+ ```
13
16
 
14
17
  - Prettier HashOf contract can now be written like this: `HashOf[Num => String]`
15
18
  - Add `SetOf` contract
data/TUTORIAL.md CHANGED
@@ -67,24 +67,40 @@ This can be useful if you're in a REPL and want to figure out how a function sho
67
67
 
68
68
  contracts.ruby comes with a lot of built-in contracts, including the following:
69
69
 
70
- * [`Num`](http://www.rubydoc.info/gems/contracts/Contracts/Num) – checks that the argument is `Numeric`
71
- * [`Pos`](http://www.rubydoc.info/gems/contracts/Contracts/Pos) – checks that the argument is a positive number
72
- * [`Neg`](http://www.rubydoc.info/gems/contracts/Contracts/Neg) – checks that the argument is a negative number
73
- * [`Nat`](http://www.rubydoc.info/gems/contracts/Contracts/Nat) – checks that the argument is a natural number
74
- * [`Bool`](http://www.rubydoc.info/gems/contracts/Contracts/Bool) – checks that the argument is `true` or `false`
75
- * [`Any`](http://www.rubydoc.info/gems/contracts/Contracts/Any) – Passes for any argument. Use when the argument has no constraints.
76
- * [`None`](http://www.rubydoc.info/gems/contracts/Contracts/None) – Fails for any argument. Use when the method takes no arguments.
77
- * [`Or`](http://www.rubydoc.info/gems/contracts/Contracts/Or) – passes if any of the given contracts pass, e.g. `Or[Fixnum, Float]`
78
- * [`Xor`](http://www.rubydoc.info/gems/contracts/Contracts/Xor) – passes if exactly one of the given contracts pass, e.g. `Xor[Fixnum, Float]`
79
- * [`And`](http://www.rubydoc.info/gems/contracts/Contracts/And) – passes if all contracts pass, e.g. `And[Fixnum, Float]`
80
- * [`Not`](http://www.rubydoc.info/gems/contracts/Contracts/Not) – passes if all contracts fail for the given argument, e.g. `Not[nil]`
81
- * [`ArrayOf`](http://www.rubydoc.info/gems/contracts/Contracts/ArrayOf) – checks that the argument is an array, and all elements pass the given contract, e.g. `ArrayOf[Num]`
82
- * [`SetOf`](http://www.rubydoc.info/gems/contracts/Contracts/SetOf) – checks that the argument is a set, and all elements pass the given contract, e.g. `SetOf[Num]`
83
- * [`HashOf`](http://www.rubydoc.info/gems/contracts/Contracts/HashOf) – checks that the argument is a hash, and all keys and values pass the given contract, e.g. `HashOf[Symbol => String]`
84
- * [`Maybe`](http://www.rubydoc.info/gems/contracts/Contracts/Maybe) – passes if the argument is `nil`, or if the given contract passes
85
- * [`RespondTo`](http://www.rubydoc.info/gems/contracts/Contracts/RespondTo) – checks that the argument responds to all of the given methods, e.g. `RespondTo[:password, :credit_card]`
86
- * [`Send`](http://www.rubydoc.info/gems/contracts/Contracts/Send) – checks that all named methods return true, e.g. `Send[:valid?]`
87
- * [`Exactly`](http://www.rubydoc.info/gems/contracts/Contracts/Exactly) – checks that the argument has the given type, not accepting sub-classes, e.g. `Exactly[Numeric]`.
70
+ * Basic types
71
+ * [`Num`](http://www.rubydoc.info/gems/contracts/Contracts/Num) – checks that the argument is `Numeric`
72
+ * [`Pos`](http://www.rubydoc.info/gems/contracts/Contracts/Pos) – checks that the argument is a positive number
73
+ * [`Neg`](http://www.rubydoc.info/gems/contracts/Contracts/Neg) – checks that the argument is a negative number
74
+ * [`Nat`](http://www.rubydoc.info/gems/contracts/Contracts/Nat) – checks that the argument is a natural number (>= 0)
75
+ * [`Bool`](http://www.rubydoc.info/gems/contracts/Contracts/Bool) – checks that the argument is `true` or `false`
76
+ * [`Any`](http://www.rubydoc.info/gems/contracts/Contracts/Any) – Passes for any argument. Use when the argument has no constraints.
77
+ * [`None`](http://www.rubydoc.info/gems/contracts/Contracts/None) – Fails for any argument. Use when the method takes no arguments.
78
+
79
+ * Logical combinations
80
+ * [`Maybe`](http://www.rubydoc.info/gems/contracts/Contracts/Maybe) – specifies that a value _may be_ nil, e.g. `Maybe[String]` (equivalent to `Or[String,nil]`)
81
+ * [`Or`](http://www.rubydoc.info/gems/contracts/Contracts/Or) – passes if any of the given contracts pass, e.g. `Or[Fixnum, Float]`
82
+ * [`Xor`](http://www.rubydoc.info/gems/contracts/Contracts/Xor) – passes if exactly one of the given contracts pass, e.g. `Xor[Fixnum, Float]`
83
+ * [`And`](http://www.rubydoc.info/gems/contracts/Contracts/And) – passes if all contracts pass, e.g. `And[Nat, -> (n) { n.even? }]`
84
+ * [`Not`](http://www.rubydoc.info/gems/contracts/Contracts/Not) – passes if all contracts fail for the given argument, e.g. `Not[nil]`
85
+
86
+ * Collections
87
+ * [`ArrayOf`](http://www.rubydoc.info/gems/contracts/Contracts/ArrayOf) – checks that the argument is an array, and all elements pass the given contract, e.g. `ArrayOf[Num]`
88
+ * [`SetOf`](http://www.rubydoc.info/gems/contracts/Contracts/SetOf) – checks that the argument is a set, and all elements pass the given contract, e.g. `SetOf[Num]`
89
+ * [`HashOf`](http://www.rubydoc.info/gems/contracts/Contracts/HashOf) – checks that the argument is a hash, and all keys and values pass the given contract, e.g. `HashOf[Symbol => String]` or `HashOf[Symbol,String]`
90
+ * [`RangeOf`](http://www.rubydoc.info/gems/contracts/Contracts/RangeOf) – checks that the argument is a range whose elements (#first and #last) pass the given contract, e.g. `RangeOf[Date]`
91
+
92
+ * Keyword arguments
93
+ * [`KeywordArgs`](http://www.rubydoc.info/gems/contracts/Contracts/KeywordArgs) – checks that the argument is an options hash, and all required keyword arguments are present, and all values pass their respective contracts, e.g. `KeywordArgs[:number => Num, :description => Optional[String]]`
94
+ * [`Optional`](http://www.rubydoc.info/gems/contracts/Contracts/Optional) – checks that the keyword argument is either not present or pass the given contract, can not be used outside of `KeywordArgs` contract, e.g. `Optional[Num]`
95
+
96
+ * Duck typing
97
+ * [`RespondTo`](http://www.rubydoc.info/gems/contracts/Contracts/RespondTo) – checks that the argument responds to all of the given methods, e.g. `RespondTo[:password, :credit_card]`
98
+ * [`Send`](http://www.rubydoc.info/gems/contracts/Contracts/Send) – checks that all named methods return a truthy value, e.g. `Send[:valid?]`
99
+
100
+ * Miscellaneous
101
+ * [`Exactly`](http://www.rubydoc.info/gems/contracts/Contracts/Exactly) – checks that the argument has the given type, not accepting sub-classes, e.g. `Exactly[Numeric]`.
102
+ * [`Eq`](http://www.rubydoc.info/gems/contracts/Contracts/Eq) – checks that the argument is precisely equal to the given value, e.g. `Eq[String]` matches the class `String` and not a string instance.
103
+ * [`Func`](http://www.rubydoc.info/gems/contracts/Contracts/Func) – specifies the contract for a proc/lambda e.g. `Contract ArrayOf[Num], Func[Num => Num] => ArrayOf[Num]`. See section "Contracts On Functions".
88
104
 
89
105
  To see all the built-in contracts and their full descriptions, check out the [RDoc](http://rubydoc.info/gems/contracts/Contracts).
90
106
 
@@ -239,6 +255,59 @@ give_largest_value(a: 1, b: 2, c: 3) # returns 3
239
255
  give_largest_value("a" => 1, 2 => 2, c: 3)
240
256
  ```
241
257
 
258
+ ### Contracts On Keyword Arguments
259
+
260
+ ruby 2.0+, but can be used for normal hashes too, when keyword arguments are
261
+ not available
262
+
263
+ Lets say you are writing a simple function and require a bunch of keyword arguments:
264
+
265
+ ```ruby
266
+ def connect(host, port:, user:, password:)
267
+ ```
268
+
269
+ You can of course put `Hash` contract on it:
270
+
271
+ ```ruby
272
+ Contract String, { :port => Num, :user => String, :password => String } => Connection
273
+ def connect(host, port:, user:, password:)
274
+ ```
275
+
276
+ But this will not quite work if you want to have a default values:
277
+
278
+ ```ruby
279
+ Contract String, { :port => Num, :user => String, :password => String } => Connection
280
+ def connect(host, port: 5000, user:, password:)
281
+ # ...
282
+ end
283
+
284
+ # No value is passed for port
285
+ connect("example.org", user: "me", password: "none")
286
+ ```
287
+
288
+ Results in:
289
+
290
+ ```
291
+ ContractError: Contract violation for argument 2 of 2:
292
+ Expected: {:port=>Num, :user=>String, :password=>String},
293
+ Actual: {:user=>"me", :password=>"none"}
294
+ Value guarded in: Object::connect
295
+ With Contract: String, Hash => Connection
296
+ At: (irb):12
297
+ ```
298
+
299
+ This can be fixed with contract `{ :port => Maybe[Num], ... }`, but that will
300
+ allow `nil` to be passed in, which is not the original intent.
301
+
302
+ So that is where `KeywordArgs` and `Optional` contracts jump in:
303
+
304
+ ```ruby
305
+ Contract String, KeywordArgs[ :port => Optional[Num], :user => String, :password => String ] => Connection
306
+ def connect(host, port: 5000, user:, password:)
307
+ ```
308
+
309
+ It looks just like the hash contract, but wrapped in `KeywordArgs` contract. Notice the usage of `Optional` contract - this way you specify that `:port` argument is optional. And it will not fail, when you omit this argument, but it will fail when you pass in `nil`.
310
+
242
311
  ### Contracts On Functions
243
312
 
244
313
  Lets say you are writing a simple map function:
@@ -260,7 +329,7 @@ This says that the second argument should be a `Proc`. You can call the function
260
329
  p map([1, 2, 3], lambda { |x| x + 1 }) # works
261
330
  ```
262
331
 
263
- But suppose you want to have a contract on the Proc too! Suppose you want to make sure that the Proc returns a number. Use the `Func` contract. `Func` takes a contract as it's argument, and uses that contract on the function that you pass in.
332
+ But suppose you want to have a contract on the Proc too! Suppose you want to make sure that the Proc returns a number. Use the `Func` contract. `Func` takes a contract as its argument, and uses that contract on the function that you pass in.
264
333
 
265
334
  Here's a `map` function that requires an array of numbers, and a function that takes a number and returns a number:
266
335
 
@@ -284,6 +353,12 @@ p map([1, 2, 3], lambda { |x| x + 1 }) # works
284
353
  p map([1, 2, 3], lambda { |x| "oops" }) # fails, the lambda returns a string.
285
354
  ```
286
355
 
356
+ The above examples showed a method accepting a `Proc` as the last argument, but the same contract works on methods that accept a block:
357
+
358
+ ```ruby
359
+ def map(arr, &block)
360
+ ```
361
+
287
362
  NOTE: This is not valid:
288
363
 
289
364
  ```ruby
@@ -448,6 +523,36 @@ end
448
523
 
449
524
  If your failure callback returns `false`, the method that the contract is guarding will not be called (the default behaviour).
450
525
 
526
+ ## Providing your own custom validators
527
+
528
+ This can be done with `Contract.override_validator`:
529
+
530
+ ```ruby
531
+ # Make contracts accept all RSpec doubles
532
+ Contract.override_validator(:class) do |contract|
533
+ lambda do |arg|
534
+ arg.is_a?(RSpec::Mocks::Double) ||
535
+ arg.is_a?(contract)
536
+ end
537
+ end
538
+ ```
539
+
540
+ The block you provide should always return lambda accepting one argument - validated argument. Block itself accepts contract as an argument.
541
+
542
+ Possible validator overrides:
543
+
544
+ - `override_validator(MyCustomContract)` - allows to add some special behaviour for custom contracts,
545
+ - `override_validator(Proc)` - e.g. `lambda { true }`,
546
+ - `override_validator(Array)` - e.g. `[Num, String]`,
547
+ - `override_validator(Hash)` - e.g. `{ :a => Num, :b => String }`,
548
+ - `override_validator(Contracts::Args)` - e.g. `Args[Num]`,
549
+ - `override_validator(Contracts::Func)` - e.g. `Func[Num => Num]`,
550
+ - `override_validator(:valid)` - allows to override how contracts that respond to `:valid?` are handled,
551
+ - `override_validator(:class)` - allows to override how class/module contract constants are handled,
552
+ - `override_validator(:default)` - otherwise, raw value contracts.
553
+
554
+ Default validators can be found here: [lib/contracts/validators.rb](https://github.com/egonSchiele/contracts.ruby/blob/master/lib/contracts/validators.rb).
555
+
451
556
  ## Disabling contracts
452
557
 
453
558
  If you want to disable contracts, set the `NO_CONTRACTS` environment variable. This will disable contracts and you won't have a performance hit. Pattern matching will still work if you disable contracts in this way! With NO_CONTRACTS only pattern-matching contracts are defined.
@@ -510,12 +615,11 @@ This is because the first contract eliminated the possibility of `age` being les
510
615
 
511
616
  ## Contracts in modules
512
617
 
513
- To use contracts on module you need to include both `Contracts` and `Contracts::Modules` into it:
618
+ Usage is the same as contracts in classes:
514
619
 
515
620
  ```ruby
516
621
  module M
517
622
  include Contracts
518
- include Contracts::Modules
519
623
 
520
624
  Contract String => String
521
625
  def self.parse
@@ -562,6 +666,17 @@ If you run it, last line will generate invariant violation:
562
666
 
563
667
  Which means, that after `#silly_next_day!` all checks specified in `invariant` statement will be verified, and if at least one fail, then invariant violation error will be raised.
564
668
 
669
+ ## Using contracts within your own code
670
+
671
+ contracts.ruby is obviously designed to check method parameters and return values. But if you want to check whether some other data obeys a contract, you can use `Contract.valid?(value, contract)`. For instance:
672
+
673
+ ```ruby
674
+ data = parse(user_input)
675
+ unless Contract.valid?(data, HashOf[String,Nat])
676
+ raise UserInputError.new(user_input)
677
+ end
678
+ ```
679
+
565
680
  ## Auto-generate documentation using contracts
566
681
 
567
682
  If you are generating documentation for your code with [YARD](http://yardoc.org/), check out [yard-contracts](https://github.com/sfcgeorge/yard-contracts). It will automatically annotate your functions with contracts information. Instead of documenting each parameter for a function yourself, you can just add a contract and yard-contracts will generate the documentation for you!
@@ -0,0 +1,69 @@
1
+ require "./lib/contracts"
2
+ require "benchmark"
3
+ require "rubygems"
4
+ require "method_profiler"
5
+ require "ruby-prof"
6
+
7
+ include Contracts
8
+
9
+ def add opts
10
+ opts[:a] + opts[:b]
11
+ end
12
+
13
+ Contract ({ :a => Num, :b => Num}) => Num
14
+ def contracts_add opts
15
+ opts[:a] + opts[:b]
16
+ end
17
+
18
+ def explicit_add opts
19
+ a = opts[:a]
20
+ b = opts[:b]
21
+ fail unless a.is_a?(Numeric)
22
+ fail unless b.is_a?(Numeric)
23
+ c = a + b
24
+ fail unless c.is_a?(Numeric)
25
+ c
26
+ end
27
+
28
+ def benchmark
29
+ Benchmark.bm 30 do |x|
30
+ x.report "testing add" do
31
+ 1_000_000.times do |_|
32
+ add(:a => rand(1000), :b => rand(1000))
33
+ end
34
+ end
35
+ x.report "testing contracts add" do
36
+ 1_000_000.times do |_|
37
+ contracts_add(:a => rand(1000), :b => rand(1000))
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ def profile
44
+ profilers = []
45
+ profilers << MethodProfiler.observe(Contract)
46
+ profilers << MethodProfiler.observe(Object)
47
+ profilers << MethodProfiler.observe(Contracts::MethodDecorators)
48
+ profilers << MethodProfiler.observe(Contracts::Decorator)
49
+ profilers << MethodProfiler.observe(Contracts::Support)
50
+ profilers << MethodProfiler.observe(UnboundMethod)
51
+ 10_000.times do |_|
52
+ contracts_add(:a => rand(1000), :b => rand(1000))
53
+ end
54
+ profilers.each { |p| puts p.report }
55
+ end
56
+
57
+ def ruby_prof
58
+ RubyProf.start
59
+ 100_000.times do |_|
60
+ contracts_add(:a => rand(1000), :b => rand(1000))
61
+ end
62
+ result = RubyProf.stop
63
+ printer = RubyProf::FlatPrinter.new(result)
64
+ printer.print(STDOUT)
65
+ end
66
+
67
+ benchmark
68
+ profile
69
+ ruby_prof if ENV["FULL_BENCH"] # takes some time
data/lib/contracts.rb CHANGED
@@ -1,12 +1,14 @@
1
1
  require "contracts/builtin_contracts"
2
2
  require "contracts/decorators"
3
- require "contracts/eigenclass"
4
3
  require "contracts/errors"
5
4
  require "contracts/formatters"
6
5
  require "contracts/invariants"
7
6
  require "contracts/method_reference"
8
- require "contracts/modules"
9
7
  require "contracts/support"
8
+ require "contracts/engine"
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)
@@ -18,15 +20,13 @@ module Contracts
18
20
  end
19
21
 
20
22
  def self.common(base)
21
- Eigenclass.lift(base)
22
-
23
23
  return if base.respond_to?(:Contract)
24
24
 
25
25
  base.extend(MethodDecorators)
26
26
 
27
27
  base.instance_eval do
28
28
  def functype(funcname)
29
- contracts = decorated_methods[:class_methods][funcname]
29
+ contracts = Engine.fetch_from(self).decorated_methods_for(:class_methods, funcname)
30
30
  if contracts.nil?
31
31
  "No contract for #{self}.#{funcname}"
32
32
  else
@@ -36,22 +36,14 @@ module Contracts
36
36
  end
37
37
 
38
38
  base.class_eval do
39
- unless base.instance_of?(Module)
40
- def Contract(*args)
41
- return if ENV["NO_CONTRACTS"]
42
- if self.class == Module
43
- puts %{
44
- Warning: You have added a Contract on a module function
45
- without including Contracts::Modules. Your Contract will
46
- just be ignored. Please include Contracts::Modules into
47
- your module.}
48
- end
49
- self.class.Contract(*args)
50
- end
39
+ # TODO: deprecate
40
+ # Required when contracts are included in global scope
41
+ def Contract(*args)
42
+ self.class.Contract(*args)
51
43
  end
52
44
 
53
45
  def functype(funcname)
54
- contracts = self.class.decorated_methods[:instance_methods][funcname]
46
+ contracts = Engine.fetch_from(self.class).decorated_methods_for(:instance_methods, funcname)
55
47
  if contracts.nil?
56
48
  "No contract for #{self.class}.#{funcname}"
57
49
  else
@@ -68,7 +60,15 @@ end
68
60
  # Contract [contract names] => return_value
69
61
  #
70
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
71
68
  class Contract < Contracts::Decorator
69
+ extend Contracts::Validators
70
+ include Contracts::CallWith
71
+
72
72
  # Default implementation of failure_callback. Provided as a block to be able
73
73
  # to monkey patch #failure_callback only temporary and then switch it back.
74
74
  # First important usage - for specs.
@@ -113,8 +113,9 @@ class Contract < Contracts::Decorator
113
113
  # == @has_proc_contract
114
114
  last_contract = args_contracts.last
115
115
  is_a_proc = last_contract.is_a?(Class) && (last_contract <= Proc || last_contract <= Method)
116
+ maybe_a_proc = last_contract.is_a?(Contracts::Maybe) && last_contract.include_proc?
116
117
 
117
- @has_proc_contract = is_a_proc || last_contract.is_a?(Contracts::Func)
118
+ @has_proc_contract = is_a_proc || maybe_a_proc || last_contract.is_a?(Contracts::Func)
118
119
  # ====
119
120
 
120
121
  # == @has_options_contract
@@ -218,54 +219,6 @@ class Contract < Contracts::Decorator
218
219
  make_validator(contract)[arg]
219
220
  end
220
221
 
221
- # This is a little weird. For each contract
222
- # we pre-make a proc to validate it so we
223
- # don't have to go through this decision tree every time.
224
- # Seems silly but it saves us a bunch of time (4.3sec vs 5.2sec)
225
- def self.make_validator(contract)
226
- # if is faster than case!
227
- klass = contract.class
228
- if klass == Proc
229
- # e.g. lambda {true}
230
- contract
231
- elsif klass == Array
232
- # e.g. [Num, String]
233
- # TODO: account for these errors too
234
- lambda do |arg|
235
- return false unless arg.is_a?(Array) && arg.length == contract.length
236
- arg.zip(contract).all? do |_arg, _contract|
237
- Contract.valid?(_arg, _contract)
238
- end
239
- end
240
- elsif klass == Hash
241
- # e.g. { :a => Num, :b => String }
242
- lambda do |arg|
243
- return false unless arg.is_a?(Hash)
244
- contract.keys.all? do |k|
245
- Contract.valid?(arg[k], contract[k])
246
- end
247
- end
248
- elsif klass == Contracts::Args
249
- lambda do |arg|
250
- Contract.valid?(arg, contract.contract)
251
- end
252
- elsif klass == Contracts::Func
253
- lambda do |arg|
254
- arg.is_a?(Method) || arg.is_a?(Proc)
255
- end
256
- else
257
- # classes and everything else
258
- # e.g. Fixnum, Num
259
- if contract.respond_to? :valid?
260
- lambda { |arg| contract.valid?(arg) }
261
- elsif klass == Class
262
- lambda { |arg| arg.is_a?(contract) }
263
- else
264
- lambda { |arg| contract == arg }
265
- end
266
- end
267
- end
268
-
269
222
  def [](*args, &blk)
270
223
  call(*args, &blk)
271
224
  end
@@ -296,95 +249,6 @@ class Contract < Contracts::Decorator
296
249
  end
297
250
  end
298
251
 
299
- def call_with(this, *args, &blk)
300
- args << blk if blk
301
-
302
- # Explicitly append blk=nil if nil != Proc contract violation anticipated
303
- maybe_append_block!(args, blk)
304
-
305
- # Explicitly append options={} if Hash contract is present
306
- maybe_append_options!(args, blk)
307
-
308
- # Loop forward validating the arguments up to the splat (if there is one)
309
- (@args_contract_index || args.size).times do |i|
310
- contract = args_contracts[i]
311
- arg = args[i]
312
- validator = @args_validators[i]
313
-
314
- unless validator && validator[arg]
315
- return unless Contract.failure_callback(:arg => arg,
316
- :contract => contract,
317
- :class => klass,
318
- :method => method,
319
- :contracts => self,
320
- :arg_pos => i+1,
321
- :total_args => args.size,
322
- :return_value => false)
323
- end
324
-
325
- if contract.is_a?(Contracts::Func)
326
- args[i] = Contract.new(klass, arg, *contract.contracts)
327
- end
328
- end
329
-
330
- # If there is a splat loop backwards to the lower index of the splat
331
- # Once we hit the splat in this direction set its upper index
332
- # Keep validating but use this upper index to get the splat validator.
333
- if @args_contract_index
334
- splat_upper_index = @args_contract_index
335
- (args.size - @args_contract_index).times do |i|
336
- arg = args[args.size - 1 - i]
337
-
338
- if args_contracts[args_contracts.size - 1 - i].is_a?(Contracts::Args)
339
- splat_upper_index = i
340
- end
341
-
342
- # Each arg after the spat is found must use the splat validator
343
- j = i < splat_upper_index ? i : splat_upper_index
344
- contract = args_contracts[args_contracts.size - 1 - j]
345
- validator = @args_validators[args_contracts.size - 1 - j]
346
-
347
- unless validator && validator[arg]
348
- return unless Contract.failure_callback(:arg => arg,
349
- :contract => contract,
350
- :class => klass,
351
- :method => method,
352
- :contracts => self,
353
- :arg_pos => i-1,
354
- :total_args => args.size,
355
- :return_value => false)
356
- end
357
-
358
- if contract.is_a?(Contracts::Func)
359
- args[args.size - 1 - i] = Contract.new(klass, arg, *contract.contracts)
360
- end
361
- end
362
- end
363
-
364
- # If we put the block into args for validating, restore the args
365
- args.slice!(-1) if blk
366
- result = if method.respond_to?(:call)
367
- # proc, block, lambda, etc
368
- method.call(*args, &blk)
369
- else
370
- # original method name referrence
371
- method.send_to(this, *args, &blk)
372
- end
373
-
374
- unless @ret_validator[result]
375
- Contract.failure_callback(:arg => result,
376
- :contract => ret_contract,
377
- :class => klass,
378
- :method => method,
379
- :contracts => self,
380
- :return_value => true)
381
- end
382
-
383
- this.verify_invariants!(method) if this.respond_to?(:verify_invariants!)
384
-
385
- result
386
- end
387
-
388
252
  # Used to determine type of failure exception this contract should raise in case of failure
389
253
  def failure_exception
390
254
  if @pattern_match