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
data/lib/arel/algebra.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'arel/algebra/core_extensions'
2
2
 
3
3
  require 'arel/algebra/attributes'
4
+ require 'arel/algebra/header'
4
5
  require 'arel/algebra/expression'
5
6
  require 'arel/algebra/ordering'
6
7
  require 'arel/algebra/predicates'
@@ -4,4 +4,4 @@ require "arel/algebra/attributes/decimal"
4
4
  require "arel/algebra/attributes/float"
5
5
  require "arel/algebra/attributes/integer"
6
6
  require "arel/algebra/attributes/string"
7
- require "arel/algebra/attributes/time"
7
+ require "arel/algebra/attributes/time"
@@ -29,7 +29,7 @@ module Arel
29
29
  end
30
30
 
31
31
  def hash
32
- @hash ||= history.size + name.hash + relation.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 not(other)
90
- Predicates::Not.new(self, other)
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 matches(regexp)
110
- Predicates::Match.new(self, regexp)
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 in(array)
114
- Predicates::In.new(self, array)
201
+ def not_in_all(*others)
202
+ Predicates::All.build(Predicates::NotIn, self, *others)
115
203
  end
116
204
  end
117
205
  include Predications
@@ -7,4 +7,4 @@ module Arel
7
7
  end
8
8
  end
9
9
  end
10
-
10
+
@@ -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 Not < Binary; end
37
- class GreaterThanOrEqualTo < Binary; end
38
- class GreaterThan < Binary; end
39
- class LessThanOrEqualTo < Binary; end
40
- class LessThan < Binary; end
41
- class Match < Binary; end
42
- class In < Binary; end
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, :havings
4
- deriving :==
3
+ attributes :relation, :predicates
4
+ deriving :==
5
+ requires :restricting
5
6
 
6
- def initialize(relation, *havings, &block)
7
- @relation = relation
8
- @havings = (havings + arguments_from_block(relation, &block)) \
9
- .collect { |g| g.bind(relation) }
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
-