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 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 contracts for this method and you can add some others.
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
 
@@ -1,3 +1,3 @@
1
1
  module RubyContracts
2
- VERSION = "0.2.2"
2
+ VERSION = "0.2.3"
3
3
  end
@@ -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] += ancestor_hash.has_key?(name) ? ancestor_hash[name][:before] : []
31
- c[:after] += ancestor_hash.has_key?(name) ? ancestor_hash[name][:after] : []
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
- contracts = Contracts.empty_contracts
37
- contracts[:before] = current_contracts[:before] + inherited_contracts[:before]
38
- contracts[:after] = current_contracts[:after] + inherited_contracts[:after]
39
- contracts
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, contract|
75
- type, *args = contract
76
- case type
77
- when :type
78
- classes = args[0]
79
- code << "if __args.size < #{classes.size} then\n"
80
- code << " self.class.__contract_failure!('#{name}', \"need at least #{classes.size} arguments (%i given)\" % [__args.size], nil, *args)\n"
81
- code << "else\n"
82
- conditions = []
83
- classes.each_with_index{ |klass, i| conditions << "__args[#{i}].kind_of?(#{klass})" }
84
- code << " if !(#{conditions.join(' && ')}) then\n"
85
- code << " self.class.__contract_failure!('#{name}', 'input type error', nil, *__args)\n"
86
- code << " end\n"
87
- code << "end\n"
88
- code
89
-
90
- when :params
91
- # Define a method that verify the assertion
92
- contract_method_name = "__verify_contract_#{name}_in_#{count = count + 1}"
93
- define_method(contract_method_name) { |*params| self.instance_exec(*params, &args[1]) }
94
-
95
- code << "if !#{contract_method_name}(*__args) then\n"
96
- code << " self.class.__contract_failure!('#{name}', \"invalid precondition: #{args[0]}\", nil, *__args)\n"
97
- code << "end\n"
98
- code
99
- else
100
- code
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.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-11 00:00:00.000000000 Z
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.