arel 2.0.10 → 2.1.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 (102) hide show
  1. data/History.txt +51 -4
  2. data/Manifest.txt +15 -31
  3. data/README.markdown +27 -3
  4. data/Rakefile +0 -2
  5. data/arel.gemspec +20 -19
  6. data/lib/arel.rb +9 -1
  7. data/lib/arel/alias_predication.rb +7 -0
  8. data/lib/arel/attributes/attribute.rb +10 -1
  9. data/lib/arel/crud.rb +43 -8
  10. data/lib/arel/expression.rb +1 -0
  11. data/lib/arel/factory_methods.rb +35 -0
  12. data/lib/arel/insert_manager.rb +5 -1
  13. data/lib/arel/math.rb +19 -0
  14. data/lib/arel/nodes.rb +32 -42
  15. data/lib/arel/nodes/and.rb +18 -1
  16. data/lib/arel/nodes/binary.rb +28 -0
  17. data/lib/arel/nodes/count.rb +0 -3
  18. data/lib/arel/nodes/function.rb +13 -2
  19. data/lib/arel/nodes/infix_operation.rb +43 -0
  20. data/lib/arel/nodes/join_source.rb +14 -0
  21. data/lib/arel/nodes/named_function.rb +14 -0
  22. data/lib/arel/nodes/node.rb +5 -2
  23. data/lib/arel/nodes/select_core.rb +24 -10
  24. data/lib/arel/nodes/select_statement.rb +8 -6
  25. data/lib/arel/nodes/sql_literal.rb +2 -0
  26. data/lib/arel/nodes/string_join.rb +2 -4
  27. data/lib/arel/nodes/table_alias.rb +7 -3
  28. data/lib/arel/nodes/{as.rb → terminal.rb} +1 -1
  29. data/lib/arel/nodes/unary.rb +17 -0
  30. data/lib/arel/nodes/unqualified_column.rb +4 -0
  31. data/lib/arel/nodes/update_statement.rb +4 -2
  32. data/lib/arel/nodes/with.rb +10 -0
  33. data/lib/arel/order_predications.rb +13 -0
  34. data/lib/arel/predications.rb +6 -24
  35. data/lib/arel/select_manager.rb +95 -40
  36. data/lib/arel/table.rb +32 -19
  37. data/lib/arel/tree_manager.rb +1 -0
  38. data/lib/arel/update_manager.rb +4 -0
  39. data/lib/arel/visitors.rb +2 -0
  40. data/lib/arel/visitors/depth_first.rb +19 -9
  41. data/lib/arel/visitors/dot.rb +42 -25
  42. data/lib/arel/visitors/ibm_db.rb +12 -0
  43. data/lib/arel/visitors/join_sql.rb +2 -23
  44. data/lib/arel/visitors/mssql.rb +7 -0
  45. data/lib/arel/visitors/mysql.rb +4 -0
  46. data/lib/arel/visitors/oracle.rb +2 -2
  47. data/lib/arel/visitors/postgresql.rb +2 -38
  48. data/lib/arel/visitors/to_sql.rb +148 -67
  49. data/test/attributes/test_attribute.rb +3 -3
  50. data/test/helper.rb +1 -1
  51. data/test/nodes/test_as.rb +6 -0
  52. data/test/nodes/test_bin.rb +23 -0
  53. data/test/nodes/test_named_function.rb +30 -0
  54. data/test/nodes/test_node.rb +10 -0
  55. data/test/nodes/test_not.rb +4 -7
  56. data/test/nodes/test_select_core.rb +23 -14
  57. data/test/support/fake_record.rb +21 -4
  58. data/test/test_attributes.rb +8 -0
  59. data/test/test_crud.rb +6 -12
  60. data/test/test_factory_methods.rb +34 -0
  61. data/test/test_insert_manager.rb +19 -0
  62. data/test/test_select_manager.rb +343 -64
  63. data/test/test_table.rb +36 -45
  64. data/test/visitors/test_depth_first.rb +29 -11
  65. data/test/visitors/test_dot.rb +47 -0
  66. data/test/visitors/test_ibm_db.rb +27 -0
  67. data/test/visitors/test_join_sql.rb +14 -7
  68. data/test/visitors/test_mssql.rb +9 -0
  69. data/test/visitors/test_oracle.rb +11 -10
  70. data/test/visitors/test_postgres.rb +12 -0
  71. data/test/visitors/test_to_sql.rb +80 -17
  72. metadata +80 -101
  73. data/lib/arel/nodes/assignment.rb +0 -6
  74. data/lib/arel/nodes/avg.rb +0 -6
  75. data/lib/arel/nodes/between.rb +0 -6
  76. data/lib/arel/nodes/does_not_match.rb +0 -6
  77. data/lib/arel/nodes/except.rb +0 -7
  78. data/lib/arel/nodes/exists.rb +0 -7
  79. data/lib/arel/nodes/greater_than.rb +0 -6
  80. data/lib/arel/nodes/greater_than_or_equal.rb +0 -6
  81. data/lib/arel/nodes/group.rb +0 -6
  82. data/lib/arel/nodes/grouping.rb +0 -6
  83. data/lib/arel/nodes/having.rb +0 -6
  84. data/lib/arel/nodes/intersect.rb +0 -7
  85. data/lib/arel/nodes/join.rb +0 -13
  86. data/lib/arel/nodes/less_than.rb +0 -6
  87. data/lib/arel/nodes/less_than_or_equal.rb +0 -6
  88. data/lib/arel/nodes/limit.rb +0 -7
  89. data/lib/arel/nodes/lock.rb +0 -6
  90. data/lib/arel/nodes/matches.rb +0 -6
  91. data/lib/arel/nodes/max.rb +0 -6
  92. data/lib/arel/nodes/min.rb +0 -6
  93. data/lib/arel/nodes/not.rb +0 -6
  94. data/lib/arel/nodes/not_equal.rb +0 -6
  95. data/lib/arel/nodes/not_in.rb +0 -6
  96. data/lib/arel/nodes/offset.rb +0 -7
  97. data/lib/arel/nodes/on.rb +0 -6
  98. data/lib/arel/nodes/or.rb +0 -6
  99. data/lib/arel/nodes/sum.rb +0 -6
  100. data/lib/arel/nodes/top.rb +0 -6
  101. data/lib/arel/nodes/union.rb +0 -7
  102. data/lib/arel/nodes/union_all.rb +0 -7
