arel 0.4.0 → 1.0.0.rc1

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 (86) hide show
  1. data/README.markdown +24 -0
  2. data/lib/arel.rb +3 -1
  3. data/lib/arel/algebra/attributes/attribute.rb +175 -141
  4. data/lib/arel/algebra/core_extensions.rb +0 -1
  5. data/lib/arel/algebra/core_extensions/hash.rb +5 -9
  6. data/lib/arel/algebra/core_extensions/object.rb +0 -4
  7. data/lib/arel/algebra/expression.rb +37 -24
  8. data/lib/arel/algebra/header.rb +5 -6
  9. data/lib/arel/algebra/ordering.rb +13 -5
  10. data/lib/arel/algebra/predicates.rb +143 -27
  11. data/lib/arel/algebra/relations.rb +0 -1
  12. data/lib/arel/algebra/relations/operations/from.rb +10 -2
  13. data/lib/arel/algebra/relations/operations/group.rb +8 -6
  14. data/lib/arel/algebra/relations/operations/having.rb +3 -6
  15. data/lib/arel/algebra/relations/operations/join.rb +52 -18
  16. data/lib/arel/algebra/relations/operations/lock.rb +4 -6
  17. data/lib/arel/algebra/relations/operations/order.rb +11 -7
  18. data/lib/arel/algebra/relations/operations/project.rb +10 -10
  19. data/lib/arel/algebra/relations/operations/skip.rb +10 -3
  20. data/lib/arel/algebra/relations/operations/take.rb +10 -3
  21. data/lib/arel/algebra/relations/operations/where.rb +12 -6
  22. data/lib/arel/algebra/relations/relation.rb +161 -92
  23. data/lib/arel/algebra/relations/row.rb +8 -5
  24. data/lib/arel/algebra/relations/utilities/compound.rb +34 -33
  25. data/lib/arel/algebra/relations/utilities/externalization.rb +10 -8
  26. data/lib/arel/algebra/relations/writes.rb +24 -13
  27. data/lib/arel/algebra/value.rb +41 -2
  28. data/lib/arel/engines/memory.rb +0 -2
  29. data/lib/arel/engines/memory/engine.rb +3 -9
  30. data/lib/arel/engines/memory/relations.rb +0 -3
  31. data/lib/arel/engines/memory/relations/array.rb +5 -3
  32. data/lib/arel/engines/memory/relations/operations.rb +2 -60
  33. data/lib/arel/engines/sql.rb +0 -2
  34. data/lib/arel/engines/sql/christener.rb +12 -6
  35. data/lib/arel/engines/sql/compilers/oracle_compiler.rb +34 -23
  36. data/lib/arel/engines/sql/compilers/postgresql_compiler.rb +23 -15
  37. data/lib/arel/engines/sql/engine.rb +19 -27
  38. data/lib/arel/engines/sql/formatters.rb +26 -10
  39. data/lib/arel/engines/sql/relations.rb +0 -7
  40. data/lib/arel/engines/sql/relations/compiler.rb +70 -35
  41. data/lib/arel/engines/sql/relations/table.rb +44 -32
  42. data/lib/arel/{engines/sql/relations/utilities/recursion.rb → recursion/base_case.rb} +0 -0
  43. data/lib/arel/session.rb +24 -40
  44. data/lib/arel/sql_literal.rb +13 -0
  45. data/lib/arel/version.rb +1 -1
  46. data/spec/algebra/unit/predicates/inequality_spec.rb +32 -0
  47. data/spec/algebra/unit/predicates/predicate_spec.rb +22 -0
  48. data/spec/algebra/unit/primitives/attribute_spec.rb +3 -9
  49. data/spec/algebra/unit/primitives/expression_spec.rb +1 -7
  50. data/spec/algebra/unit/relations/join_spec.rb +0 -7
  51. data/spec/algebra/unit/relations/project_spec.rb +3 -3
  52. data/spec/algebra/unit/relations/relation_spec.rb +74 -25
  53. data/spec/algebra/unit/session/session_spec.rb +7 -7
  54. data/spec/engines/memory/integration/joins/cross_engine_spec.rb +20 -10
  55. data/spec/engines/memory/unit/relations/array_spec.rb +6 -5
  56. data/spec/engines/memory/unit/relations/join_spec.rb +7 -6
  57. data/spec/engines/memory/unit/relations/order_spec.rb +7 -6
  58. data/spec/engines/memory/unit/relations/project_spec.rb +6 -6
  59. data/spec/engines/memory/unit/relations/skip_spec.rb +10 -5
  60. data/spec/engines/memory/unit/relations/take_spec.rb +7 -5
  61. data/spec/engines/memory/unit/relations/where_spec.rb +13 -9
  62. data/spec/engines/sql/unit/engine_spec.rb +20 -0
  63. data/spec/engines/sql/unit/relations/group_spec.rb +2 -2
  64. data/spec/engines/sql/unit/relations/order_spec.rb +5 -5
  65. data/spec/engines/sql/unit/relations/project_spec.rb +4 -4
  66. data/spec/engines/sql/unit/relations/table_spec.rb +0 -7
  67. data/spec/engines/sql/unit/relations/take_spec.rb +26 -0
  68. data/spec/engines/sql/unit/relations/where_spec.rb +1 -1
  69. data/spec/spec_helper.rb +1 -4
  70. data/spec/sql/christener_spec.rb +70 -0
  71. data/spec/support/model.rb +7 -2
  72. metadata +109 -23
  73. data/lib/arel/algebra/core_extensions/class.rb +0 -32
  74. data/lib/arel/algebra/relations/operations/alias.rb +0 -7
  75. data/lib/arel/engines/memory/predicates.rb +0 -99
  76. data/lib/arel/engines/memory/primitives.rb +0 -27
  77. data/lib/arel/engines/memory/relations/compound.rb +0 -9
  78. data/lib/arel/engines/memory/relations/writes.rb +0 -7
  79. data/lib/arel/engines/sql/predicates.rb +0 -103
  80. data/lib/arel/engines/sql/primitives.rb +0 -97
  81. data/lib/arel/engines/sql/relations/operations/alias.rb +0 -5
  82. data/lib/arel/engines/sql/relations/operations/join.rb +0 -33
  83. data/lib/arel/engines/sql/relations/relation.rb +0 -65
  84. data/lib/arel/engines/sql/relations/utilities/compound.rb +0 -10
  85. data/lib/arel/engines/sql/relations/utilities/externalization.rb +0 -14
  86. data/lib/arel/engines/sql/relations/writes.rb +0 -19
