arel 0.3.3 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/lib/arel/algebra.rb +1 -0
  2. data/lib/arel/algebra/attributes.rb +1 -1
  3. data/lib/arel/algebra/attributes/attribute.rb +95 -7
  4. data/lib/arel/algebra/attributes/integer.rb +1 -1
  5. data/lib/arel/algebra/core_extensions/object.rb +0 -13
  6. data/lib/arel/algebra/header.rb +67 -0
  7. data/lib/arel/algebra/predicates.rb +153 -7
  8. data/lib/arel/algebra/relations/operations/having.rb +11 -7
  9. data/lib/arel/algebra/relations/operations/join.rb +1 -2
  10. data/lib/arel/algebra/relations/operations/project.rb +1 -1
  11. data/lib/arel/algebra/relations/relation.rb +13 -22
  12. data/lib/arel/algebra/relations/utilities/compound.rb +5 -1
  13. data/lib/arel/algebra/relations/utilities/externalization.rb +1 -1
  14. data/lib/arel/engines/memory/predicates.rb +58 -2
  15. data/lib/arel/engines/memory/relations/array.rb +6 -3
  16. data/lib/arel/engines/memory/relations/operations.rb +1 -1
  17. data/lib/arel/engines/sql/attributes.rb +1 -1
  18. data/lib/arel/engines/sql/core_extensions/array.rb +4 -0
  19. data/lib/arel/engines/sql/core_extensions/nil_class.rb +4 -0
  20. data/lib/arel/engines/sql/core_extensions/object.rb +4 -0
  21. data/lib/arel/engines/sql/core_extensions/range.rb +4 -0
  22. data/lib/arel/engines/sql/predicates.rb +48 -2
  23. data/lib/arel/engines/sql/primitives.rb +8 -0
  24. data/lib/arel/engines/sql/relations/compiler.rb +2 -2
  25. data/lib/arel/engines/sql/relations/operations/join.rb +1 -1
  26. data/lib/arel/engines/sql/relations/relation.rb +4 -0
  27. data/lib/arel/engines/sql/relations/table.rb +8 -4
  28. data/lib/arel/version.rb +1 -1
  29. data/spec/algebra/unit/relations/join_spec.rb +1 -2
  30. data/spec/algebra/unit/relations/table_spec.rb +1 -1
  31. data/spec/attributes/boolean_spec.rb +1 -1
  32. data/spec/attributes/float_spec.rb +1 -1
  33. data/spec/attributes/header_spec.rb +42 -0
  34. data/spec/attributes/integer_spec.rb +1 -1
  35. data/spec/attributes/string_spec.rb +1 -1
  36. data/spec/attributes/time_spec.rb +4 -2
  37. data/spec/engines/memory/integration/joins/cross_engine_spec.rb +3 -4
  38. data/spec/engines/sql/unit/predicates/in_spec.rb +23 -5
  39. data/spec/engines/sql/unit/predicates/noteq_spec.rb +75 -0
  40. data/spec/engines/sql/unit/primitives/attribute_spec.rb +0 -19
  41. data/spec/engines/sql/unit/relations/having_spec.rb +33 -0
  42. data/spec/relations/join_spec.rb +5 -3
  43. data/spec/relations/relation_spec.rb +2 -2
  44. data/spec/shared/relation_spec.rb +126 -13
  45. data/spec/support/check.rb +1 -1
  46. data/spec/support/connections/mysql_connection.rb +1 -1
  47. data/spec/support/connections/oracle_connection.rb +1 -1
  48. data/spec/support/connections/postgresql_connection.rb +1 -1
  49. data/spec/support/guards.rb +1 -1
  50. data/spec/support/matchers.rb +1 -1
  51. data/spec/support/matchers/be_like.rb +3 -3
  52. data/spec/support/matchers/have_rows.rb +1 -1
  53. data/spec/support/model.rb +6 -2
  54. metadata +7 -4
data/lib/arel/algebra.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'arel/algebra/core_extensions'
2
2
 
3
3
  require 'arel/algebra/attributes'
4
+ require 'arel/algebra/header'
4
5
  require 'arel/algebra/expression'
5
6
  require 'arel/algebra/ordering'
6
7
  require 'arel/algebra/predicates'
@@ -4,4 +4,4 @@ require "arel/algebra/attributes/decimal"
4
4
  require "arel/algebra/attributes/float"
5
5
  require "arel/algebra/attributes/integer"
6
6
  require "arel/algebra/attributes/string"