@@ -2,10 +2,27 @@ module Arel
2
2
  module Nodes
3
3
  class Unary < Arel::Nodes::Node
4
4
  attr_accessor :expr
5
+ alias :value :expr
5
6
 
6
7
  def initialize expr
7
8
  @expr = expr
8
9
  end
9
10
  end
11
+
12
+ %w{
13
+ Bin
14
+ Group
15
+ Grouping
16
+ Having
17
+ Limit
18
+ Not
19
+ Offset
20
+ On
21
+ Top
22
+ Lock
23
+ DistinctOn
24
+ }.each do |name|
25
+ const_set(name, Class.new(Unary))
26
+ end
10
27
  end
11
28
  end
@@ -4,6 +4,10 @@ module Arel
4
4
  alias :attribute :expr
5
5
  alias :attribute= :expr=
6
6
 
7
+ def relation
8
+ @expr.relation
9
+ end
10
+
7
11
  def column
8
12
  @expr.column
9
13
  end
@@ -2,13 +2,15 @@ module Arel
2
2
  module Nodes
3
3
  class UpdateStatement < Arel::Nodes::Node
4
4
  attr_accessor :relation, :wheres, :values, :orders, :limit
5
+ attr_accessor :key
5
6
 
6
7
  def initialize
7
8
  @relation = nil
8
9
  @wheres = []
9
10
  @values = []
