contracts 0.3 → 0.4

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.
@@ -218,22 +218,23 @@ module Contracts
218
218
  end
219
219
  end
220
220
 
221
- # Takes a class +A+. If argument.is_a? +A+, the contract passes.
222
- # Example: <tt>IsA[Numeric]</tt>
223
- class IsA < CallableClass
221
+ # Takes a class +A+. If argument is an object of type +A+, the contract passes.
222
+ # If it is a subclass of A (or not related to A in any way), it fails.
223
+ # Example: <tt>Exactly[Numeric]</tt>
224
+ class Exactly < CallableClass
224
225
  def initialize(cls)
225
226
  @cls = cls
226
227
  end
227
228
 
228
229
  def valid?(val)
229
- val.is_a? @cls
230
+ val.class == @cls
230
231
  end
231
232
 
232
233
  def to_s
233
- "a #{@cls.inspect}"
234
+ "exactly #{@cls.inspect}"
234
235
  end
235
236
  end
236
-
237
+
237
238
  # Takes a variable number of contracts. The contract
238
239
  # passes if all of those contracts fail for the given argument.
239
240
  # Example: <tt>Not[nil]</tt>
@@ -313,6 +314,27 @@ module Contracts
313
314
  end
314
315
  end
315
316
 
317
+ # Use this to specify the Hash characteristics. Takes two contracts,
318
+ # one for hash keys and one for hash values.
319
+ # Example: <tt>HashOf[Symbol, String]</tt>
320
+ class HashOf < CallableClass
321
+ def initialize(key, value)
322
+ @key = key
323
+ @value = value
324
+ end
325
+
326
+ def valid?(hash)
327
+ keys_match = hash.keys.map {|k| Contract.valid?(k, @key) }.all?
328
+ vals_match = hash.values.map {|v| Contract.valid?(v, @value) }.all?
329
+
330
+ [keys_match, vals_match].all?
331
+ end
332
+
333
+ def to_s
334
+ "Hash<#{@key.to_s}, #{@value.to_s}>"
335
+ end
336
+ end
337
+
316
338
  # Takes a Contract.
317
339
  # The contract passes if the contract passes or the given value is nil.
318
340
  # Maybe(foo) is equivalent to Or[foo, nil].
@@ -21,7 +21,7 @@ module Contracts
21
21
  if contracts.nil?
22
22
  "No contract for #{self}.#{funcname}"
23
23
  else
24
- "#{funcname} :: #{contracts}"
24
+ "#{funcname} :: #{contracts[0]}"
25
25
  end
26
26
  end
27
27
  end
@@ -35,7 +35,7 @@ module Contracts
35
35
  if contracts.nil?
36
36
  "No contract for #{self.class}.#{funcname}"
37
37
  else
38
- "#{funcname} :: #{contracts}"
38
+ "#{funcname} :: #{contracts[0]}"
39
39
  end
40
40
  end
41
41
  end
@@ -100,7 +100,14 @@ class Contract < Decorator
100
100
  position = file + ":" + line.to_s
101
101
  end
102
102
  method_name = data[:method].is_a?(Proc) ? "Proc" : data[:method].name