7
- require "arel/algebra/attributes/time"
7
+ require "arel/algebra/attributes/time"
@@ -29,7 +29,7 @@ module Arel
29
29
  end
30
30
 
31
31
  def hash
32
- @hash ||= history.size + name.hash + relation.hash
32
+ @hash ||= name.hash + root.relation.hash
33
33
  end
34
34
 
35
35
  def as(aliaz = nil)
@@ -86,32 +86,120 @@ module Arel
86
86
  Predicates::Equality.new(self, other)
87
87
  end
88
88
 
89
- def not(other)
90
- Predicates::Not.new(self, other)
89
+ def eq_any(*others)
90
+ Predicates::Any.build(Predicates::Equality, self, *others)
91
+ end
92
+
93
+ def eq_all(*others)
94
+ Predicates::All.build(Predicates::Equality, self, *others)
95
+ end
96
+
97
+ def not_eq(other)
98
+ Predicates::Inequality.new(self, other)
99
+ end
100
+
101
+ def not_eq_any(*others)
102
+ Predicates::Any.build(Predicates::Inequality, self, *others)
103
+ end
104
+
105
+ def not_eq_all(*others)
106
+ Predicates::All.build(Predicates::Inequality, self, *others)
91
107
  end
92
108
 
93
109
  def lt(other)
94
110
  Predicates::LessThan.new(self, other)
95
111
  end
96
112
 
113
+ def lt_any(*others)
114
+ Predicates::Any.build(Predicates::LessThan, self, *others)
115
+ end
116
+
117
+ def lt_all(*others)
118
+ Predicates::All.build(Predicates::LessThan, self, *others)
119
+ end
120
+
97
121
  def lteq(other)
98
122
  Predicates::LessThanOrEqualTo.new(self, other)
99
123
  end
100
124
 
125
+ def lteq_any(*others)
126
+ Predicates::Any.build(Predicates::LessThanOrEqualTo, self, *others)
127
+ end
128
+
129
+ def lteq_all(*others)
130
+ Predicates::All.build(Predicates::LessThanOrEqualTo, self, *others)
131
+ end
132
+
101
133
  def gt(other)
102
134
  Predicates::GreaterThan.new(self, other)
103
135
  end
104
136
 
137
+ def gt_any(*others)
138
+ Predicates::Any.build(Predicates::GreaterThan, self, *others)
139
+ end
140
+
141
+ def gt_all(*others)
142
+ Predicates::All.build(Predicates::GreaterThan, self, *others)
143
+ end
144
+
105
145
  def gteq(other)
106
146
  Predicates::GreaterThanOrEqualTo.new(self, other)
107
147
  end
108
148
 
109
- def matches(regexp)
110
- Predicates::Match.new(self, regexp)
149
+ def gteq_any(*others)
150
+ Predicates::Any.build(Predicates::GreaterThanOrEqualTo, self, *others)
151
+ end
152
+
153
+ def gteq_all(*others)
154
+ Predicates::All.build(Predicates::GreaterThanOrEqualTo, self, *others)
155
+ end
156
+
157
+ def matches(other)
158
+ Predicates::Match.new(self, other)
159
+ end
160
+
161
+ def matches_any(*others)
162
+ Predicates::Any.build(Predicates::Match, self, *others)
163
+ end
164
+
165
+ def matches_all(*others)
166
+ Predicates::All.build(Predicates::Match, self, *others)
167
+ end
168
+
169
+ def not_matches(other)
170
+ Predicates::NotMatch.new(self, other)
171
+ end
172
+
173
+ def not_matches_any(*others)
174
+ Predicates::Any.build(Predicates::NotMatch, self, *others)
175
+ end
176
+
177
+ def not_matches_all(*others)
178
+ Predicates::All.build(Predicates::NotMatch, self, *others)
179
+ end
180
+
181
+ def in(other)
182
+ Predicates::In.new(self, other)
183
+ end
184
+
185
+ def in_any(*others)
186
+ Predicates::Any.build(Predicates::In, self, *others)
187
+ end
188
+
189
+ def in_all(*others)
190
+ Predicates::All.build(Predicates::In, self, *others)
191
+ end
192
+
193
+ def not_in(other)
194
+ Predicates::NotIn.new(self, other)
195
+ end
196
+
197
+ def not_in_any(*others)
198
+ Predicates::Any.build(Predicates::NotIn, self, *others)
111
199
  end
112
200
 