10
- @orders = []
11
- @limit = nil
11
+ @orders = []
12
+ @limit = nil
13
+ @key = nil
12
14
  end
13
15
 
14
16
  def initialize_copy other
@@ -0,0 +1,10 @@
1
+ module Arel
2
+ module Nodes
3
+ class With < Arel::Nodes::Unary
4
+ alias children expr
5
+ end
6
+
7
+ class WithRecursive < With; end
8
+ end
9
+ end
10
+
@@ -0,0 +1,13 @@
1
+ module Arel
2
+ module OrderPredications
3
+
4
+ def asc
5
+ Nodes::Ordering.new self, :asc
6
+ end
7
+
8
+ def desc
9
+ Nodes::Ordering.new self, :desc
10
+ end
11
+
12
+ end
13
+ end
@@ -1,8 +1,5 @@
1
1
  module Arel
2
2
  module Predications
3
- def as other
4
- Nodes::As.new self, other
5
- end
6
3
 
7
4
  def not_eq other
8
5
  Nodes::NotEqual.new self, other
@@ -36,9 +33,9 @@ module Arel
36
33
  if other.exclude_end?
37
34
  left = Nodes::GreaterThanOrEqual.new(self, other.begin)
38
35
  right = Nodes::LessThan.new(self, other.end)
39
- Nodes::And.new left, right
36
+ Nodes::And.new [left, right]
40
37
  else
41
- Nodes::Between.new(self, Nodes::And.new(other.begin, other.end))
38
+ Nodes::Between.new(self, Nodes::And.new([other.begin, other.end]))
42
39
  end
43
40
  else
44
41
  Nodes::In.new self, other
@@ -152,32 +149,17 @@ module Arel
152
149
  grouping_all :lteq, others
153
150
  end
154
151
 
155
- def asc
156
- Nodes::Ordering.new self, :asc
157
- end
158
-
159
- def desc
160
- Nodes::Ordering.new self, :desc
161
- end
162
-
163
152
  private
164
153
 
165
154
  def grouping_any method_id, others
