activerecord 4.0.13 → 4.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +745 -2700
  3. data/README.rdoc +2 -2
  4. data/examples/performance.rb +30 -18
  5. data/examples/simple.rb +4 -4
  6. data/lib/active_record.rb +2 -6
  7. data/lib/active_record/aggregations.rb +2 -1
  8. data/lib/active_record/association_relation.rb +0 -4
  9. data/lib/active_record/associations.rb +87 -43
  10. data/lib/active_record/associations/alias_tracker.rb +1 -3
  11. data/lib/active_record/associations/association.rb +8 -16
  12. data/lib/active_record/associations/association_scope.rb +5 -16
  13. data/lib/active_record/associations/belongs_to_association.rb +34 -25
  14. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  15. data/lib/active_record/associations/builder/association.rb +78 -54
  16. data/lib/active_record/associations/builder/belongs_to.rb +91 -58
  17. data/lib/active_record/associations/builder/collection_association.rb +47 -45
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +107 -25
  19. data/lib/active_record/associations/builder/has_many.rb +2 -2
  20. data/lib/active_record/associations/builder/has_one.rb +5 -7
  21. data/lib/active_record/associations/builder/singular_association.rb +6 -7
  22. data/lib/active_record/associations/collection_association.rb +68 -105
  23. data/lib/active_record/associations/collection_proxy.rb +12 -15
  24. data/lib/active_record/associations/has_many_association.rb +11 -9
  25. data/lib/active_record/associations/has_many_through_association.rb +16 -12
  26. data/lib/active_record/associations/has_one_association.rb +1 -1
  27. data/lib/active_record/associations/join_dependency.rb +204 -165
  28. data/lib/active_record/associations/join_dependency/join_association.rb +43 -101
  29. data/lib/active_record/associations/join_dependency/join_base.rb +6 -8
  30. data/lib/active_record/associations/join_dependency/join_part.rb +18 -37
  31. data/lib/active_record/associations/join_helper.rb +2 -11
  32. data/lib/active_record/associations/preloader.rb +89 -34
  33. data/lib/active_record/associations/preloader/association.rb +43 -25
  34. data/lib/active_record/associations/preloader/collection_association.rb +2 -2
  35. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  36. data/lib/active_record/associations/preloader/singular_association.rb +3 -3
  37. data/lib/active_record/associations/preloader/through_association.rb +58 -26
  38. data/lib/active_record/associations/singular_association.rb +6 -5
  39. data/lib/active_record/associations/through_association.rb +2 -2
  40. data/lib/active_record/attribute_assignment.rb +5 -2
  41. data/lib/active_record/attribute_methods.rb +45 -40
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -1
  43. data/lib/active_record/attribute_methods/dirty.rb +8 -22
  44. data/lib/active_record/attribute_methods/primary_key.rb +1 -7
  45. data/lib/active_record/attribute_methods/read.rb +55 -28
  46. data/lib/active_record/attribute_methods/serialization.rb +12 -33
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -13
  48. data/lib/active_record/attribute_methods/write.rb +37 -12
  49. data/lib/active_record/autosave_association.rb +207 -207
  50. data/lib/active_record/base.rb +5 -1
  51. data/lib/active_record/callbacks.rb +2 -2
  52. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +2 -7
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +11 -22
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +12 -14
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -5
  56. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +84 -0
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +9 -8
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +52 -83
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +0 -5
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +14 -97
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +58 -60
  63. data/lib/active_record/connection_adapters/column.rb +1 -35
  64. data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
  65. data/lib/active_record/connection_adapters/mysql2_adapter.rb +3 -4
  66. data/lib/active_record/connection_adapters/mysql_adapter.rb +16 -15
  67. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +24 -18
  68. data/lib/active_record/connection_adapters/postgresql/cast.rb +20 -16
  69. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +23 -43
  70. data/lib/active_record/connection_adapters/postgresql/oid.rb +19 -12
  71. data/lib/active_record/connection_adapters/postgresql/quoting.rb +28 -23
  72. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +8 -30
  73. data/lib/active_record/connection_adapters/postgresql_adapter.rb +92 -75
  74. data/lib/active_record/connection_adapters/schema_cache.rb +8 -29
  75. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +31 -64
  76. data/lib/active_record/connection_handling.rb +2 -2
  77. data/lib/active_record/core.rb +22 -43
  78. data/lib/active_record/counter_cache.rb +7 -7
  79. data/lib/active_record/enum.rb +100 -0
  80. data/lib/active_record/errors.rb +10 -5
  81. data/lib/active_record/fixture_set/file.rb +2 -1
  82. data/lib/active_record/fixtures.rb +171 -74
  83. data/lib/active_record/inheritance.rb +16 -22
  84. data/lib/active_record/integration.rb +52 -1
  85. data/lib/active_record/locking/optimistic.rb +7 -2
  86. data/lib/active_record/locking/pessimistic.rb +1 -1
  87. data/lib/active_record/log_subscriber.rb +5 -12
  88. data/lib/active_record/migration.rb +62 -46
  89. data/lib/active_record/migration/command_recorder.rb +7 -13
  90. data/lib/active_record/model_schema.rb +7 -14
  91. data/lib/active_record/nested_attributes.rb +10 -8
  92. data/lib/active_record/no_touching.rb +52 -0
  93. data/lib/active_record/null_relation.rb +3 -3
  94. data/lib/active_record/persistence.rb +16 -34
  95. data/lib/active_record/querying.rb +14 -12
  96. data/lib/active_record/railtie.rb +0 -50
  97. data/lib/active_record/railties/databases.rake +12 -15
  98. data/lib/active_record/readonly_attributes.rb +0 -6
  99. data/lib/active_record/reflection.rb +189 -75
  100. data/lib/active_record/relation.rb +69 -94
  101. data/lib/active_record/relation/batches.rb +57 -23
  102. data/lib/active_record/relation/calculations.rb +36 -43
  103. data/lib/active_record/relation/delegation.rb +54 -39
  104. data/lib/active_record/relation/finder_methods.rb +107 -62
  105. data/lib/active_record/relation/merger.rb +7 -20
  106. data/lib/active_record/relation/predicate_builder.rb +57 -38
  107. data/lib/active_record/relation/predicate_builder/array_handler.rb +29 -0
  108. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  109. data/lib/active_record/relation/query_methods.rb +110 -98
  110. data/lib/active_record/relation/spawn_methods.rb +1 -2
  111. data/lib/active_record/result.rb +45 -6
  112. data/lib/active_record/runtime_registry.rb +5 -0
  113. data/lib/active_record/sanitization.rb +6 -8
  114. data/lib/active_record/schema_dumper.rb +16 -5
  115. data/lib/active_record/schema_migration.rb +24 -25
  116. data/lib/active_record/scoping/default.rb +5 -18
  117. data/lib/active_record/scoping/named.rb +8 -29
  118. data/lib/active_record/store.rb +56 -28
  119. data/lib/active_record/tasks/database_tasks.rb +8 -4
  120. data/lib/active_record/timestamp.rb +4 -4
  121. data/lib/active_record/transactions.rb +8 -10
  122. data/lib/active_record/validations/presence.rb +1 -1
  123. data/lib/active_record/validations/uniqueness.rb +1 -6
  124. data/lib/active_record/version.rb +1 -1
  125. data/lib/rails/generators/active_record.rb +2 -8
  126. data/lib/rails/generators/active_record/migration.rb +18 -0
  127. data/lib/rails/generators/active_record/migration/migration_generator.rb +4 -0
  128. data/lib/rails/generators/active_record/model/model_generator.rb +4 -0
  129. metadata +32 -45
  130. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -65
  131. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  132. data/lib/active_record/tasks/firebird_database_tasks.rb +0 -56
  133. data/lib/active_record/tasks/oracle_database_tasks.rb +0 -45
  134. data/lib/active_record/tasks/sqlserver_database_tasks.rb +0 -48
  135. data/lib/active_record/test_case.rb +0 -102
