contracts 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/contracts.rb +41 -3
- data/lib/foo.rb +6 -0
- data/lib/test.rb +41 -0
- metadata +5 -3
data/lib/contracts.rb
CHANGED
@@ -5,6 +5,7 @@ class Class
|
|
5
5
|
include MethodDecorators
|
6
6
|
end
|
7
7
|
|
8
|
+
|
8
9
|
class Contract < Decorator
|
9
10
|
attr_accessor :contracts, :klass, :method
|
10
11
|
decorator_name :contract
|
@@ -23,8 +24,13 @@ class Contract < Decorator
|
|
23
24
|
def self.failure_msg(data)
|
24
25
|
# TODO __file__ and __line__ won't work in Ruby 1.9.
|
25
26
|
# It provides a source_location method instead.
|
27
|
+
expected = if data[:contract].to_s == ""
|
28
|
+
data[:contract].inspect
|
29
|
+
else
|
30
|
+
data[:contract].to_s
|
31
|
+
end
|
26
32
|
%{Contract violation:
|
27
|
-
Expected: #{
|
33
|
+
Expected: #{expected},
|
28
34
|
Actual: #{data[:arg].inspect}
|
29
35
|
Value guarded in: #{data[:class]}::#{data[:method].name}
|
30
36
|
With Contract: #{data[:contracts].map { |t| t.is_a?(Class) ? t.name : t.class.name }.join(", ") }
|
@@ -58,6 +64,21 @@ class Contract < Decorator
|
|
58
64
|
end
|
59
65
|
|
60
66
|
def self.validate_all(args, contracts, klass, method)
|
67
|
+
if args.size > contracts.size - 1
|
68
|
+
# *args
|
69
|
+
if contracts[-2].is_a? Args
|
70
|
+
while contracts.size < args.size + 1
|
71
|
+
contracts.insert(-2, contracts[-2].dup)
|
72
|
+
end
|
73
|
+
else
|
74
|
+
raise %{The number of arguments doesn't match the number of contracts.
|
75
|
+
Did you forget to write a contract for the return value of the function?
|
76
|
+
Or if you want a variable number of arguments using *args, use the Args contract.
|
77
|
+
Args: #{args.inspect}
|
78
|
+
Contracts: #{contracts.map { |t| t.is_a?(Class) ? t.name : t.class.name }.join(", ")}}
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
61
82
|
args.zip(contracts).each do |arg, contract|
|
62
83
|
validate(arg, contract, klass, method, contracts)
|
63
84
|
end
|
@@ -76,16 +97,22 @@ class Contract < Decorator
|
|
76
97
|
def self.valid?(arg, contract)
|
77
98
|
case contract
|
78
99
|
when Class
|
100
|
+
# e.g. Fixnum
|
79
101
|
validate_class arg, contract
|
80
102
|
when Proc
|
103
|
+
# e.g. lambda {true}
|
81
104
|
validate_proc arg, contract
|
82
105
|
when Array
|
106
|
+
# e.g. [Num, String]
|
83
107
|
# TODO account for these errors too
|
84
108
|
return mkerror(false, arg, contract) unless arg.is_a?(Array)
|
85
109
|
validate_all(arg, contract)
|
86
110
|
when Hash
|
111
|
+
# e.g. { :a => Num, :b => String }
|
87
112
|
return mkerror(false, arg, contract) unless arg.is_a?(Hash)
|
88
113
|
validate_hash(arg, contract)
|
114
|
+
when Args
|
115
|
+
valid? arg, contract.contract
|
89
116
|
else
|
90
117
|
if contract.respond_to? :valid?
|
91
118
|
mkerror(contract.valid?(arg), arg, contract)
|
@@ -95,12 +122,23 @@ class Contract < Decorator
|
|
95
122
|
end
|
96
123
|
end
|
97
124
|
|
98
|
-
def call(this, *args)
|
125
|
+
def call(this, *args, &blk)
|
99
126
|
Contract.validate_all(args, @contracts, @klass, @method)
|
100
|
-
result = @method.bind(this).call(*args)
|
127
|
+
result = @method.bind(this).call(*args, &blk)
|
101
128
|
if args.size == @contracts.size - 1
|
102
129
|
Contract.validate(result, @contracts[-1], @klass, @method, @contracts)
|
103
130
|
end
|
104
131
|
result
|
105
132
|
end
|
106
133
|
end
|
134
|
+
|
135
|
+
class Args < Contracts::CallableClass
|
136
|
+
attr_reader :contract
|
137
|
+
def initialize(contract)
|
138
|
+
@contract = contract
|
139
|
+
end
|
140
|
+
|
141
|
+
def to_s
|
142
|
+
"Args[#{@contract}]"
|
143
|
+
end
|
144
|
+
end
|
data/lib/foo.rb
ADDED
data/lib/test.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'contracts'
|
2
|
+
include Contracts
|
3
|
+
|
4
|
+
class Object
|
5
|
+
Contract Num, Num
|
6
|
+
def double(x)
|
7
|
+
x * 2
|
8
|
+
end
|
9
|
+
|
10
|
+
# bug: the `b` here doesn't get typechecked and throws an error.
|
11
|
+
Contract Num, Num, Num
|
12
|
+
def add(a, b="hello!")
|
13
|
+
a + b
|
14
|
+
end
|
15
|
+
|
16
|
+
Contract Proc, nil
|
17
|
+
def run(&blk)
|
18
|
+
puts "running:"
|
19
|
+
blk.call
|
20
|
+
end
|
21
|
+
|
22
|
+
Contract Method, Num
|
23
|
+
def call(func)
|
24
|
+
func.call
|
25
|
+
end
|
26
|
+
|
27
|
+
# thinks there are too many args, throws error
|
28
|
+
# sidenote: there should be a check to make sure the # of args and contracts match up.
|
29
|
+
Contract Args[Num], Num
|
30
|
+
def sum(*vals)
|
31
|
+
vals.inject(0) do |acc, v|
|
32
|
+
acc + v
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
run {
|
38
|
+
puts "hi!"
|
39
|
+
}
|
40
|
+
|
41
|
+
puts add(1, 2)
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: contracts
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 27
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 2
|
10
|
+
version: 0.0.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Aditya Bhargava
|
@@ -31,6 +31,8 @@ files:
|
|
31
31
|
- lib/builtin_contracts.rb
|
32
32
|
- lib/contracts.rb
|
33
33
|
- lib/decorators.rb
|
34
|
+
- lib/foo.rb
|
35
|
+
- lib/test.rb
|
34
36
|
has_rdoc: true
|
35
37
|
homepage: http://github.com/egonSchiele/contracts.ruby
|
36
38
|
licenses: []
|