166
- others = others.dup
167
- first = send method_id, others.shift
168
-
169
- Nodes::Grouping.new others.inject(first) { |memo,expr|
170
- Nodes::Or.new(memo, send(method_id, expr))
155
+ nodes = others.map {|expr| send(method_id, expr)}
156
+ Nodes::Grouping.new nodes.inject { |memo,node|
157
+ Nodes::Or.new(memo, node)
171
158
  }
172
159
  end
173
160
 
174
161
  def grouping_all method_id, others
175
- others = others.dup
176
- first = send method_id, others.shift
177
-
178
- Nodes::Grouping.new others.inject(first) { |memo,expr|
179
- Nodes::And.new(memo, send(method_id, expr))
180
- }
162
+ Nodes::Grouping.new Nodes::And.new(others.map {|expr| send(method_id, expr)})
181
163
  end
182
164
  end
183
165
  end
@@ -9,18 +9,33 @@ module Arel
9
9
  from table
10
10
  end
11
11
 
12
- def taken
12
+ def initialize_copy other
13
+ super
14
+ @ctx = @ast.cores.last
15
+ end
16
+
17
+ def limit
13
18
  @ast.limit && @ast.limit.expr
14
19
  end
20
+ alias :taken :limit
15
21
 
16
22
  def constraints
17
23
  @ctx.wheres
18
24
  end
19
25
 
26
+ def offset
27
+ @ast.offset && @ast.offset.expr
28
+ end
29
+
20
30
  def skip amount
21
- @ast.offset = Nodes::Offset.new(amount)
31
+ if amount
32
+ @ast.offset = Nodes::Offset.new(amount)
33
+ else
34
+ @ast.offset = nil
35
+ end
22
36
  self
23
37
  end
38
+ alias :offset= :skip
24
39
 
25
40
  ###
26
41
  # Produces an Arel::Nodes::Exists node
@@ -28,8 +43,14 @@ module Arel
28
43
  Arel::Nodes::Exists.new @ast
29
44
  end
30
45
 
46
+ def as other
47
+ create_table_alias grouping(@ast), Nodes::SqlLiteral.new(other)
48
+ end
49
+
31
50
  def where_clauses
32
- #warn "where_clauses is deprecated" if $VERBOSE
51
+ if $VERBOSE
52
+ warn "(#{caller.first}) where_clauses is deprecated and will be removed in arel 3.0.0 with no replacement"
53
+ end
33
54
  to_sql = Visitors::ToSql.new @engine
34
55
  @ctx.wheres.map { |c| to_sql.accept c }
35
56
  end
@@ -52,7 +73,7 @@ module Arel
52
73
  end
53
74
 
54
75
  def on *exprs
55
- @ctx.froms.constraint = Nodes::On.new(collapse(exprs))
76
+ @ctx.source.right.last.right = Nodes::On.new(collapse(exprs))
56
77
  self
57
78
  end
58
79
 
@@ -72,35 +93,36 @@ module Arel
72
93
  # FIXME: this is a hack to support
73
94
  # test_with_two_tables_in_from_without_getting_double_quoted
74
95
  # from the AR tests.
75
- if @ctx.froms
76
- source = @ctx.froms
77
96
 
78
- if Nodes::SqlLiteral === table && Nodes::Join === source
79
- source.left = table
80
- table = source
81
- end
97
+ case table
98
+ when Nodes::Join
99
+ @ctx.source.right << table
100
+ else
101
+ @ctx.source.left = table
82
102
  end
83
103
 
84
- @ctx.froms = table
85
104
  self
86
105
  end
87
106
 
107
+ def froms
108
+ @ast.cores.map { |x| x.from }.compact
109
+ end
110
+
88
111
  def join relation, klass = Nodes::InnerJoin
89
112
  return self unless relation
90
113
 
91
114
  case relation
92
115
  when String, Nodes::SqlLiteral
93
116
  raise if relation.blank?
94
- from Nodes::StringJoin.new(@ctx.froms, relation)
95
- else
96
- from klass.new(@ctx.froms, relation, nil)
117
+ klass = Nodes::StringJoin
97
118
  end
98
- end
99
119
 
100
- def having expr
101
- expr = Nodes::SqlLiteral.new(expr) if String === expr
120
+ @ctx.source.right << create_join(relation, nil, klass)
121
+ self
122
+ end
102
123
 
103
- @ctx.having = Nodes::Having.new(expr)
124
+ def having *exprs
125
+ @ctx.having = Nodes::Having.new(collapse(exprs, @ctx.having))
104
126
  self
105
127
  end
106
128
 
@@ -126,6 +148,7 @@ module Arel
126
148
  end
127
149
 
128
150
  def wheres
151
+ warn "#{caller[0]}: SelectManager#wheres is deprecated and will be removed in ARel 3.0.0 with no replacement"
129
152
  Compatibility::Wheres.new @engine, @ctx.wheres
130
153
  end
131
154
 
@@ -156,17 +179,34 @@ module Arel
156
179
  end
157
180
  alias :minus :except
158
181
 
182
+ def with *subqueries
183
+ if subqueries.first.is_a? Symbol
184
+ node_class = Nodes.const_get("With#{subqueries.shift.to_s.capitalize}")
185
+ else
186
+ node_class = Nodes::With
187
+ end
188
+ @ast.with = node_class.new(subqueries.flatten)
189
+
190
+ self
191
+ end
192
+
159
193
  def take limit
160
- @ast.limit = Nodes::Limit.new(limit)
161
- @ctx.top = Nodes::Top.new(limit)
194
+ if limit
195
+ @ast.limit = Nodes::Limit.new(limit)
196
+ @ctx.top = Nodes::Top.new(limit)
197
+ else
198
+ @ast.limit = nil
199
+ @ctx.top = nil
200
+ end
162
201
  self
163
202
  end
203
+ alias limit= take
164
204
 
165
205
  def join_sql
166
- return nil unless @ctx.froms
206
+ return nil if @ctx.source.right.empty?
167
207
 
168
- viz = Visitors::JoinSql.new @engine
169
- Nodes::SqlLiteral.new viz.accept @ctx
208
+ sql = @visitor.dup.extend(Visitors::JoinSql).accept @ctx
209
+ Nodes::SqlLiteral.new sql
170
210
  end
171
211
 
172
212
  def order_clauses
@@ -175,9 +215,13 @@ module Arel
175
215
  }
