arel 0.3.3 → 0.4.0
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/lib/arel/algebra.rb +1 -0
- data/lib/arel/algebra/attributes.rb +1 -1
- data/lib/arel/algebra/attributes/attribute.rb +95 -7
- data/lib/arel/algebra/attributes/integer.rb +1 -1
- data/lib/arel/algebra/core_extensions/object.rb +0 -13
- data/lib/arel/algebra/header.rb +67 -0
- data/lib/arel/algebra/predicates.rb +153 -7
- data/lib/arel/algebra/relations/operations/having.rb +11 -7
- data/lib/arel/algebra/relations/operations/join.rb +1 -2
- data/lib/arel/algebra/relations/operations/project.rb +1 -1
- data/lib/arel/algebra/relations/relation.rb +13 -22
- data/lib/arel/algebra/relations/utilities/compound.rb +5 -1
- data/lib/arel/algebra/relations/utilities/externalization.rb +1 -1
- data/lib/arel/engines/memory/predicates.rb +58 -2
- data/lib/arel/engines/memory/relations/array.rb +6 -3
- data/lib/arel/engines/memory/relations/operations.rb +1 -1
- data/lib/arel/engines/sql/attributes.rb +1 -1
- data/lib/arel/engines/sql/core_extensions/array.rb +4 -0
- data/lib/arel/engines/sql/core_extensions/nil_class.rb +4 -0
- data/lib/arel/engines/sql/core_extensions/object.rb +4 -0
- data/lib/arel/engines/sql/core_extensions/range.rb +4 -0
- data/lib/arel/engines/sql/predicates.rb +48 -2
- data/lib/arel/engines/sql/primitives.rb +8 -0
- data/lib/arel/engines/sql/relations/compiler.rb +2 -2
- data/lib/arel/engines/sql/relations/operations/join.rb +1 -1
- data/lib/arel/engines/sql/relations/relation.rb +4 -0
- data/lib/arel/engines/sql/relations/table.rb +8 -4
- data/lib/arel/version.rb +1 -1
- data/spec/algebra/unit/relations/join_spec.rb +1 -2
- data/spec/algebra/unit/relations/table_spec.rb +1 -1
- data/spec/attributes/boolean_spec.rb +1 -1
- data/spec/attributes/float_spec.rb +1 -1
- data/spec/attributes/header_spec.rb +42 -0
- data/spec/attributes/integer_spec.rb +1 -1
- data/spec/attributes/string_spec.rb +1 -1
- data/spec/attributes/time_spec.rb +4 -2
- data/spec/engines/memory/integration/joins/cross_engine_spec.rb +3 -4
- data/spec/engines/sql/unit/predicates/in_spec.rb +23 -5
- data/spec/engines/sql/unit/predicates/noteq_spec.rb +75 -0
- data/spec/engines/sql/unit/primitives/attribute_spec.rb +0 -19
- data/spec/engines/sql/unit/relations/having_spec.rb +33 -0
- data/spec/relations/join_spec.rb +5 -3
- data/spec/relations/relation_spec.rb +2 -2
- data/spec/shared/relation_spec.rb +126 -13
- data/spec/support/check.rb +1 -1
- data/spec/support/connections/mysql_connection.rb +1 -1
- data/spec/support/connections/oracle_connection.rb +1 -1
- data/spec/support/connections/postgresql_connection.rb +1 -1
- data/spec/support/guards.rb +1 -1
- data/spec/support/matchers.rb +1 -1
- data/spec/support/matchers/be_like.rb +3 -3
- data/spec/support/matchers/have_rows.rb +1 -1
- data/spec/support/model.rb +6 -2
- metadata +7 -4
data/lib/arel/algebra.rb
CHANGED
@@ -29,7 +29,7 @@ module Arel
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def hash
|
32
|
-
@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
|
90
|
-
Predicates::
|
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
|
110
|
-
Predicates::
|
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
|
114
|
-
Predicates::
|
201
|
+
def not_in_all(*others)
|
202
|
+
Predicates::All.build(Predicates::NotIn, self, *others)
|
115
203
|
end
|
116
204
|
end
|
117
205
|
include Predications
|
@@ -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
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
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, :
|
4
|
-
deriving
|
3
|
+
attributes :relation, :predicates
|
4
|
+
deriving :==
|
5
|
+
requires :restricting
|
5
6
|
|
6
|
-
def initialize(relation, *
|
7
|
-
|
8
|
-
@
|
9
|
-
|
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
|
-
|