@@ -1,5 +1,20 @@
1
1
  module ActiveRecord
2
2
  class PredicateBuilder # :nodoc:
3
+ @handlers = []
4
+
5
+ autoload :RelationHandler, 'active_record/relation/predicate_builder/relation_handler'
6
+ autoload :ArrayHandler, 'active_record/relation/predicate_builder/array_handler'
7
+
8
+ def self.resolve_column_aliases(klass, hash)
9
+ hash = hash.dup
10
+ hash.keys.grep(Symbol) do |key|
11
+ if klass.attribute_alias? key
12
+ hash[klass.attribute_alias(key)] = hash.delete key
13
+ end
14
+ end
15
+ hash
16
+ end
17
+
3
18
  def self.build_from_hash(klass, attributes, default_table)
4
19
  queries = []
5
20
 
@@ -40,9 +55,9 @@ module ActiveRecord
40
55
  #
41
56
  # For polymorphic relationships, find the foreign key and type:
42
57
  # PriceEstimate.where(estimate_of: treasure)
43
- if klass && value.class < Base && reflection = klass.reflect_on_association(column.to_sym)
44
- if reflection.polymorphic?
45
- queries << build(table[reflection.foreign_type], value.class.base_class)
58
+ if klass && reflection = klass.reflect_on_association(column.to_sym)
59
+ if reflection.polymorphic? && base_class = polymorphic_base_class_from_value(value)
60
+ queries << build(table[reflection.foreign_type], base_class)
46
61
  end
