arel 2.0.10 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
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