arel 0.3.3 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -19,8 +19,7 @@ module Arel
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def attributes
|
22
|
-
@attributes ||= (relation1.externalize.attributes
|
23
|
-
relation2.externalize.attributes).collect { |a| a.bind(self) }
|
22
|
+
@attributes ||= (relation1.externalize.attributes | relation2.externalize.attributes).bind(self)
|
24
23
|
end
|
25
24
|
|
26
25
|
def wheres
|
@@ -84,16 +84,7 @@ module Arel
|
|
84
84
|
|
85
85
|
module AttributeAccessable
|
86
86
|
def [](index)
|
87
|
-
|
88
|
-
@cached_attributes[index] ||= case index
|
89
|
-
when Symbol, String
|
90
|
-
find_attribute_matching_name(index)
|
91
|
-
when Attribute, Expression
|
92
|
-
find_attribute_matching_attribute(index)
|
93
|
-
when ::Array
|
94
|
-
# TESTME
|
95
|
-
index.collect { |i| self[i] }
|
96
|
-
end
|
87
|
+
attributes[index]
|
97
88
|
end
|
98
89
|
|
99
90
|
def find_attribute_matching_name(name)
|
@@ -127,18 +118,18 @@ module Arel
|
|
127
118
|
include AttributeAccessable
|
128
119
|
|
129
120
|
module DefaultOperations
|
130
|
-
def attributes;
|
131
|
-
def projections; []
|
132
|
-
def wheres; []
|
133
|
-
def orders; []
|
134
|
-
def inserts; []
|
135
|
-
def groupings; []
|
136
|
-
def havings; []
|
137
|
-
def joins(formatter = nil); nil
|
138
|
-
def taken; nil
|
139
|
-
def skipped; nil
|
140
|
-
def sources; []
|
141
|
-
def locked; []
|
121
|
+
def attributes; Header.new end
|
122
|
+
def projections; [] end
|
123
|
+
def wheres; [] end
|
124
|
+
def orders; [] end
|
125
|
+
def inserts; [] end
|
126
|
+
def groupings; [] end
|
127
|
+
def havings; [] end
|
128
|
+
def joins(formatter = nil); nil end # FIXME
|
129
|
+
def taken; nil end
|
130
|
+
def skipped; nil end
|
131
|
+
def sources; [] end
|
132
|
+
def locked; [] end
|
142
133
|
end
|
143
134
|
include DefaultOperations
|
144
135
|
end
|
@@ -13,7 +13,7 @@ module Arel
|
|
13
13
|
@requires
|
14
14
|
end
|
15
15
|
|
16
|
-
[:
|
16
|
+
[:wheres, :groupings, :orders, :havings, :projections].each do |operation_name|
|
17
17
|
class_eval <<-OPERATION, __FILE__, __LINE__
|
18
18
|
def #{operation_name}
|
19
19
|
@#{operation_name} ||= relation.#{operation_name}.collect { |o| o.bind(self) }
|
@@ -21,6 +21,10 @@ module Arel
|
|
21
21
|
OPERATION
|
22
22
|
end
|
23
23
|
|
24
|
+
def attributes
|
25
|
+
@attributes ||= relation.attributes.bind(self)
|
26
|
+
end
|
27
|
+
|
24
28
|
def hash
|
25
29
|
@hash ||= :relation.hash
|
26
30
|
end
|
@@ -6,11 +6,53 @@ module Arel
|
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
9
|
+
class Unary < Predicate
|
10
|
+
def eval(row)
|
11
|
+
operand.eval(row).send(operator)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Not < Unary
|
16
|
+
def eval(row)
|
17
|
+
!operand.eval(row)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Polyadic < Predicate
|
22
|
+
def eval(row)
|
23
|
+
predicates.send(compounder) do |operation|
|
24
|
+
operation.eval(row)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Any < Polyadic
|
30
|
+
def compounder; :any? end
|
31
|
+
end
|
32
|
+
|
33
|
+
class All < Polyadic
|
34
|
+
def compounder; :all? end
|
35
|
+
end
|
36
|
+
|
37
|
+
class CompoundPredicate < Binary
|
38
|
+
def eval(row)
|
39
|
+
eval "operand1.eval(row) #{operator} operand2.eval(row)"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class Or < CompoundPredicate
|
44
|
+
def operator; :or end
|
45
|
+
end
|
46
|
+
|
47
|
+
class And < CompoundPredicate
|
48
|
+
def operator; :and end
|
49
|
+
end
|
50
|
+
|
9
51
|
class Equality < Binary
|
10
52
|
def operator; :== end
|
11
53
|
end
|
12
54
|
|
13
|
-
class
|
55
|
+
class Inequality < Binary
|
14
56
|
def eval(row)
|
15
57
|
operand1.eval(row) != operand2.eval(row)
|
16
58
|
end
|
@@ -36,8 +78,22 @@ module Arel
|
|
36
78
|
def operator; :=~ end
|
37
79
|
end
|
38
80
|
|
81
|
+
class NotMatch < Binary
|
82
|
+
def eval(row)
|
83
|
+
operand1.eval(row) !~ operand2.eval(row)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
39
87
|
class In < Binary
|
40
|
-
def
|
88
|
+
def eval(row)
|
89
|
+
operand2.eval(row).include?(operand1.eval(row))
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class NotIn < Binary
|
94
|
+
def eval(row)
|
95
|
+
!(operand2.eval(row).include?(operand1.eval(row)))
|
96
|
+
end
|
41
97
|
end
|
42
98
|
end
|
43
99
|
end
|
@@ -15,9 +15,12 @@ module Arel
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def attributes
|
18
|
-
@attributes ||=
|
19
|
-
|
20
|
-
|
18
|
+
@attributes ||= begin
|
19
|
+
attrs = @attribute_names_and_types.collect do |attribute, type|
|
20
|
+
attribute = type.new(self, attribute) if Symbol === attribute
|
21
|
+
attribute
|
22
|
+
end
|
23
|
+
Header.new(attrs)
|
21
24
|
end
|
22
25
|
end
|
23
26
|
|
@@ -8,7 +8,7 @@ module Arel
|
|
8
8
|
class Order < Compound
|
9
9
|
def eval
|
10
10
|
unoperated_rows.sort do |row1, row2|
|
11
|
-
ordering =
|
11
|
+
ordering = orders.detect { |o| o.eval(row1, row2) != 0 } || orders.last
|
12
12
|
ordering.eval(row1, row2)
|
13
13
|
end
|
14
14
|
end
|
@@ -6,6 +6,16 @@ module Arel
|
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
9
|
+
class Unary < Predicate
|
10
|
+
def to_sql(formatter = nil)
|
11
|
+
"#{predicate_sql} (#{operand.to_sql(formatter)})"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Not < Unary
|
16
|
+
def predicate_sql; "NOT" end
|
17
|
+
end
|
18
|
+
|
9
19
|
class CompoundPredicate < Binary
|
10
20
|
def to_sql(formatter = nil)
|
11
21
|
"(#{operand1.to_sql(formatter)} #{predicate_sql} #{operand2.to_sql(formatter)})"
|
@@ -20,14 +30,32 @@ module Arel
|
|
20
30
|
def predicate_sql; "AND" end
|
21
31
|
end
|
22
32
|
|
33
|
+
class Polyadic < Predicate
|
34
|
+
def to_sql(formatter = nil)
|
35
|
+
"(" +
|
36
|
+
predicates.map {|p| p.to_sql(formatter)}.join(" #{predicate_sql} ") +
|
37
|
+
")"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Any < Polyadic
|
42
|
+
def predicate_sql; "OR" end
|
43
|
+
end
|
44
|
+
|
45
|
+
class All < Polyadic
|
46
|
+
def predicate_sql; "AND" end
|
47
|
+
end
|
48
|
+
|
23
49
|
class Equality < Binary
|
24
50
|
def predicate_sql
|
25
51
|
operand2.equality_predicate_sql
|
26
52
|
end
|
27
53
|
end
|
28
54
|
|
29
|
-
class
|
30
|
-
def predicate_sql
|
55
|
+
class Inequality < Binary
|
56
|
+
def predicate_sql
|
57
|
+
operand2.inequality_predicate_sql
|
58
|
+
end
|
31
59
|
end
|
32
60
|
|
33
61
|
class GreaterThanOrEqualTo < Binary
|
@@ -50,8 +78,26 @@ module Arel
|
|
50
78
|
def predicate_sql; 'LIKE' end
|
51
79
|
end
|
52
80
|
|
81
|
+
class NotMatch < Binary
|
82
|
+
def predicate_sql; 'NOT LIKE' end
|
83
|
+
end
|
84
|
+
|
53
85
|
class In < Binary
|
86
|
+
def to_sql(formatter = nil)
|
87
|
+
if operand2.is_a?(Range) && operand2.exclude_end?
|
88
|
+
GreaterThanOrEqualTo.new(operand1, operand2.begin).and(
|
89
|
+
LessThan.new(operand1, operand2.end)
|
90
|
+
).to_sql(formatter)
|
91
|
+
else
|
92
|
+
super
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
54
96
|
def predicate_sql; operand2.inclusion_predicate_sql end
|
55
97
|
end
|
98
|
+
|
99
|
+
class NotIn < Binary
|
100
|
+
def predicate_sql; operand2.exclusion_predicate_sql end
|
101
|
+
end
|
56
102
|
end
|
57
103
|
end
|
@@ -30,10 +30,18 @@ module Arel
|
|
30
30
|
value.inclusion_predicate_sql
|
31
31
|
end
|
32
32
|
|
33
|
+
def exclusion_predicate_sql
|
34
|
+
value.exclusion_predicate_sql
|
35
|
+
end
|
36
|
+
|
33
37
|
def equality_predicate_sql
|
34
38
|
value.equality_predicate_sql
|
35
39
|
end
|
36
40
|
|
41
|
+
def inequality_predicate_sql
|
42
|
+
value.inequality_predicate_sql
|
43
|
+
end
|
44
|
+
|
37
45
|
def to_sql(formatter = Sql::WhereCondition.new(relation))
|
38
46
|
formatter.value value
|
39
47
|
end
|
@@ -12,9 +12,9 @@ module Arel
|
|
12
12
|
"SELECT #{select_clauses.join(', ')}",
|
13
13
|
"FROM #{from_clauses}",
|
14
14
|
(joins(self) unless joins(self).blank? ),
|
15
|
-
("WHERE #{where_clauses.join(
|
15
|
+
("WHERE #{where_clauses.join(' AND ')}" unless wheres.blank? ),
|
16
16
|
("GROUP BY #{group_clauses.join(', ')}" unless groupings.blank? ),
|
17
|
-
("HAVING #{having_clauses.join('
|
17
|
+
("HAVING #{having_clauses.join(' AND ')}" unless havings.blank? ),
|
18
18
|
("ORDER BY #{order_clauses.join(', ')}" unless orders.blank? )
|
19
19
|
engine.add_limit_offset!(query,{ :limit => taken, :offset => skipped }) if taken || skipped
|
20
20
|
query << " #{locked}" unless locked.blank?
|
@@ -10,7 +10,7 @@ module Arel
|
|
10
10
|
join_sql,
|
11
11
|
relation2.externalize.table_sql(formatter),
|
12
12
|
("ON" unless predicates.blank?),
|
13
|
-
(ons + relation2.externalize.wheres).collect { |p| p.bind(environment).to_sql(Sql::WhereClause.new(environment)) }.join(' AND ')
|
13
|
+
(ons + relation2.externalize.wheres).collect { |p| p.bind(environment.relation).to_sql(Sql::WhereClause.new(environment)) }.join(' AND ')
|
14
14
|
].compact.join(" ")
|
15
15
|
[relation1.joins(environment), this_join, relation2.joins(environment)].compact.join(" ")
|
16
16
|
end
|
@@ -22,6 +22,10 @@ module Arel
|
|
22
22
|
"IN"
|
23
23
|
end
|
24
24
|
|
25
|
+
def exclusion_predicate_sql
|
26
|
+
"NOT IN"
|
27
|
+
end
|
28
|
+
|
25
29
|
def primary_key
|
26
30
|
connection_id = engine.connection.object_id
|
27
31
|
if @@connection_tables_primary_keys[connection_id] && @@connection_tables_primary_keys[connection_id].has_key?(table.name)
|
@@ -42,11 +42,14 @@ module Arel
|
|
42
42
|
def attributes
|
43
43
|
return @attributes if defined?(@attributes)
|
44
44
|
if table_exists?
|
45
|
-
@attributes
|
46
|
-
|
45
|
+
@attributes ||= begin
|
46
|
+
attrs = columns.collect do |column|
|
47
|
+
Sql::Attributes.for(column).new(column, self, column.name.to_sym)
|
48
|
+
end
|
49
|
+
Header.new(attrs)
|
47
50
|
end
|
48
51
|
else
|
49
|
-
|
52
|
+
Header.new
|
50
53
|
end
|
51
54
|
end
|
52
55
|
|
@@ -67,7 +70,8 @@ module Arel
|
|
67
70
|
end
|
68
71
|
|
69
72
|
def reset
|
70
|
-
@
|
73
|
+
@columns = nil
|
74
|
+
@attributes = Header.new([])
|
71
75
|
end
|
72
76
|
|
73
77
|
def ==(other)
|
data/lib/arel/version.rb
CHANGED
@@ -18,8 +18,7 @@ module Arel
|
|
18
18
|
describe '#attributes' do
|
19
19
|
it 'combines the attributes of the two relations' do
|
20
20
|
join = InnerJoin.new(@relation1, @relation2, @predicate)
|
21
|
-
join.attributes.should ==
|
22
|
-
(@relation1.attributes + @relation2.attributes).collect { |a| a.bind(join) }
|
21
|
+
join.attributes.should == (@relation1.attributes | @relation2.attributes).bind(join)
|
23
22
|
end
|
24
23
|
end
|
25
24
|
end
|
@@ -9,7 +9,7 @@ module Arel
|
|
9
9
|
describe '[]' do
|
10
10
|
describe 'when given a', Symbol do
|
11
11
|
it "manufactures an attribute if the symbol names an attribute within the relation" do
|
12
|
-
check @relation[:id].should ==
|
12
|
+
check @relation[:id].should == Attributes::Integer.new(@relation, :id)
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|