47
62
 
48
63
  column = reflection.foreign_key
@@ -52,6 +67,18 @@ module ActiveRecord
52
67
  queries
53
68
  end
54
69
 
70
+ def self.polymorphic_base_class_from_value(value)
71
+ case value
72
+ when Relation
73
+ value.klass.base_class
74
+ when Array
75
+ val = value.compact.first
76
+ val.class.base_class if val.is_a?(Base)
77
+ when Base
78
+ value.class.base_class
79
+ end
80
+ end
81
+
55
82
  def self.references(attributes)
56
83
  attributes.map do |key, value|
57
84
  if value.is_a?(Hash)
@@ -63,44 +90,36 @@ module ActiveRecord
63
90
  end.compact
64
91
  end
65
92
 
93
+ # Define how a class is converted to Arel nodes when passed to +where+.
94
+ # The handler can be any object that responds to +call+, and will be used
95
+ # for any value that +===+ the class given. For example:
96
+ #
97
+ # MyCustomDateRange = Struct.new(:start, :end)
98
+ # handler = proc do |column, range|
99
+ # Arel::Nodes::Between.new(column,
100
+ # Arel::Nodes::And.new([range.start, range.end])
101
+ # )
102
+ # end
103
+ # ActiveRecord::PredicateBuilder.register_handler(MyCustomDateRange, handler)
104
+ def self.register_handler(klass, handler)
105
+ @handlers.unshift([klass, handler])
106
+ end
107
+
108
+ register_handler(BasicObject, ->(attribute, value) { attribute.eq(value) })
109
+ # FIXME: I think we need to deprecate this behavior
110
+ register_handler(Class, ->(attribute, value) { attribute.eq(value.name) })
111
+ register_handler(Base, ->(attribute, value) { attribute.eq(value.id) })
112
+ register_handler(Range, ->(attribute, value) { attribute.in(value) })
113
+ register_handler(Relation, RelationHandler.new)
114
+ register_handler(Array, ArrayHandler.new)
115
+
66
116
  private
67
117
  def self.build(attribute, value)
68
- case value
69
- when Array
70
- values = value.to_a.map {|x| x.is_a?(Base) ? x.id : x}
71
- ranges, values = values.partition {|v| v.is_a?(Range)}
72
-
73
- values_predicate = if values.include?(nil)
74
- values = values.compact
75
-
76
- case values.length
77
- when 0
78
- attribute.eq(nil)
79
- when 1
80
- attribute.eq(values.first).or(attribute.eq(nil))
81
- else
82
- attribute.in(values).or(attribute.eq(nil))
83
- end
84
- else
85
- attribute.in(values)
86
- end
118
+ handler_for(value).call(attribute, value)
119
+ end
87
120
 
88
- array_predicates = ranges.map { |range| attribute.in(range) }
89
- array_predicates << values_predicate
90
- array_predicates.inject { |composite, predicate| composite.or(predicate) }
91
- when ActiveRecord::Relation
92
- value = value.select(value.klass.arel_table[value.klass.primary_key]) if value.select_values.empty?
93
- attribute.in(value.arel.ast)
94
- when Range
95
- attribute.in(value)
96
- when ActiveRecord::Base
97
- attribute.eq(value.id)
98
- when Class
99
- # FIXME: I think we need to deprecate this behavior
100
- attribute.eq(value.name)
101
- else
102
- attribute.eq(value)
103
- end
121
+ def self.handler_for(object)
122
+ @handlers.detect { |klass, _| klass === object }.last
104
123
  end
