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
@@ -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
@@ -10,7 +10,7 @@ module Arel
10
10
  end
11
11
 
12
12
  def attributes
13
- @attributes ||= projections.collect { |p| p.bind(self) }
13
+ @attributes ||= Header.new(projections).bind(self)
14
14
  end
15
15
 
16
16
  def externalizable?
@@ -84,16 +84,7 @@ module Arel
84
84
 
85
85
  module AttributeAccessable
86
86
  def [](index)
87
- @cached_attributes ||= {}
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; [] end
131
- def projections; [] end
132
- def wheres; [] end
133
- def orders; [] end
134
- def inserts; [] end
135
- def groupings; [] end
136
- def havings; [] end
137
- def joins(formatter = nil); nil end # FIXME
138
- def taken; nil end
139
- def skipped; nil end
140
- def sources; [] end
141
- def locked; [] end
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
- [:attributes, :wheres, :groupings, :orders, :havings, :projections].each do |operation_name|
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
@@ -8,7 +8,7 @@ module Arel
8
8
  end
9
9
 
10
10
  def attributes
11
- @attributes ||= relation.attributes.collect { |a| a.to_attribute(self) }
11
+ @attributes ||= Header.new(relation.attributes.map { |a| a.to_attribute(self) })
12
12
  end
13
13
  end
14
14
 
@@ -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 Not < Binary
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 operator; :include? end
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 ||= @attribute_names_and_types.collect do |attribute, type|
19
- attribute = type.new(self, attribute) if Symbol === attribute
20
- attribute
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 = orderings.detect { |o| o.eval(row1, row2) != 0 } || orderings.last
11
+ ordering = orders.detect { |o| o.eval(row1, row2) != 0 } || orders.last
12
12
  ordering.eval(row1, row2)
13
13
  end
14
14
  end
@@ -37,4 +37,4 @@ module Arel
37
37
  end
38
38
  end
39
39
  end
40
- end
40
+ end
@@ -13,6 +13,10 @@ module Arel
13
13
  "IN"
14
14
  end
15
15
 
16
+ def exclusion_predicate_sql
17
+ "NOT IN"
18
+ end
19
+
16
20
  Array.send(:include, self)
17
21
  end
18
22
  end
@@ -5,6 +5,10 @@ module Arel
5
5
  'IS'
6
6
  end
7
7
 
8
+ def inequality_predicate_sql
9
+ 'IS NOT'
10
+ end
11
+
8
12
  NilClass.send(:include, self)
9
13
  end
10
14
  end
@@ -9,6 +9,10 @@ module Arel
9
9
  '='
10
10
  end
11
11
 
12
+ def inequality_predicate_sql
13
+ '!='
14
+ end
15
+
12
16
  Object.send(:include, self)
13
17
  end
14
18
  end
@@ -9,6 +9,10 @@ module Arel
9
9
  "BETWEEN"
10
10
  end
11
11
 
12
+ def exclusion_predicate_sql
13
+ "NOT BETWEEN"
14
+ end
15
+
12
16
  Range.send(:include, self)
13
17
  end
14
18
  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 Not < Binary
30
- def predicate_sql; '!=' end
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(" AND ")}" unless wheres.blank? ),
15
+ ("WHERE #{where_clauses.join(' AND ')}" unless wheres.blank? ),
16
16
  ("GROUP BY #{group_clauses.join(', ')}" unless groupings.blank? ),
17
- ("HAVING #{having_clauses.join(', ')}" unless havings.blank? ),
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 = columns.collect do |column|
46
- Sql::Attributes.for(column).new(column, self, column.name.to_sym)
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
- @attributes = @columns = nil
73
+ @columns = nil
74
+ @attributes = Header.new([])
71
75
  end
72
76
 
73
77
  def ==(other)
data/lib/arel/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Arel
2
- VERSION = "0.3.3" unless defined?(Arel::VERSION)
2
+ VERSION = "0.4.0" unless defined?(Arel::VERSION)
3
3
  end
@@ -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 == Attribute.new(@relation, :id)
12
+ check @relation[:id].should == Attributes::Integer.new(@relation, :id)
13
13
  end
14
14
  end
15
15