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
@@ -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
|
|