113
- def in(array)
114
- Predicates::In.new(self, array)
201
+ def not_in_all(*others)
202
+ Predicates::All.build(Predicates::NotIn, self, *others)
115
203
  end
116
204
  end
117
205
  include Predications
@@ -7,4 +7,4 @@ module Arel
7
7
  end
8
8
  end
9
9
  end
10
-
10
+
@@ -12,19 +12,6 @@ module Arel
12
12
  yield(self)
13
13
  end
14
14
 
15
- # TODO remove this when ActiveSupport beta1 is out.
16
- # Returns the object's singleton class.
17
- def singleton_class
18
- class << self
19
- self
20
- end
21
- end unless respond_to?(:singleton_class)
22
-
23
- # class_eval on an object acts like singleton_class_eval.
24
- def class_eval(*args, &block)
25
- singleton_class.class_eval(*args, &block)
26
- end
27
-
28
15
  Object.send(:include, self)
29
16
  end
30
17
  end
@@ -0,0 +1,67 @@
1
+ module Arel
2
+ class Header
3
+ include Enumerable
4
+
5
+ def initialize(attrs = [])
6
+ @attributes = attrs.to_ary
7
+ @names = Hash.new do |h,k|
8
+ h[k] = @attributes.detect { |a| a.named?(k) }
9
+ end
10
+ end
11
+
12
+ def each(&block)
13
+ to_ary.each(&block)
14
+ self
15
+ end
16
+
17
+ def [](key)
18
+ case key
19
+ when String, Symbol then find_by_name(key)
20
+ when Attribute then find_by_attribute(key)
21
+ end
22
+ end
23
+
24
+ def ==(other)
25
+ to_set == other.to_set
26
+ end
27
+
28
+ def union(other)
29
+ new(to_ary | other)
30
+ end
31
+
32
+ alias | union
33
+
34
+ def to_ary
35
+ @attributes
36
+ end
37
+
38
+ def bind(relation)
39
+ Header.new(map { |a| a.bind(relation) })
40
+ end
41
+
42
+ # TMP
43
+ def index(i)
44
+ to_ary.index(i)
45
+ end
46
+
47
+ private
48
+
49
+ def new(attrs)
50
+ self.class.new(attrs)
51
+ end
52
+
53
+ def matching(attribute)
54
+ select { |a| !a.is_a?(Value) && a.root == attribute.root }
55
+ end
56
+
57
+ def find_by_name(name)
58
+ @names[name.to_sym]
59
+ end
60
+
61
+ def find_by_attribute(attr)
62
+ matching(attr).max do |a, b|
63
+ (a.original_attribute / attr) <=> (b.original_attribute / attr)
64
+ end
65
+ end
66
+ end
67
+ end
@@ -8,6 +8,82 @@ module Arel
8
8
  def and(other_predicate)
9
9
  And.new(self, other_predicate)
10
10
  end
11
+
12
+ def complement
13
+ Not.new(self)
14
+ end
15
+
16
+ def not
17
+ self.complement
18
+ end
19
+ end
20
+
21
+ class Polyadic < Predicate
22
+ attributes :predicates
23
+
24
+ def initialize(*predicates)
25
+ @predicates = predicates
26
+ end
27
+
28
+ # Build a Polyadic predicate based on:
29
+ # * <tt>operator</tt> - The Predicate subclass that defines the type of operation
30
+ # (LessThan, Equality, etc)
31
+ # * <tt>operand1</tt> - The left-hand operand (normally an Arel::Attribute)
32
+ # * <tt>additional_operands</tt> - All possible right-hand operands
33
+ def self.build(operator, operand1, *additional_operands)
34
+ new(
35
+ *additional_operands.uniq.inject([]) do |predicates, operand|
36
+ predicates << operator.new(operand1, operand)
37
+ end
38
+ )
39
+ end
40
+
41
+ def ==(other)
42
+ same_elements?(@predicates, other.predicates)
43
+ end
44
+
45
+ def bind(relation)
46
+ self.class.new(
47
+ *predicates.map {|p| p.find_correlate_in(relation)}
48
+ )
49
+ end
50
+
51
+ private
52
+
53
+ def same_elements?(a1, a2)
54
+ [:select, :inject, :size].each do |m|
55
+ return false unless [a1, a2].each {|a| a.respond_to?(m) }
56
+ end
57
+ a1.inject({}) { |h,e| h[e] = a1.select { |i| i == e }.size; h } ==
58
+ a2.inject({}) { |h,e| h[e] = a2.select { |i| i == e }.size; h }
59
+ end
60
+ end
61
+
62
+ class Any < Polyadic
63
+ def complement
64
+ All.new(*predicates.map {|p| p.complement})
65
+ end
66
+ end
67
+
68
+ class All < Polyadic
69
+ def complement
70
+ Any.new(*predicates.map {|p| p.complement})
71
+ end
72
+ end
73
+
74
+ class Unary < Predicate
75
+ attributes :operand
76
+ deriving :initialize, :==
77
+
78
+ def bind(relation)
79
+ self.class.new(operand.find_correlate_in(relation))
80
+ end
81
+ end
82
+
83
+ class Not < Unary
84
+ def complement
85
+ operand
86
+ end
11
87
  end