176
216
  end
177
217
 
218
+ def join_sources
219
+ @ctx.source.right
220
+ end
221
+
178
222
  def joins manager
179
223
  if $VERBOSE
180
- warn "joins is deprecated and will be removed in 2.2"
224
+ warn "joins is deprecated and will be removed in 3.0.0"
181
225
  warn "please remove your call to joins from #{caller.first}"
182
226
  end
183
227
  manager.join_sql
@@ -203,13 +247,22 @@ module Arel
203
247
 
204
248
  # FIXME: this method should go away
205
249
  def insert values
206
- im = InsertManager.new @engine
250
+ if $VERBOSE
251
+ warn <<-eowarn
252
+ insert (#{caller.first}) is deprecated and will be removed in ARel 3.0.0. Please
253
+ switch to `compile_insert`
254
+ eowarn
255
+ end
256
+
257
+ im = compile_insert(values)
207
258
  table = @ctx.froms
208
- primary_key_name = (primary_key = table.primary_key) && primary_key.name
259
+
260
+ primary_key = table.primary_key
261
+ primary_key_name = primary_key.name if primary_key
262
+
209
263
  # FIXME: in AR tests values sometimes were Array and not Hash therefore is_a?(Hash) check is added
210
264
  primary_key_value = primary_key && values.is_a?(Hash) && values[primary_key]
211
265
  im.into table
212
- im.insert values
213
266
  # Oracle adapter needs primary key name to generate RETURNING ... INTO ... clause
214
267
  # for tables which assign primary key value using trigger.
215
268
  # RETURNING ... INTO ... clause will be added only if primary_key_value is nil
@@ -218,20 +271,22 @@ module Arel
218
271
  end
219
272
 
220
273
  private
221
- def collapse exprs
222
- exprs.map! { |x| x.class == ::String ? Arel.sql(x) : x }
223
-
224
- return exprs.first if exprs.length == 1
225
-
226
- right = exprs.pop
227
- left = exprs.pop
228
-
229
- right = Nodes::SqlLiteral.new(right) if String === right
230
-
231
- right = Nodes::And.new left, right
232
- exprs.reverse.inject(right) { |memo,expr|
233
- Nodes::And.new(expr, memo)
274
+ def collapse exprs, existing = nil
275
+ exprs = exprs.unshift(existing.expr) if existing
276
+ exprs = exprs.compact.map { |expr|
277
+ if String === expr
278
+ # FIXME: Don't do this automatically
279
+ Arel.sql(expr)
280
+ else
281
+ expr
282
+ end
234
283
  }
284
+
285
+ if exprs.length == 1
286
+ exprs.first
287
+ else
288
+ create_and exprs
289
+ end
235
290
  end
236
291
  end
237
292
  end
@@ -1,12 +1,16 @@
1
1
  module Arel
2
2
  class Table
3
3
  include Arel::Crud
4
+ include Arel::FactoryMethods
4
5
 
5
6
  @engine = nil
6
7
  class << self; attr_accessor :engine; end
7
8
 
8
9
  attr_accessor :name, :engine, :aliases, :table_alias
9
10
 
11
+ # TableAlias and Table both have a #table_name which is the name of the underlying table
12
+ alias :table_name :name
13
+
10
14
  def initialize name, engine = Table.engine
11
15
  @name = name.to_s
12
16
  @engine = engine
@@ -17,7 +21,6 @@ module Arel
17
21
 
18
22
  if Hash === engine
19
23
  @engine = engine[:engine] || Table.engine
20
- @columns = attributes_for engine[:columns]
21
24
 
22
25
  # Sometime AR sends an :as parameter to table, to let the table know
23
26
  # that it is an Alias. We may want to override new, and return a
@@ -27,6 +30,11 @@ module Arel
27
30
  end
28
31
 
29
32
  def primary_key
33
+ if $VERBOSE
34
+ warn <<-eowarn
35
+ primary_key (#{caller.first}) is deprecated and will be removed in ARel 3.0.0
36
+ eowarn
37
+ end
30
38
  @primary_key ||= begin
31
39
  primary_key_name = @engine.connection.primary_key(name)
32
40
  # some tables might be without primary key
@@ -35,7 +43,7 @@ module Arel
35
43
  end
36
44
 
37
45
  def alias name = "#{self.name}_2"
38
- Nodes::TableAlias.new(name, self).tap do |node|
46
+ Nodes::TableAlias.new(self, name).tap do |node|
39
47
  @aliases << node
40
48
  end
41
49
  end
@@ -46,7 +54,7 @@ module Arel
46
54
 
47
55
  def joins manager
48
56
  if $VERBOSE
49
- warn "joins is deprecated and will be removed in 2.2"
57
+ warn "joins is deprecated and will be removed in 3.0.0"
50
58
  warn "please remove your call to joins from #{caller.first}"
51
59
  end
52
60
  nil
@@ -58,10 +66,10 @@ module Arel
58
66
  case relation
59
67
  when String, Nodes::SqlLiteral
60
68
  raise if relation.blank?
61
- from Nodes::StringJoin.new(self, relation)
62
- else
63
- from klass.new(self, relation, nil)
69
+ klass = Nodes::StringJoin
64
70
  end
71
+
72
+ from(self).join(relation, klass)
65
73
  end
66
74
 
67
75
  def group *columns
@@ -93,41 +101,46 @@ module Arel
93
101
  end
94
102
 
95
103
  def columns
104
+ if $VERBOSE
105
+ warn <<-eowarn
106
+ (#{caller.first}) Arel::Table#columns is deprecated and will be removed in
107
+ Arel 3.0.0 with no replacement. PEW PEW PEW!!!
108
+ eowarn
109
+ end
96
110
  @columns ||=
97
111
  attributes_for @engine.connection.columns(@name, "#{@name} Columns")
98
112
  end
99
113
 
100
114
  def [] name
101
- return nil unless table_exists?
102
-
103
- name = name.to_sym
104
- columns.find { |column| column.name == name }
115
+ ::Arel::Attribute.new self, name
105
116
  end
106
117
 
107
118
  def select_manager
108
119
  SelectManager.new(@engine)
109
120
  end
110
121
 
122
+ def insert_manager
123
+ InsertManager.new(@engine)
124
+ end
125
+
111
126
  private
112
127
 
113
128
  def attributes_for columns
114
129
  return nil unless columns
115
130
 
116
131
  columns.map do |column|
117
- Attributes.for(column).new self, column.name.to_sym, column
132
+ Attributes.for(column).new self, column.name.to_sym
118
133
  end
119
134
  end
120
135
 
121
- def table_exists?
122
- @table_exists ||= tables.key?(@name) || engine.connection.table_exists?(name)
123
- end
124
-
125
- def tables
126
- self.class.table_cache(@engine)
127
- end
128
-
129
136
  @@table_cache = nil
130
137
  def self.table_cache engine # :nodoc:
138
+ if $VERBOSE
139
+ warn <<-eowarn
140
+ (#{caller.first}) Arel::Table.table_cache is deprecated and will be removed in
141
+ Arel 3.0.0 with no replacement. PEW PEW PEW!!!
142
+ eowarn
143
+ end
131
144
  @@table_cache ||= Hash[engine.connection.tables.map { |x| [x,true] }]
132
145
  end
133
146
  end