103
- %{Contract violation:
103
+
104
+ header = if data[:return_value]
105
+ "Contract violation for return value:"
106
+ else
107
+ "Contract violation for argument #{data[:arg_pos]} of #{data[:total_args]}:"
108
+ end
109
+
110
+ %{#{header}
104
111
  Expected: #{expected},
105
112
  Actual: #{data[:arg].inspect}
106
113
  Value guarded in: #{data[:class]}::#{method_name}
@@ -177,7 +184,7 @@ class Contract < Decorator
177
184
  if contract.respond_to? :valid?
178
185
  lambda { |arg| contract.valid?(arg) }
179
186
  elsif klass == Class
180
- lambda { |arg| contract == arg.class }
187
+ lambda { |arg| arg.is_a?(contract) }
181
188
  else
182
189
  lambda { |arg| contract == arg }
183
190
  end
@@ -204,14 +211,14 @@ class Contract < Decorator
204
211
  j = i < last_index ? i : last_index
205
212
  #unless true #@args_contracts[i].valid?(args[i])
206
213
  unless @args_validators[j][_args[i]]
207
- call_function = Contract.failure_callback({:arg => _args[i], :contract => @args_contracts[j], :class => @klass, :method => @method, :contracts => self})
214
+ call_function = Contract.failure_callback({:arg => _args[i], :contract => @args_contracts[j], :class => @klass, :method => @method, :contracts => self, :arg_pos => i+1, :total_args => _args.size})
208
215
  return unless call_function
209
216
  end
210
217
  end
211
218
 
212
219
  if @has_func_contracts
213
220
  # contracts on methods
214
- contracts.each_with_index do |contract, i|
221
+ @args_contracts.each_with_index do |contract, i|
215
222
  if contract.is_a? Contracts::Func
216
223
  args[i] = Contract.new(@klass, args[i], *contract.contracts)
217
224
  end
@@ -226,7 +233,7 @@ class Contract < Decorator
226
233
  @method.call(*args, &blk)
227
234
  end
228
235
  unless @ret_validator[result]
229
- Contract.failure_callback({:arg => result, :contract => @ret_contract, :class => @klass, :method => @method, :contracts => self})
236
+ Contract.failure_callback({:arg => result, :contract => @ret_contract, :class => @klass, :method => @method, :contracts => self, :return_value => true})
230
237
  end
231
238
  result
232
239
  end
@@ -40,12 +40,14 @@ module MethodDecorators
40
40
  decorator = klass.new(self, method(name), *args)
41
41
  @decorated_methods[:class_methods][name] ||= []
42
42
  @decorated_methods[:class_methods][name] << decorator
43
- is_private = self.private_methods.include?(name.to_s)
43
+ # private_instance_methods is an array of strings on 1.8 and an array of symbols on 1.9
44
+ is_private = self.private_methods.include?(name) || self.private_methods.include?(name.to_s)
44
45
  else
45
46
  decorator = klass.new(self, instance_method(name), *args)
46
47
  @decorated_methods[:instance_methods][name] ||= []
47
48
  @decorated_methods[:instance_methods][name] << decorator
48
- is_private = self.private_instance_methods.include?(name.to_s)
49
+ # private_instance_methods is an array of strings on 1.8 and an array of symbols on 1.9
50
+ is_private = self.private_instance_methods.include?(name) || self.private_instance_methods.include?(name.to_s)
49
51
  end
50
52
  end
51
53
 
@@ -53,9 +55,46 @@ module MethodDecorators
53
55
  # just calls the decorator passing in all args that were to be passed into the method.
54
56
  # The decorator in turn has a reference to the actual method, so it can call it
55
57
  # on its own, after doing it's decorating of course.
56
- class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
58
+
59
+ =begin
60
+ Very important: THe line `current = #{self}` in the start is crucial.
61
+ Not having it means that any method that used contracts could NOT use `super`
62
+ (see this issue for example: https://github.com/egonSchiele/contracts.ruby/issues/27).
63
+ Here's why: Suppose you have this code:
64
+
65
+ class Foo
66
+ Contract nil => String
67
+ def to_s
68
+ "Foo"
69
+ end
70
+ end
71
+
72
+ class Bar < Foo
73
+ Contract nil => String
74
+ def to_s
75
+ super + "Bar"
76
+ end
77
+ end
78
+
79
+ b = Bar.new
80
+ p b.to_s
81
+
82
+ `to_s` in Bar calls `super`. So you expect this to call `Foo`'s to_s. However,
83
+ we have overwritten the function (that's what this next defn is). So it gets a
84
+ reference to the function to call by looking at `decorated_methods`.
85
+
86
+ Now, this line used to read something like:
87
+
88
+ current = self#{is_class_method ? "" : ".class"}
89
+
90
+ In that case, `self` would always be `Bar`, regardless of whether you were calling
91
+ Foo's to_s or Bar's to_s. So you would keep getting Bar's decorated_methods, which
92
+ means you would always call Bar's to_s...infinite recursion! Instead, you want to
93
+ call Foo's version of decorated_methods. So the line needs to be `current = #{self}`.
94
+ =end
95
+ method_def = %{
57
96
  def #{is_class_method ? "self." : ""}#{name}(*args, &blk)
58
- current = self#{is_class_method ? "" : ".class"}
97
+ current = #{self}
59
98
  ancestors = current.ancestors
60
99
  ancestors.shift # first one is just the class itself
61
100
  while current && !current.respond_to?(:decorated_methods) || current.decorated_methods.nil?
@@ -86,7 +125,9 @@ module MethodDecorators
86
125
  result
87
126
  end
88
127
  #{is_private ? "private #{name.inspect}" : ""}
89
- ruby_eval
128
+ }
129
+
130
+ class_eval method_def, __FILE__, __LINE__ + 1
90
131
  end
91
132
 
92
133
  def decorate(klass, *args)
metadata CHANGED
@@ -1,12 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: contracts
3
3
  version: !ruby/object:Gem::Version
4
- hash: 13
4
+ hash: 3
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 3
9
- version: "0.3"
8
+ - 4
9
+ version: "0.4"
10
10
  platform: ruby
11
11
  authors:
12
12
  - Aditya Bhargava
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2012-06-07 00:00:00 Z
17
+ date: 2014-05-08 00:00:00 Z
18
18
  dependencies: []
19
19
 
20
20
  description: This library provides contracts for Ruby. Contracts let you clearly express how your code behaves, and free you from writing tons of boilerplate, defensive code.