105
124
  end
106
125
  end
@@ -0,0 +1,29 @@
1
+ module ActiveRecord
2
+ class PredicateBuilder
3
+ class ArrayHandler # :nodoc:
4
+ def call(attribute, value)
5
+ values = value.map { |x| x.is_a?(Base) ? x.id : x }
6
+ ranges, values = values.partition { |v| v.is_a?(Range) }
7
+
8
+ values_predicate = if values.include?(nil)
9
+ values = values.compact
10
+
11
+ case values.length
12
+ when 0
13
+ attribute.eq(nil)
14
+ when 1
15
+ attribute.eq(values.first).or(attribute.eq(nil))
16
+ else
17
+ attribute.in(values).or(attribute.eq(nil))
18
+ end
19
+ else
20
+ attribute.in(values)
21
+ end
22
+
23
+ array_predicates = ranges.map { |range| attribute.in(range) }
24
+ array_predicates << values_predicate
25
+ array_predicates.inject { |composite, predicate| composite.or(predicate) }
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,13 @@
1
+ module ActiveRecord
2
+ class PredicateBuilder
3
+ class RelationHandler # :nodoc:
4
+ def call(attribute, value)
5
+ if value.select_values.empty?
6
+ value = value.select(value.klass.arel_table[value.klass.primary_key])
7
+ end
8
+
9
+ attribute.in(value.arel.ast)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,12 +1,9 @@
1
1
  require 'active_support/core_ext/array/wrap'
2
- require 'active_model/forbidden_attributes_protection'
3
2
 
4
3
  module ActiveRecord
5
4
  module QueryMethods
6
5
  extend ActiveSupport::Concern
7
6
 
8
- include ActiveModel::ForbiddenAttributesProtection
9
-
10
7
  # WhereChain objects act as placeholder for queries in which #where does not have any parameter.
11
8
  # In this case, #where must be chained with #not to return a new relation.
12
9
  class WhereChain
@@ -103,6 +100,14 @@ module ActiveRecord
103
100
  # firing an additional query. This will often result in a
104
101
  # performance improvement over a simple +join+.
105
102
  #
103
+ # You can also specify multiple relationships, like this:
104
+ #
105
+ # users = User.includes(:address, :friends)
106
+ #
107
+ # Loading nested relationships is possible using a Hash:
108
+ #
109
+ # users = User.includes(:address, friends: [:address, :followers])
110
+ #
106
111
  # === conditions
107
112
  #
108
113
  # If you want to add conditions to your included models you'll have
@@ -114,14 +119,15 @@ module ActiveRecord
114
119
  #
115
120
  # User.includes(:posts).where('posts.name = ?', 'example').references(:posts)
116
121
  def includes(*args)
117
- check_if_method_has_arguments!("includes", args)
122
+ check_if_method_has_arguments!(:includes, args)
118
123
  spawn.includes!(*args)
119
124
  end
120
125
 
121
126
  def includes!(*args) # :nodoc:
122
- args.reject! {|a| a.blank? }
127
+ args.reject!(&:blank?)
128
+ args.flatten!
123
129
 
124
- self.includes_values = (includes_values + args).flatten.uniq
130
+ self.includes_values |= args
125
131
  self
126
132
  end
127
133
 
@@ -132,7 +138,7 @@ module ActiveRecord
132
138
  # FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" =
133
139
  # "users"."id"
134
140
  def eager_load(*args)
135
- check_if_method_has_arguments!("eager_load", args)
141
+ check_if_method_has_arguments!(:eager_load, args)
136
142
  spawn.eager_load!(*args)
137
143
  end
138
144
 
@@ -146,7 +152,7 @@ module ActiveRecord
146
152
  # User.preload(:posts)
147
153
  # => SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (1, 2, 3)
148
154
  def preload(*args)
