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