ruby_contracts 0.2.2 → 0.2.3
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.
- data/README.md +2 -1
- data/lib/ruby_contracts/version.rb +1 -1
- data/lib/ruby_contracts.rb +54 -34
- metadata +2 -2
data/README.md
CHANGED
@@ -42,7 +42,8 @@ Without the environment variable `ENABLE_ASSERTION` you have zero overhead and z
|
|
42
42
|
|
43
43
|
When you inherit a class that contains contracts, all contracts must be satified by the subclass.
|
44
44
|
|
45
|
-
If you override a method, you still have to satisfy the existing
|
45
|
+
If you override a method, you still have to satisfy the existing postconditions for this method and you can add some others.
|
46
|
+
This means that postconditions groups are AND-ed and preconditions groups are OR-ed by inheritance.
|
46
47
|
|
47
48
|
See the [issue #1](https://github.com/nicoolas25/ruby_contracts/issues/1) for an example.
|
48
49
|
|
data/lib/ruby_contracts.rb
CHANGED
@@ -3,6 +3,13 @@ require "ruby_contracts/version"
|
|
3
3
|
module Contracts
|
4
4
|
class Error < Exception ; end
|
5
5
|
|
6
|
+
# When it is used with @__contracts_for:
|
7
|
+
# before key must contain a disjunction of conjunction with preconditions
|
8
|
+
# after key must contain a conjunction with postconditions
|
9
|
+
#
|
10
|
+
# When it is used with @__contracts:
|
11
|
+
# before key must contain a conjunction
|
12
|
+
# after key must contain a conjunction
|
6
13
|
def self.empty_contracts
|
7
14
|
{:before => [], :after => []}
|
8
15
|
end
|
@@ -27,16 +34,17 @@ module Contracts
|
|
27
34
|
def __contracts_for(name, current_contracts=nil)
|
28
35
|
inherited_contracts = ancestors[1..-1].reduce(Contracts.empty_contracts) do |c, klass|
|
29
36
|
ancestor_hash = klass.instance_variable_get('@__contracts_for') || {}
|
30
|
-
c[:before]
|
31
|
-
c[:after] += ancestor_hash
|
37
|
+
c[:before] << ancestor_hash[name][:before] if ancestor_hash.has_key?(name)
|
38
|
+
c[:after] += ancestor_hash[name][:after] if ancestor_hash.has_key?(name)
|
32
39
|
c
|
33
40
|
end
|
34
|
-
current_contracts = @__contracts_for[name] || current_contracts || Contracts.empty_contracts
|
35
41
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
42
|
+
if current_contracts
|
43
|
+
inherited_contracts[:before] << current_contracts[:before] unless current_contracts[:before].empty?
|
44
|
+
inherited_contracts[:after] += current_contracts[:after] unless current_contracts[:after].empty?
|
45
|
+
end
|
46
|
+
|
47
|
+
inherited_contracts
|
40
48
|
end
|
41
49
|
|
42
50
|
def __contract_failure!(name, message, result, *args)
|
@@ -61,45 +69,55 @@ module Contracts
|
|
61
69
|
super
|
62
70
|
|
63
71
|
return unless ENV['ENABLE_ASSERTION']
|
72
|
+
return if @__skip_other_contracts_definitions
|
64
73
|
return if @__contracts_for.has_key?(name)
|
65
74
|
|
66
75
|
__contracts = @__contracts_for[name] ||= __contracts_for(name, @__contracts)
|
67
76
|
@__contracts = Contracts.empty_contracts
|
68
77
|
|
69
78
|
if !__contracts[:before].empty? || !__contracts[:after].empty?
|
79
|
+
@__skip_other_contracts_definitions = true
|
70
80
|
original_method_name = "#{name}__with_contracts"
|
71
81
|
define_method(original_method_name, instance_method(name))
|
72
82
|
|
73
83
|
count = 0
|
74
|
-
before_contracts = __contracts[:before].reduce("") do |code,
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
84
|
+
before_contracts = __contracts[:before].reduce("__before_contracts_disjunction = []\n") do |code, contracts_disjunction|
|
85
|
+
contracts_conjunction = contracts_disjunction.reduce("__before_contracts_conjunction = []\n") do |code, contract|
|
86
|
+
type, *args = contract
|
87
|
+
case type
|
88
|
+
when :type
|
89
|
+
classes = args[0]
|
90
|
+
code << "if __args.size < #{classes.size} then\n"
|
91
|
+
code << " __before_contracts_conjunction << ['#{name}', \"need at least #{classes.size} arguments (%i given)\" % [__args.size], nil, *args]\n"
|
92
|
+
code << "else\n"
|
93
|
+
conditions = []
|
94
|
+
classes.each_with_index{ |klass, i| conditions << "__args[#{i}].kind_of?(#{klass})" }
|
95
|
+
code << " if !(#{conditions.join(' && ')}) then\n"
|
96
|
+
code << " __before_contracts_conjunction << ['#{name}', 'input type error', nil, *__args]\n"
|
97
|
+
code << " end\n"
|
98
|
+
code << "end\n"
|
99
|
+
code
|
100
|
+
|
101
|
+
when :params
|
102
|
+
# Define a method that verify the assertion
|
103
|
+
contract_method_name = "__verify_contract_#{name}_in_#{count = count + 1}"
|
104
|
+
define_method(contract_method_name) { |*params| self.instance_exec(*params, &args[1]) }
|
105
|
+
|
106
|
+
code << "if !#{contract_method_name}(*__args) then\n"
|
107
|
+
code << " __before_contracts_conjunction << ['#{name}', \"invalid precondition: #{args[0]}\", nil, *__args]\n"
|
108
|
+
code << "end\n"
|
109
|
+
code
|
110
|
+
else
|
111
|
+
code
|
112
|
+
end
|
101
113
|
end
|
114
|
+
code << contracts_conjunction
|
115
|
+
code << "__before_contracts_disjunction << __before_contracts_conjunction\n"
|
116
|
+
code
|
102
117
|
end
|
118
|
+
before_contracts << "if __before_contracts_disjunction.any?{|conj| !conj.empty?} then\n"
|
119
|
+
before_contracts << " self.class.__contract_failure!(*__before_contracts_disjunction.first.first)\n"
|
120
|
+
before_contracts << "end\n"
|
103
121
|
|
104
122
|
after_contracts = __contracts[:after].reduce("") do |code, contract|
|
105
123
|
type, *args = contract
|
@@ -134,6 +152,8 @@ module Contracts
|
|
134
152
|
EOM
|
135
153
|
|
136
154
|
class_eval method
|
155
|
+
|
156
|
+
@__skip_other_contracts_definitions = false
|
137
157
|
end
|
138
158
|
end
|
139
159
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_contracts
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-06-
|
12
|
+
date: 2013-06-13 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: Micro DSL to add pre & post condition to methods. It try to bring some
|
15
15
|
design by contract in the Ruby world.
|