149
- check_if_method_has_arguments!("preload", args)
155
+ check_if_method_has_arguments!(:preload, args)
150
156
  spawn.preload!(*args)
151
157
  end
152
158
 
@@ -164,14 +170,15 @@ module ActiveRecord
164
170
  # User.includes(:posts).where("posts.name = 'foo'").references(:posts)
165
171
  # # => Query now knows the string references posts, so adds a JOIN
166
172
  def references(*args)
167
- check_if_method_has_arguments!("references", args)
173
+ check_if_method_has_arguments!(:references, args)
168
174
  spawn.references!(*args)
169
175
  end
170
176
 
171
177
  def references!(*args) # :nodoc:
172
178
  args.flatten!
179
+ args.map!(&:to_s)
173
180
 
174
- self.references_values = (references_values + args.map!(&:to_s)).uniq
181
+ self.references_values |= args
175
182
  self
176
183
  end
177
184
 
@@ -219,12 +226,14 @@ module ActiveRecord
219
226
  to_a.select { |*block_args| yield(*block_args) }
220
227
  else
221
228
  raise ArgumentError, 'Call this with at least one field' if fields.empty?
222
- spawn._select!(*fields)
229
+ spawn.select!(*fields)
223
230
  end
224
231
  end
225
232
 
226
- def _select!(*fields) # :nodoc:
227
- self.select_values += fields.flatten
233
+ def select!(*fields) # :nodoc:
234
+ fields.flatten!
235
+
236
+ self.select_values += fields
228
237
  self
229
238
  end
230
239
 
@@ -244,7 +253,7 @@ module ActiveRecord
244
253
  # User.group('name AS grouped_name, age')
245
254
  # => [#<User id: 3, name: "Foo", age: 21, ...>, #<User id: 2, name: "Oscar", age: 21, ...>, #<User id: 5, name: "Foo", age: 23, ...>]
246
255
  def group(*args)
247
- check_if_method_has_arguments!("group", args)
256
+ check_if_method_has_arguments!(:group, args)
248
257
  spawn.group!(*args)
249
258
  end
250
259
 
@@ -275,24 +284,12 @@ module ActiveRecord
275
284
  # User.order(:name, email: :desc)
276
285
  # => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC
277
286
  def order(*args)
278
- check_if_method_has_arguments!("order", args)
287
+ check_if_method_has_arguments!(:order, args)
279
288
  spawn.order!(*args)
280
289
  end
281
290
 
282
291
  def order!(*args) # :nodoc:
283
- args.flatten!
284
- validate_order_args args
285
-
286
- references = args.reject { |arg| Arel::Node === arg }
287
- references.map! { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }.compact!
288
- references!(references) if references.any?
289
-
290
- # if a symbol is given we prepend the quoted table name
291
- args = args.map! { |arg|
292
- arg.is_a?(Symbol) ?
293
- Arel::Nodes::Ascending.new(klass.arel_table[arg]) :
294
- arg
295
- }
292
+ preprocess_order_args(args)
296
293
 
297
294
  self.order_values += args
298
295
  self
@@ -308,13 +305,12 @@ module ActiveRecord
308
305
  #
309
306
  # generates a query with 'ORDER BY id ASC, name ASC'.
310
307
  def reorder(*args)
311
- check_if_method_has_arguments!("reorder", args)
308
+ check_if_method_has_arguments!(:reorder, args)
312
309
  spawn.reorder!(*args)
313
310
  end
314
311
 
315
312
  def reorder!(*args) # :nodoc:
316
- args.flatten!
317
- validate_order_args args
313
+ preprocess_order_args(args)
318
314
 
319
315
  self.reordering_value = true
320
316
  self.order_values = args
@@ -345,20 +341,27 @@ module ActiveRecord
345
341
  # User.where(name: "John", active: true).unscope(where: :name)
346
342
  # == User.where(active: true)
347
343
  #
348
- # Note that this method is more generalized than ActiveRecord::SpawnMethods#except
349
- # because #except will only affect a particular relation's values. It won't wipe
350
- # the order, grouping, etc. when that relation is merged. For example:
344
+ # This method is similar to <tt>except</tt>, but unlike
345
+ # <tt>except</tt>, it persists across merges:
346
+ #
347
+ # User.order('email').merge(User.except(:order))
348
+ # == User.order('email')
349
+ #
350
+ # User.order('email').merge(User.unscope(:order))
351
+ # == User.all
352
+ #
353
+ # This means it can be used in association definitions:
351
354
  #