12
88
 
13
89
  class Binary < Predicate
@@ -25,20 +101,90 @@ module Arel
25
101
  end
26
102
  end
27
103
 
104
+ class CompoundPredicate < Binary; end
105
+
106
+ class And < CompoundPredicate
107
+ def complement
108
+ Or.new(operand1.complement, operand2.complement)
109
+ end
110
+ end
111
+
112
+ class Or < CompoundPredicate
113
+ def complement
114
+ And.new(operand1.complement, operand2.complement)
115
+ end
116
+ end
117
+
28
118
  class Equality < Binary
29
119
  def ==(other)
30
120
  Equality === other and
31
121
  ((operand1 == other.operand1 and operand2 == other.operand2) or
32
122
  (operand1 == other.operand2 and operand2 == other.operand1))
33
123
  end
124
+
125
+ def complement
126
+ Inequality.new(operand1, operand2)
127
+ end
34
128
  end
35
129
 
36
- class Not < Binary; end
37
- class GreaterThanOrEqualTo < Binary; end
38
- class GreaterThan < Binary; end
39
- class LessThanOrEqualTo < Binary; end
40
- class LessThan < Binary; end
41
- class Match < Binary; end
42
- class In < Binary; end
130
+ class Inequality < Binary
131
+ def ==(other)
132
+ Equality === other and
133
+ ((operand1 == other.operand1 and operand2 == other.operand2) or
134
+ (operand1 == other.operand2 and operand2 == other.operand1))
135
+ end
136
+
137
+ def complement
138
+ Equality.new(operand1, operand2)
139
+ end
140
+ end
141
+
142
+ class GreaterThanOrEqualTo < Binary
143
+ def complement
144
+ LessThan.new(operand1, operand2)
145
+ end
146
+ end
147
+
148
+ class GreaterThan < Binary
149
+ def complement
150
+ LessThanOrEqualTo.new(operand1, operand2)
151
+ end
152
+ end
153
+
154
+ class LessThanOrEqualTo < Binary
155
+ def complement
156
+ GreaterThan.new(operand1, operand2)
157
+ end
158
+ end
159
+
160
+ class LessThan < Binary
161
+ def complement
162
+ GreaterThanOrEqualTo.new(operand1, operand2)
163
+ end
164
+ end
165
+
166
+ class Match < Binary
167
+ def complement
168
+ NotMatch.new(operand1, operand2)
169
+ end
170
+ end
171
+
172
+ class NotMatch < Binary
173
+ def complement
174
+ Match.new(operand1, operand2)
175
+ end
176
+ end
177
+
178
+ class In < Binary
179
+ def complement
180
+ NotIn.new(operand1, operand2)
181
+ end
182
+ end
183
+
184
+ class NotIn < Binary
185
+ def complement
186
+ In.new(operand1, operand2)
187
+ end
188
+ end
43
189
  end
44
190
  end
@@ -1,13 +1,17 @@
1
1
  module Arel
2
2
  class Having < Compound
3
- attributes :relation, :havings
4
- deriving :==
3
+ attributes :relation, :predicates
4
+ deriving :==
5
+ requires :restricting
5
6
 
6
- def initialize(relation, *havings, &block)
7
- @relation = relation
8
- @havings = (havings + arguments_from_block(relation, &block)) \
9
- .collect { |g| g.bind(relation) }
7
+ def initialize(relation, *predicates, &block)
8
+ predicates = [yield(relation)] + predicates if block_given?
9
+ @predicates = predicates.map { |p| p.bind(relation) }
10
+ @relation = relation
11
+ end
12
+
13
+ def havings
14
+ @havings ||= relation.havings + predicates
10
15
  end
11
16
  end
12
17
  end
13
-