data/README.markdown CHANGED
@@ -180,3 +180,27 @@ As you can see, we're completely missing data for user with id 3. `dumpty` has n
180
180
  FROM users
181
181
  LEFT OUTER JOIN (SELECT user_id, count(*) as cnt FROM photos GROUP BY user_id) AS photos_aggregation
182
182
  ON photos_aggregation.user_id = users.id
183
+
184
+ ## ActiveRecord Adapter Support
185
+
186
+ Arel provides built-in support for the following ActiveRecord DB adapters:
187
+
188
+ * IBM DB
189
+ * MySQL
190
+ * Oracle
191
+ * PostgreSQL
192
+ * SQLite
193
+
194
+ You can add support for other adapters by defining a SQL compiler at the appropriate place. Here's an example: let's say
195
+ you've got a YourSQL adapter. Create a file at `yoursql/arel_compiler.rb` somewhere on the load path (presumably in
196
+ the YourSQL adapter gem). The contents of that file can be as simple as:
197
+
198
+ module Arel
199
+ module SqlCompiler
200
+ class YourSQLCompiler < GenericCompiler
201
+ end
202
+ end
203
+ end
204
+
205
+ Override any methods as necessary. See examples at
206
+ [lib/arel/engines/sql/compilers](http://github.com/rails/arel/tree/master/lib/arel/engines/sql/compilers/).
data/lib/arel.rb CHANGED
@@ -1,10 +1,12 @@
1
1
  require 'active_support/inflector'
2
- require 'active_support/core_ext/class/attribute_accessors'
3
2
  require 'active_support/core_ext/module/delegation'
4
3
  require 'active_support/core_ext/object/blank'
5
4
 
5
+ require 'arel/recursion/base_case'
6
+
6
7
  module Arel
7
8
  require 'arel/algebra'
9
+ require 'arel/sql_literal'
8
10
  require 'arel/engines'
9
11
  require 'arel/version'
10
12
 
@@ -3,14 +3,42 @@ require 'set'
3
3
  module Arel
4
4
  class TypecastError < StandardError ; end
5
5
  class Attribute
6
- attributes :relation, :name, :alias, :ancestor
7
- deriving :==
8
- delegate :engine, :christener, :to => :relation
6
+ attr_reader :relation, :name, :alias, :ancestor, :hash
7
+ attr_reader :history
9
8
 
10
9
  def initialize(relation, name, options = {})
11
- @relation, @name, @alias, @ancestor = relation, name, options[:alias], options[:ancestor]
10
+ @relation = relation # this is actually a table (I think)
11
+ @name = name
12
+ @alias = options[:alias]
13
+ @ancestor = options[:ancestor]
14
+ @history = [self] + (@ancestor ? @ancestor.history : [])
15
+ @root = @history.last
16
+ @original_relation = nil
17
+ @original_attribute = nil
18
+
19
+ # FIXME: I think we can remove this eventually
20
+ @hash = name.hash + root.relation.class.hash
12
21
  end
13
22
 
23
+ def engine
24
+ @relation.engine
25
+ end
26
+
27
+ def christener
28
+ @relation.christener
29
+ end
30
+
31
+ def == other
32
+ super ||
33
+ Attribute === other &&
34
+ @name == other.name &&
35
+ @alias == other.alias &&
36
+ @ancestor == other.ancestor &&
37
+ @relation == other.relation
38
+ end
39
+
40
+ alias :eql? :==
41
+
14
42
  def named?(hypothetical_name)
15
43
  (@alias || name).to_s == hypothetical_name.to_s
16
44
  end
@@ -19,190 +47,184 @@ module Arel
19
47
  false
20
48
  end
21
49
 
22
- def inspect
23
- "<Attribute #{name}>"
50
+ def eval(row)
51
+ row[self]
24
52
  end
25
53
 
26
- module Transformations
27
- def self.included(klass)
28
- klass.send :alias_method, :eql?, :==
29
- end
30
-
31
- def hash
32
- @hash ||= name.hash + root.relation.hash
33
- end
54
+ def as(aliaz = nil)
55
+ Attribute.new(relation, name, :alias => aliaz, :ancestor => self)
56
+ end
34
57
 
35
- def as(aliaz = nil)
36
- Attribute.new(relation, name, :alias => aliaz, :ancestor => self)
37
- end
58
+ def bind(new_relation)
59
+ relation == new_relation ? self : Attribute.new(new_relation, name, :alias => @alias, :ancestor => self)
60
+ end
38
61
 
39
- def bind(new_relation)
40
- relation == new_relation ? self : Attribute.new(new_relation, name, :alias => @alias, :ancestor => self)
41
- end
62
+ def to_attribute(relation)
63
+ bind(relation)
64
+ end
42
65
 
43
- def to_attribute(relation)
44
- bind(relation)
45
- end
66
+ def join?
67
+ relation.join?
46
68
  end
47
- include Transformations
48
69
 
49
- module Congruence
50
- def history
51
- @history ||= [self] + (ancestor ? ancestor.history : [])
52
- end
70
+ def root
71
+ history.last
72
+ end
53
73
 
54
- def join?
55
- relation.join?
56
- end
74
+ def original_relation
75
+ @original_relation ||= original_attribute.relation
76
+ end
57
77
 
58
- def root
59
- history.last
60
- end
78
+ def original_attribute
79
+ @original_attribute ||= history.detect { |a| !a.join? }
80
+ end
61
81
 
62
- def original_relation
63
- @original_relation ||= original_attribute.relation
64
- end
82
+ def find_correlate_in(relation)
83
+ relation[self] || self
84
+ end
65
85
 
66
- def original_attribute
67
- @original_attribute ||= history.detect { |a| !a.join? }
68
- end
86
+ def descends_from?(other)
87
+ history.include?(other)
88
+ end
69
89
 
70
- def find_correlate_in(relation)
71
- relation[self] || self
72
- end
90
+ def /(other)
91
+ other ? (history & other.history).size : 0
92
+ end
73
93
 
74
- def descends_from?(other)
75
- history.include?(other)
76
- end
94
+ PREDICATES = [
95
+ :eq, :eq_any, :eq_all, :not_eq, :not_eq_any, :not_eq_all, :lt, :lt_any,
96
+ :lt_all, :lteq, :lteq_any, :lteq_all, :gt, :gt_any, :gt_all, :gteq,
97
+ :gteq_any, :gteq_all, :matches, :matches_any, :matches_all, :not_matches,
98
+ :not_matches_any, :not_matches_all, :in, :in_any, :in_all, :not_in,
99
+ :not_in_any, :not_in_all
100
+ ]
77
101
 
78
- def /(other)
79
- other ? (history & other.history).size : 0
102
+ Predications = Class.new do
103
+ def self.instance_methods *args
104
+ warn "this module is deprecated, please use the PREDICATES constant"
105
+ PREDICATES
80
106
  end
81
107
  end
82
- include Congruence
83
108
 
84
- module Predications
85
- def eq(other)
86
- Predicates::Equality.new(self, other)
87
- end
109
+ def eq(other)
110
+ Predicates::Equality.new(self, other)
111
+ end
88
112
 
89
- def eq_any(*others)
90
- Predicates::Any.build(Predicates::Equality, self, *others)
91
- end
113
+ def eq_any(*others)
114
+ Predicates::Any.build(Predicates::Equality, self, *others)
115
+ end
92
116
 
93
- def eq_all(*others)
94
- Predicates::All.build(Predicates::Equality, self, *others)
95
- end
117
+ def eq_all(*others)
118
+ Predicates::All.build(Predicates::Equality, self, *others)
119
+ end
96
120
 
97
- def not_eq(other)
98
- Predicates::Inequality.new(self, other)
99
- end
121
+ def not_eq(other)
122
+ Predicates::Inequality.new(self, other)
123
+ end
100
124
 
101
- def not_eq_any(*others)
102
- Predicates::Any.build(Predicates::Inequality, self, *others)
103
- end
125
+ def not_eq_any(*others)
126
+ Predicates::Any.build(Predicates::Inequality, self, *others)
127
+ end
104
128
 
105
- def not_eq_all(*others)
106
- Predicates::All.build(Predicates::Inequality, self, *others)
107
- end
129
+ def not_eq_all(*others)
130
+ Predicates::All.build(Predicates::Inequality, self, *others)
131
+ end
108
132
 
109
- def lt(other)
110
- Predicates::LessThan.new(self, other)
111
- end
133
+ def lt(other)
134
+ Predicates::LessThan.new(self, other)
135
+ end
112
136
 
113
- def lt_any(*others)
114
- Predicates::Any.build(Predicates::LessThan, self, *others)
115
- end
137
+ def lt_any(*others)
138
+ Predicates::Any.build(Predicates::LessThan, self, *others)
139
+ end
116
140
 
117
- def lt_all(*others)
118
- Predicates::All.build(Predicates::LessThan, self, *others)
119
- end
141
+ def lt_all(*others)
142
+ Predicates::All.build(Predicates::LessThan, self, *others)
143
+ end
120
144
 
121
- def lteq(other)
122
- Predicates::LessThanOrEqualTo.new(self, other)
123
- end
145
+ def lteq(other)
146
+ Predicates::LessThanOrEqualTo.new(self, other)
147
+ end
124
148
 
125
- def lteq_any(*others)
126
- Predicates::Any.build(Predicates::LessThanOrEqualTo, self, *others)
127
- end
149
+ def lteq_any(*others)
150
+ Predicates::Any.build(Predicates::LessThanOrEqualTo, self, *others)
151
+ end
128
152
 
129
- def lteq_all(*others)
130
- Predicates::All.build(Predicates::LessThanOrEqualTo, self, *others)
131
- end
153
+ def lteq_all(*others)
154
+ Predicates::All.build(Predicates::LessThanOrEqualTo, self, *others)
155
+ end
132
156
 
133
- def gt(other)
134
- Predicates::GreaterThan.new(self, other)
135
- end
157
+ def gt(other)
158
+ Predicates::GreaterThan.new(self, other)
159
+ end
136
160
 
137
- def gt_any(*others)
138
- Predicates::Any.build(Predicates::GreaterThan, self, *others)
139
- end
161
+ def gt_any(*others)
162
+ Predicates::Any.build(Predicates::GreaterThan, self, *others)
163
+ end
140
164
 
141
- def gt_all(*others)
142
- Predicates::All.build(Predicates::GreaterThan, self, *others)
143
- end
165
+ def gt_all(*others)
166
+ Predicates::All.build(Predicates::GreaterThan, self, *others)
167
+ end
144
168
 
145
- def gteq(other)
146
- Predicates::GreaterThanOrEqualTo.new(self, other)
147
- end
169
+ def gteq(other)
170
+ Predicates::GreaterThanOrEqualTo.new(self, other)
171
+ end
148
172
 
149
- def gteq_any(*others)
150
- Predicates::Any.build(Predicates::GreaterThanOrEqualTo, self, *others)
151
- end
173
+ def gteq_any(*others)
174
+ Predicates::Any.build(Predicates::GreaterThanOrEqualTo, self, *others)
175
+ end
152
176
 
153
- def gteq_all(*others)
154
- Predicates::All.build(Predicates::GreaterThanOrEqualTo, self, *others)
155
- end
177
+ def gteq_all(*others)
178
+ Predicates::All.build(Predicates::GreaterThanOrEqualTo, self, *others)
179
+ end
156
180
 
157
- def matches(other)
158
- Predicates::Match.new(self, other)
159
- end
181
+ def matches(other)
182
+ Predicates::Match.new(self, other)
183
+ end
160
184
 
161
- def matches_any(*others)
162
- Predicates::Any.build(Predicates::Match, self, *others)
163
- end
185
+ def matches_any(*others)
186
+ Predicates::Any.build(Predicates::Match, self, *others)
187
+ end
164
188
 
165
- def matches_all(*others)
166
- Predicates::All.build(Predicates::Match, self, *others)
167
- end
189
+ def matches_all(*others)
190
+ Predicates::All.build(Predicates::Match, self, *others)
191
+ end
168
192
 
169
- def not_matches(other)
170
- Predicates::NotMatch.new(self, other)
171
- end
193
+ def not_matches(other)
194
+ Predicates::NotMatch.new(self, other)
195
+ end
172
196
 
173
- def not_matches_any(*others)
174
- Predicates::Any.build(Predicates::NotMatch, self, *others)
175
- end
197
+ def not_matches_any(*others)
198
+ Predicates::Any.build(Predicates::NotMatch, self, *others)
199
+ end
176
200
 
177
- def not_matches_all(*others)
178
- Predicates::All.build(Predicates::NotMatch, self, *others)
179
- end
201
+ def not_matches_all(*others)
202
+ Predicates::All.build(Predicates::NotMatch, self, *others)
203
+ end
180
204
 
181
- def in(other)
182
- Predicates::In.new(self, other)
183
- end
205
+ def in(other)
206
+ Predicates::In.new(self, other)
207
+ end
184
208
 
185
- def in_any(*others)
186
- Predicates::Any.build(Predicates::In, self, *others)
187
- end
209
+ def in_any(*others)
210
+ Predicates::Any.build(Predicates::In, self, *others)
211
+ end
188
212
 
189
- def in_all(*others)
190
- Predicates::All.build(Predicates::In, self, *others)
191
- end
213
+ def in_all(*others)
214
+ Predicates::All.build(Predicates::In, self, *others)
215
+ end
192
216
 
193
- def not_in(other)
194
- Predicates::NotIn.new(self, other)
195
- end
217
+ def not_in(other)
218
+ Predicates::NotIn.new(self, other)
219
+ end
196
220
 
197
- def not_in_any(*others)
198
- Predicates::Any.build(Predicates::NotIn, self, *others)
199
- end
221
+ def not_in_any(*others)
222
+ Predicates::Any.build(Predicates::NotIn, self, *others)
223
+ end
200
224
 
201
- def not_in_all(*others)
202
- Predicates::All.build(Predicates::NotIn, self, *others)
203
- end
225
+ def not_in_all(*others)
226
+ Predicates::All.build(Predicates::NotIn, self, *others)
204
227
  end
205
- include Predications
206
228
 
207
229
  module Expressions
208
230
  def count(distinct = false)
@@ -266,5 +288,17 @@ module Arel
266
288
  end
267
289
  end
268
290
  include Types
291
+
292
+ def column
293
+ original_relation.column_for(self)
294
+ end
295
+
296
+ def format(object)
297
+ object.to_sql(Sql::Attribute.new(self))
298
+ end
299
+
300
+ def to_sql(formatter = Sql::WhereCondition.new(relation))
301
+ formatter.attribute self
302
+ end
269
303
  end
270
304
  end
@@ -1,4 +1,3 @@
1
1
  require 'arel/algebra/core_extensions/object'
2
- require 'arel/algebra/core_extensions/class'
3
2
  require 'arel/algebra/core_extensions/symbol'
4
3
  require 'arel/algebra/core_extensions/hash'
@@ -1,11 +1,7 @@
1
- module Arel
2
- module HashExtensions
3
- def bind(relation)
4
- inject({}) do |bound, (key, value)|
5
- bound.merge(key.bind(relation) => value.bind(relation))
6
- end
7
- end
8
-
9
- Hash.send(:include, self)
1
+ class Hash
2
+ def bind(relation)
3
+ Hash[map { |key, value|
4
+ [key.bind(relation), value.bind(relation)]
5
+ }]
10
6
  end
11
7
  end