352
- # Post.comments.except(:order)
355
+ # has_many :comments, -> { unscope where: :trashed }
353
356
  #
354
- # will still have an order if it comes from the default_scope on Comment.
355
357
  def unscope(*args)
356
- check_if_method_has_arguments!("unscope", args)
358
+ check_if_method_has_arguments!(:unscope, args)
357
359
  spawn.unscope!(*args)
358
360
  end
359
361
 
360
362
  def unscope!(*args) # :nodoc:
361
363
  args.flatten!
364
+ self.unscope_values += args
362
365
 
363
366
  args.each do |scope|
364
367
  case scope
@@ -392,8 +395,12 @@ module ActiveRecord
392
395
  # User.joins("LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id")
393
396
  # => SELECT "users".* FROM "users" LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id
394
397
  def joins(*args)
395
- check_if_method_has_arguments!("joins", args)
396
- spawn.joins!(*args.compact.flatten)
398
+ check_if_method_has_arguments!(:joins, args)
399
+
400
+ args.compact!
401
+ args.flatten!
402
+
403
+ spawn.joins!(*args)
397
404
  end
398
405
 
399
406
  def joins!(*args) # :nodoc:
@@ -420,7 +427,7 @@ module ActiveRecord
420
427
  # === string
421
428
  #
422
429
  # A single string, without additional arguments, is passed to the query
423
- # constructor as a SQL fragment, and used in the where clause of the query.
430
+ # constructor as an SQL fragment, and used in the where clause of the query.
424
431
  #
425
432
  # Client.where("orders_count = '2'")
426
433
  # # SELECT * from clients where orders_count = '2';
@@ -543,16 +550,25 @@ module ActiveRecord
543
550
  if opts == :chain
544
551
  WhereChain.new(self)
545
552
  else
546
- if Hash === opts
547
- opts = sanitize_forbidden_attributes(opts)
548
- references!(PredicateBuilder.references(opts))
549
- end
553
+ references!(PredicateBuilder.references(opts)) if Hash === opts
550
554
 
551
555
  self.where_values += build_where(opts, rest)
552
556
  self
553
557
  end
554
558
  end
555
559
 
560
+ # Allows you to change a previously set where condition for a given attribute, instead of appending to that condition.
561
+ #
562
+ # Post.where(trashed: true).where(trashed: false) # => WHERE `trashed` = 1 AND `trashed` = 0
563
+ # Post.where(trashed: true).rewhere(trashed: false) # => WHERE `trashed` = 0
564
+ # Post.where(active: true).where(trashed: true).rewhere(trashed: false) # => WHERE `active` = 1 AND `trashed` = 0
565
+ #
566
+ # This is short-hand for unscope(where: conditions.keys).where(conditions). Note that unlike reorder, we're only unscoping
567
+ # the named conditions -- not the entire where statement.
568
+ def rewhere(conditions)
569
+ unscope(where: conditions.keys).where(conditions)
570
+ end
571
+
556
572
  # Allows to specify a HAVING clause. Note that you can't use HAVING
557
573
  # without also specifying a GROUP clause.
558
574
  #
@@ -615,11 +631,12 @@ module ActiveRecord
615
631
  self
616
632
  end
617
633
 
618
- # Returns a chainable relation with zero records.
634
+ # Returns a chainable relation with zero records, specifically an
635
+ # instance of the <tt>ActiveRecord::NullRelation</tt> class.
619
636
  #
620
- # The returned relation implements the Null Object pattern. It is an
621
- # object with defined null behavior and always returns an empty array of
622
- # records without querying the database.
637
+ # The returned <tt>ActiveRecord::NullRelation</tt> inherits from Relation and implements the
638
+ # Null Object pattern. It is an object with defined null behavior and always returns an empty
639
+ # array of records without querying the database.
623
640
  #
624
641
  # Any subsequent condition chained to the returned relation will continue
625
642
  # generating an empty relation and will not fire any query to the database.
@@ -639,7 +656,7 @@ module ActiveRecord
639
656
  # when 'Reviewer'
640
657
  # Post.published
641
658
  # when 'Bad User'
642
- # Post.none # => returning [] instead breaks the previous code
659
+ # Post.none # It can't be chained if [] is returned.
643
660
  # end
644
661
  # end
645
662
  #
@@ -684,20 +701,14 @@ module ActiveRecord
684
701
  end
685
702
 
686
703
  def create_with!(value) # :nodoc:
687
- if value
688
- value = sanitize_forbidden_attributes(value)
689
- self.create_with_value = create_with_value.merge(value)
690
- else
691
- self.create_with_value = {}
692
- end
693
-
704
+ self.create_with_value = value ? create_with_value.merge(value) : {}
694
705
  self
695
706
  end
696
707
 
697
708
  # Specifies table from which the records will be fetched. For example:
698
709
  #
699
710
  # Topic.select('title').from('posts')
700
- # #=> SELECT title FROM posts
711
+ # # => SELECT title FROM posts
701
712
  #
702
713
  # Can accept other relation objects. For example:
703
714
  #
@@ -783,9 +794,10 @@ module ActiveRecord
783
794
  end
784
795
 
785
796
  def extending!(*modules, &block) # :nodoc:
786
- modules << Module.new(&block) if block_given?
797
+ modules << Module.new(&block) if block
798
+ modules.flatten!
787
799
 
788
- self.extending_values += modules.flatten
800
+ self.extending_values += modules
789
801
  extend(*extending_values) if extending_values.any?
790
802
 
791
803
  self
@@ -805,7 +817,7 @@ module ActiveRecord
805
817
 
806
818
  # Returns the Arel object associated with the relation.
807
819
  def arel
808
- @arel ||= with_default_scope.build_arel
820
+ @arel ||= build_arel
809
821
  end
810
822
 
811
823
  # Like #arel, but ignores the default scope of the model.
@@ -816,12 +828,12 @@ module ActiveRecord
816
828
 
817
829
  collapse_wheres(arel, (where_values - ['']).uniq)
818
830
 
819
- arel.having(*having_values.uniq.reject{|h| h.blank?}) unless having_values.empty?
831
+ arel.having(*having_values.uniq.reject(&:blank?)) unless having_values.empty?
820
832
 
821
833
  arel.take(connection.sanitize_limit(limit_value)) if limit_value
822
834
  arel.skip(offset_value.to_i) if offset_value
823
835
 
824
- arel.group(*group_values.uniq.reject{|g| g.blank?}) unless group_values.empty?
836
+ arel.group(*group_values.uniq.reject(&:blank?)) unless group_values.empty?
825
837
 
826
838
  build_order(arel)
827
839
 
@@ -870,13 +882,11 @@ module ActiveRecord
870
882
  end
871
883
 
872
884
  def custom_join_ast(table, joins)
873
- joins = joins.reject { |join| join.blank? }
885
+ joins = joins.reject(&:blank?)
874
886
 
875
887
  return [] if joins.empty?
876
888
 
877
- @implicit_readonly = true
878
-
879
- joins.map do |join|
889
+ joins.map! do |join|
880
890
  case join
881
891
  when Array
882
892
  join = Arel.sql(join.join(' ')) if array_of_strings?(join)
@@ -909,6 +919,7 @@ module ActiveRecord
909
919
 
910
920
  [@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))]
911
921
  when Hash
922
+ opts = PredicateBuilder.resolve_column_aliases(klass, opts)
912
923
  attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts)
913
924
 
914
925
  attributes.values.grep(ActiveRecord::Relation) do |rel|
@@ -940,7 +951,7 @@ module ActiveRecord
940
951
  :string_join
941
952
  when Hash, Symbol, Array
942
953
  :association_join
943
- when ActiveRecord::Associations::JoinDependency::JoinAssociation
954
+ when ActiveRecord::Associations::JoinDependency
944
955
  :stashed_join
945
956
  when Arel::Nodes::Join
946
957
  :join_node
@@ -952,7 +963,7 @@ module ActiveRecord
952
963
  association_joins = buckets[:association_join] || []
953
964
  stashed_association_joins = buckets[:stashed_join] || []
954
965
  join_nodes = (buckets[:join_node] || []).uniq
955
- string_joins = (buckets[:string_join] || []).map { |x| x.strip }.uniq
966
+ string_joins = (buckets[:string_join] || []).map(&:strip).uniq
956
967
 
957
968
  join_list = join_nodes + custom_join_ast(manager, string_joins)
958
969
 
@@ -962,23 +973,17 @@ module ActiveRecord
962
973
  join_list
963
974
  )
964
975
 
965
- join_dependency.graft(*stashed_association_joins)
976
+ joins = join_dependency.join_constraints stashed_association_joins
966
977
 
967
- @implicit_readonly = true unless association_joins.empty? && stashed_association_joins.empty?
978
+ joins.each { |join| manager.from(join) }
968
979
 
969
- # FIXME: refactor this to build an AST
970
- join_dependency.join_associations.each do |association|
971
- association.join_to(manager)
972
- end
973
-
974
- manager.join_sources.concat join_list
980
+ manager.join_sources.concat(join_list)
975
981
 
976
982
  manager
977
983
  end
978
984
 
979
985
  def build_select(arel, selects)
980
986
  unless selects.empty?
981
- @implicit_readonly = false
982
987
  arel.project(*selects)
983
988
  else
984
989
  arel.project(@klass.arel_table[Arel.star])
@@ -993,16 +998,10 @@ module ActiveRecord
993
998
  when Arel::Nodes::Ordering
994
999
  o.reverse
995
1000
  when String
996
- o.to_s.split(',').collect do |s|
1001
+ o.to_s.split(',').map! do |s|
997
1002
  s.strip!
998
1003
  s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC')
999
1004
  end
1000
- when Symbol
1001
- { o => :desc }
1002
- when Hash
1003
- o.each_with_object({}) do |(field, dir), memo|
1004
- memo[field] = (dir == :asc ? :desc : :asc )
1005
- end
1006
1005
  else
1007
1006
  o
1008
1007
  end
@@ -1010,35 +1009,48 @@ module ActiveRecord
1010
1009
  end
1011
1010
 
1012
1011
  def array_of_strings?(o)
1013
- o.is_a?(Array) && o.all?{|obj| obj.is_a?(String)}
1012
+ o.is_a?(Array) && o.all? { |obj| obj.is_a?(String) }
1014
1013
  end
1015
1014
 
1016
1015
  def build_order(arel)
1017
- orders = order_values
1016
+ orders = order_values.uniq
1017
+ orders.reject!(&:blank?)
1018
1018
  orders = reverse_sql_order(orders) if reverse_order_value
1019
1019
 
1020
- orders = orders.uniq.reject(&:blank?).flat_map do |order|
1021
- case order
1022
- when Symbol
1023
- table[order].asc
1024
- when Hash
1025
- order.map { |field, dir| table[field].send(dir) }
1026
- else
1027
- order
1028
- end
1029
- end
1030
-
1031
1020
  arel.order(*orders) unless orders.empty?
1032
1021
  end
1033
1022
 
1034
1023
  def validate_order_args(args)
1035
- args.select { |a| Hash === a }.each do |h|
1024
+ args.grep(Hash) do |h|
1036
1025
  unless (h.values - [:asc, :desc]).empty?
1037
1026
  raise ArgumentError, 'Direction should be :asc or :desc'
1038
1027
  end
1039
1028
  end
1040
1029
  end
1041
1030
 
1031
+ def preprocess_order_args(order_args)
1032
+ order_args.flatten!
1033
+ validate_order_args(order_args)
1034
+
1035
+ references = order_args.grep(String)
1036
+ references.map! { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }.compact!
1037
+ references!(references) if references.any?
1038
+
1039
+ # if a symbol is given we prepend the quoted table name
1040
+ order_args.map! do |arg|
1041
+ case arg
1042
+ when Symbol
1043
+ table[arg].asc
1044
+ when Hash
1045
+ arg.map { |field, dir|
1046
+ table[field].send(dir)
1047
+ }
1048
+ else
1049
+ arg
1050
+ end
1051
+ end.flatten!
1052
+ end
1053
+
1042
1054
  # Checks to make sure that the arguments are not blank. Note that if some
1043
1055
  # blank-like object were initially passed into the query method, then this
1044
1056
  # method will not raise an error.