activerecord 4.1.0 → 4.2.0

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 (185) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +776 -1330
  3. data/README.rdoc +15 -10
  4. data/lib/active_record/aggregations.rb +12 -8
  5. data/lib/active_record/association_relation.rb +4 -0
  6. data/lib/active_record/associations/alias_tracker.rb +14 -13
  7. data/lib/active_record/associations/association.rb +2 -2
  8. data/lib/active_record/associations/association_scope.rb +83 -43
  9. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  10. data/lib/active_record/associations/builder/association.rb +15 -4
  11. data/lib/active_record/associations/builder/belongs_to.rb +7 -29
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +9 -6
  13. data/lib/active_record/associations/builder/has_many.rb +1 -1
  14. data/lib/active_record/associations/builder/has_one.rb +2 -2
  15. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  16. data/lib/active_record/associations/collection_association.rb +66 -29
  17. data/lib/active_record/associations/collection_proxy.rb +22 -26
  18. data/lib/active_record/associations/has_many_association.rb +65 -18
  19. data/lib/active_record/associations/has_many_through_association.rb +55 -27
  20. data/lib/active_record/associations/has_one_association.rb +0 -1
  21. data/lib/active_record/associations/join_dependency/join_association.rb +19 -15
  22. data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
  23. data/lib/active_record/associations/join_dependency.rb +20 -12
  24. data/lib/active_record/associations/preloader/association.rb +34 -11
  25. data/lib/active_record/associations/preloader/through_association.rb +4 -3
  26. data/lib/active_record/associations/preloader.rb +49 -59
  27. data/lib/active_record/associations/singular_association.rb +25 -4
  28. data/lib/active_record/associations/through_association.rb +23 -14
  29. data/lib/active_record/associations.rb +171 -42
  30. data/lib/active_record/attribute.rb +149 -0
  31. data/lib/active_record/attribute_assignment.rb +18 -10
  32. data/lib/active_record/attribute_decorators.rb +66 -0
  33. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
  34. data/lib/active_record/attribute_methods/dirty.rb +98 -44
  35. data/lib/active_record/attribute_methods/primary_key.rb +14 -8
  36. data/lib/active_record/attribute_methods/query.rb +1 -1
  37. data/lib/active_record/attribute_methods/read.rb +22 -59
  38. data/lib/active_record/attribute_methods/serialization.rb +37 -147
  39. data/lib/active_record/attribute_methods/time_zone_conversion.rb +34 -28
  40. data/lib/active_record/attribute_methods/write.rb +14 -21
  41. data/lib/active_record/attribute_methods.rb +67 -94
  42. data/lib/active_record/attribute_set/builder.rb +86 -0
  43. data/lib/active_record/attribute_set.rb +77 -0
  44. data/lib/active_record/attributes.rb +139 -0
  45. data/lib/active_record/autosave_association.rb +45 -38
  46. data/lib/active_record/base.rb +10 -20
  47. data/lib/active_record/callbacks.rb +7 -7
  48. data/lib/active_record/coders/json.rb +13 -0
  49. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +78 -52
  50. data/lib/active_record/connection_adapters/abstract/database_statements.rb +38 -59
  51. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -0
  52. data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -55
  53. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -5
  54. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +126 -54
  55. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
  56. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +198 -64
  57. data/lib/active_record/connection_adapters/abstract/transaction.rb +126 -114
  58. data/lib/active_record/connection_adapters/abstract_adapter.rb +154 -55
  59. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +240 -135
  60. data/lib/active_record/connection_adapters/column.rb +28 -239
  61. data/lib/active_record/connection_adapters/connection_specification.rb +16 -25
  62. data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -22
  63. data/lib/active_record/connection_adapters/mysql_adapter.rb +65 -149
  64. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  65. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  66. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -27
  67. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -0
  68. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  69. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +97 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -374
  93. data/lib/active_record/connection_adapters/postgresql/quoting.rb +55 -135
  94. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  95. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  96. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +127 -38
  97. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  98. data/lib/active_record/connection_adapters/postgresql_adapter.rb +220 -466
  99. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  100. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +66 -61
  101. data/lib/active_record/connection_handling.rb +3 -3
  102. data/lib/active_record/core.rb +143 -32
  103. data/lib/active_record/counter_cache.rb +60 -7
  104. data/lib/active_record/enum.rb +10 -11
  105. data/lib/active_record/errors.rb +49 -27
  106. data/lib/active_record/explain.rb +1 -1
  107. data/lib/active_record/fixtures.rb +56 -70
  108. data/lib/active_record/gem_version.rb +2 -2
  109. data/lib/active_record/inheritance.rb +35 -10
  110. data/lib/active_record/integration.rb +4 -4
  111. data/lib/active_record/locking/optimistic.rb +35 -17
  112. data/lib/active_record/log_subscriber.rb +1 -1
  113. data/lib/active_record/migration/command_recorder.rb +19 -2
  114. data/lib/active_record/migration/join_table.rb +1 -1
  115. data/lib/active_record/migration.rb +52 -49
  116. data/lib/active_record/model_schema.rb +49 -57
  117. data/lib/active_record/nested_attributes.rb +7 -7
  118. data/lib/active_record/null_relation.rb +19 -5
  119. data/lib/active_record/persistence.rb +50 -31
  120. data/lib/active_record/query_cache.rb +3 -3
  121. data/lib/active_record/querying.rb +10 -7
  122. data/lib/active_record/railtie.rb +14 -11
  123. data/lib/active_record/railties/databases.rake +56 -54
  124. data/lib/active_record/readonly_attributes.rb +0 -1
  125. data/lib/active_record/reflection.rb +286 -102
  126. data/lib/active_record/relation/batches.rb +0 -1
  127. data/lib/active_record/relation/calculations.rb +39 -31
  128. data/lib/active_record/relation/delegation.rb +2 -2
  129. data/lib/active_record/relation/finder_methods.rb +80 -36
  130. data/lib/active_record/relation/merger.rb +25 -30
  131. data/lib/active_record/relation/predicate_builder/array_handler.rb +31 -13
  132. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  133. data/lib/active_record/relation/predicate_builder.rb +11 -10
  134. data/lib/active_record/relation/query_methods.rb +141 -55
  135. data/lib/active_record/relation/spawn_methods.rb +3 -0
  136. data/lib/active_record/relation.rb +69 -30
  137. data/lib/active_record/result.rb +18 -7
  138. data/lib/active_record/sanitization.rb +12 -2
  139. data/lib/active_record/schema.rb +0 -1
  140. data/lib/active_record/schema_dumper.rb +58 -26
  141. data/lib/active_record/schema_migration.rb +11 -0
  142. data/lib/active_record/scoping/default.rb +8 -7
  143. data/lib/active_record/scoping/named.rb +4 -0
  144. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  145. data/lib/active_record/statement_cache.rb +95 -10
  146. data/lib/active_record/store.rb +19 -10
  147. data/lib/active_record/tasks/database_tasks.rb +73 -7
  148. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -2
  149. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  150. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  151. data/lib/active_record/timestamp.rb +11 -9
  152. data/lib/active_record/transactions.rb +37 -21
  153. data/lib/active_record/type/big_integer.rb +13 -0
  154. data/lib/active_record/type/binary.rb +50 -0
  155. data/lib/active_record/type/boolean.rb +30 -0
  156. data/lib/active_record/type/date.rb +46 -0
  157. data/lib/active_record/type/date_time.rb +43 -0
  158. data/lib/active_record/type/decimal.rb +40 -0
  159. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  160. data/lib/active_record/type/decorator.rb +14 -0
  161. data/lib/active_record/type/float.rb +19 -0
  162. data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
  163. data/lib/active_record/type/integer.rb +55 -0
  164. data/lib/active_record/type/mutable.rb +16 -0
  165. data/lib/active_record/type/numeric.rb +36 -0
  166. data/lib/active_record/type/serialized.rb +56 -0
  167. data/lib/active_record/type/string.rb +36 -0
  168. data/lib/active_record/type/text.rb +11 -0
  169. data/lib/active_record/type/time.rb +26 -0
  170. data/lib/active_record/type/time_value.rb +38 -0
  171. data/lib/active_record/type/type_map.rb +64 -0
  172. data/lib/active_record/type/unsigned_integer.rb +15 -0
  173. data/lib/active_record/type/value.rb +101 -0
  174. data/lib/active_record/type.rb +23 -0
  175. data/lib/active_record/validations/associated.rb +5 -3
  176. data/lib/active_record/validations/presence.rb +6 -4
  177. data/lib/active_record/validations/uniqueness.rb +11 -17
  178. data/lib/active_record/validations.rb +25 -19
  179. data/lib/active_record.rb +3 -0
  180. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
  181. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +4 -1
  182. data/lib/rails/generators/active_record/migration/templates/migration.rb +6 -0
  183. data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
  184. metadata +65 -10
  185. data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -26,7 +26,7 @@ module ActiveRecord
26
26
  queries << '1=0'
27
27
  else
28
28
  table = Arel::Table.new(column, default_table.engine)
29
- association = klass.reflect_on_association(column.to_sym)
29
+ association = klass._reflect_on_association(column)
30
30
 
31
31
  value.each do |k, v|
32
32
  queries.concat expand(association && association.klass, table, k, v)
@@ -55,7 +55,7 @@ module ActiveRecord
55
55
  #
56
56
  # For polymorphic relationships, find the foreign key and type:
57
57
  # PriceEstimate.where(estimate_of: treasure)
58
- if klass && reflection = klass.reflect_on_association(column.to_sym)
58
+ if klass && reflection = klass._reflect_on_association(column)
59
59
  if reflection.polymorphic? && base_class = polymorphic_base_class_from_value(value)
60
60
  queries << build(table[reflection.foreign_type], base_class)
61
61
  end
@@ -109,17 +109,18 @@ module ActiveRecord
109
109
  # FIXME: I think we need to deprecate this behavior
110
110
  register_handler(Class, ->(attribute, value) { attribute.eq(value.name) })
111
111
  register_handler(Base, ->(attribute, value) { attribute.eq(value.id) })
112
- register_handler(Range, ->(attribute, value) { attribute.in(value) })
112
+ register_handler(Range, ->(attribute, value) { attribute.between(value) })
113
113
  register_handler(Relation, RelationHandler.new)
114
114
  register_handler(Array, ArrayHandler.new)
115
115
 
116
- private
117
- def self.build(attribute, value)
118
- handler_for(value).call(attribute, value)
119
- end
116
+ def self.build(attribute, value)
117
+ handler_for(value).call(attribute, value)
118
+ end
119
+ private_class_method :build
120
120
 
121
- def self.handler_for(object)
122
- @handlers.detect { |klass, _| klass === object }.last
123
- end
121
+ def self.handler_for(object)
122
+ @handlers.detect { |klass, _| klass === object }.last
123
+ end
124
+ private_class_method :handler_for
124
125
  end
125
126
  end
@@ -1,9 +1,13 @@
1
1
  require 'active_support/core_ext/array/wrap'
2
+ require 'active_support/core_ext/string/filters'
3
+ require 'active_model/forbidden_attributes_protection'
2
4
 
3
5
  module ActiveRecord
4
6
  module QueryMethods
5
7
  extend ActiveSupport::Concern
6
8
 
9
+ include ActiveModel::ForbiddenAttributesProtection
10
+
7
11
  # WhereChain objects act as placeholder for queries in which #where does not have any parameter.
8
12
  # In this case, #where must be chained with #not to return a new relation.
9
13
  class WhereChain
@@ -64,6 +68,7 @@ module ActiveRecord
64
68
  #
65
69
  def #{name}_values=(values) # def select_values=(values)
66
70
  raise ImmutableRelation if @loaded # raise ImmutableRelation if @loaded
71
+ check_cached_relation
67
72
  @values[:#{name}] = values # @values[:select] = values
68
73
  end # end
69
74
  CODE
@@ -81,11 +86,22 @@ module ActiveRecord
81
86
  class_eval <<-CODE, __FILE__, __LINE__ + 1
82
87
  def #{name}_value=(value) # def readonly_value=(value)
83
88
  raise ImmutableRelation if @loaded # raise ImmutableRelation if @loaded
89
+ check_cached_relation
84
90
  @values[:#{name}] = value # @values[:readonly] = value
85
91
  end # end
86
92
  CODE
87
93
  end
88
94
 
95
+ def check_cached_relation # :nodoc:
96
+ if defined?(@arel) && @arel
97
+ @arel = nil
98
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
99
+ Modifying already cached Relation. The cache will be reset. Use a
100
+ cloned Relation to prevent this warning.
101
+ MSG
102
+ end
103
+ end
104
+
89
105
  def create_with_value # :nodoc:
90
106
  @values[:create_with] || {}
91
107
  end
@@ -170,7 +186,7 @@ module ActiveRecord
170
186
 
171
187
  # Use to indicate that the given +table_names+ are referenced by an SQL string,
172
188
  # and should therefore be JOINed in any query rather than loaded separately.
173
- # This method only works in conjuction with +includes+.
189
+ # This method only works in conjunction with +includes+.
174
190
  # See #includes for more details.
175
191
  #
176
192
  # User.includes(:posts).where("posts.name = 'foo'")
@@ -204,7 +220,7 @@ module ActiveRecord
204
220
  # fields are retrieved:
205
221
  #
206
222
  # Model.select(:field)
207
- # # => [#<Model field:value>]
223
+ # # => [#<Model id: nil, field: "value">]
208
224
  #
209
225
  # Although in the above example it looks as though this method returns an
210
226
  # array, it actually returns a relation object and can have other query
@@ -213,12 +229,12 @@ module ActiveRecord
213
229
  # The argument to the method can also be an array of fields.
214
230
  #
215
231
  # Model.select(:field, :other_field, :and_one_more)
216
- # # => [#<Model field: "value", other_field: "value", and_one_more: "value">]
232
+ # # => [#<Model id: nil, field: "value", other_field: "value", and_one_more: "value">]
217
233
  #
218
234
  # You can also use one or more strings, which will be used unchanged as SELECT fields.
219
235
  #
220
236
  # Model.select('field AS field_one', 'other_field AS field_two')
221
- # # => [#<Model field: "value", other_field: "value">]
237
+ # # => [#<Model id: nil, field: "value", other_field: "value">]
222
238
  #
223
239
  # If an alias was specified, it will be accessible from the resulting objects:
224
240
  #
@@ -226,7 +242,7 @@ module ActiveRecord
226
242
  # # => "value"
227
243
  #
228
244
  # Accessing attributes of an object that do not have fields retrieved by a select
229
- # will throw <tt>ActiveModel::MissingAttributeError</tt>:
245
+ # except +id+ will throw <tt>ActiveModel::MissingAttributeError</tt>:
230
246
  #
231
247
  # Model.select(:field).first.other_field
232
248
  # # => ActiveModel::MissingAttributeError: missing attribute: other_field
@@ -235,11 +251,11 @@ module ActiveRecord
235
251
  to_a.select { |*block_args| yield(*block_args) }
236
252
  else
237
253
  raise ArgumentError, 'Call this with at least one field' if fields.empty?
238
- spawn.select!(*fields)
254
+ spawn._select!(*fields)
239
255
  end
240
256
  end
241
257
 
242
- def select!(*fields) # :nodoc:
258
+ def _select!(*fields) # :nodoc:
243
259
  fields.flatten!
244
260
  fields.map! do |field|
245
261
  klass.attribute_alias?(field) ? klass.attribute_alias(field) : field
@@ -263,6 +279,10 @@ module ActiveRecord
263
279
  #
264
280
  # User.group('name AS grouped_name, age')
265
281
  # => [#<User id: 3, name: "Foo", age: 21, ...>, #<User id: 2, name: "Oscar", age: 21, ...>, #<User id: 5, name: "Foo", age: 23, ...>]
282
+ #
283
+ # Passing in an array of attributes to group by is also supported.
284
+ # User.select([:id, :first_name]).group(:id, :first_name).first(3)
285
+ # => [#<User id: 1, first_name: "Bill">, #<User id: 2, first_name: "Earl">, #<User id: 3, first_name: "Beto">]
266
286
  def group(*args)
267
287
  check_if_method_has_arguments!(:group, args)
268
288
  spawn.group!(*args)
@@ -277,15 +297,6 @@ module ActiveRecord
277
297
 
278
298
  # Allows to specify an order attribute:
279
299
  #
280
- # User.order('name')
281
- # => SELECT "users".* FROM "users" ORDER BY name
282
- #
283
- # User.order('name DESC')
284
- # => SELECT "users".* FROM "users" ORDER BY name DESC
285
- #
286
- # User.order('name DESC, email')
287
- # => SELECT "users".* FROM "users" ORDER BY name DESC, email
288
- #
289
300
  # User.order(:name)
290
301
  # => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC
291
302
  #
@@ -294,6 +305,15 @@ module ActiveRecord
294
305
  #
295
306
  # User.order(:name, email: :desc)
296
307
  # => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC
308
+ #
309
+ # User.order('name')
310
+ # => SELECT "users".* FROM "users" ORDER BY name
311
+ #
312
+ # User.order('name DESC')
313
+ # => SELECT "users".* FROM "users" ORDER BY name DESC
314
+ #
315
+ # User.order('name DESC, email')
316
+ # => SELECT "users".* FROM "users" ORDER BY name DESC, email
297
317
  def order(*args)
298
318
  check_if_method_has_arguments!(:order, args)
299
319
  spawn.order!(*args)
@@ -407,19 +427,17 @@ module ActiveRecord
407
427
  # => SELECT "users".* FROM "users" LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id
408
428
  def joins(*args)
409
429
  check_if_method_has_arguments!(:joins, args)
410
-
411
- args.compact!
412
- args.flatten!
413
-
414
430
  spawn.joins!(*args)
415
431
  end
416
432
 
417
433
  def joins!(*args) # :nodoc:
434
+ args.compact!
435
+ args.flatten!
418
436
  self.joins_values += args
419
437
  self
420
438
  end
421
439
 
422
- def bind(value)
440
+ def bind(value) # :nodoc:
423
441
  spawn.bind!(value)
424
442
  end
425
443
 
@@ -557,15 +575,14 @@ module ActiveRecord
557
575
  end
558
576
  end
559
577
 
560
- def where!(opts = :chain, *rest) # :nodoc:
561
- if opts == :chain
562
- WhereChain.new(self)
563
- else
564
- references!(PredicateBuilder.references(opts)) if Hash === opts
565
-
566
- self.where_values += build_where(opts, rest)
567
- self
578
+ def where!(opts, *rest) # :nodoc:
579
+ if Hash === opts
580
+ opts = sanitize_forbidden_attributes(opts)
581
+ references!(PredicateBuilder.references(opts))
568
582
  end
583
+
584
+ self.where_values += build_where(opts, rest)
585
+ self
569
586
  end
570
587
 
571
588
  # Allows you to change a previously set where condition for a given attribute, instead of appending to that condition.
@@ -671,11 +688,11 @@ module ActiveRecord
671
688
  # end
672
689
  #
673
690
  def none
674
- extending(NullRelation)
691
+ where("1=0").extending!(NullRelation)
675
692
  end
676
693
 
677
694
  def none! # :nodoc:
678
- extending!(NullRelation)
695
+ where!("1=0").extending!(NullRelation)
679
696
  end
680
697
 
681
698
  # Sets readonly attributes for the returned relation. If value is
@@ -711,7 +728,13 @@ module ActiveRecord
711
728
  end
712
729
 
713
730
  def create_with!(value) # :nodoc:
714
- self.create_with_value = value ? create_with_value.merge(value) : {}
731
+ if value
732
+ value = sanitize_forbidden_attributes(value)
733
+ self.create_with_value = create_with_value.merge(value)
734
+ else
735
+ self.create_with_value = {}
736
+ end
737
+
715
738
  self
716
739
  end
717
740
 
@@ -821,7 +844,9 @@ module ActiveRecord
821
844
  end
822
845
 
823
846
  def reverse_order! # :nodoc:
824
- self.reverse_order_value = !reverse_order_value
847
+ orders = order_values.uniq
848
+ orders.reject!(&:blank?)
849
+ self.order_values = reverse_sql_order(orders)
825
850
  self
826
851
  end
827
852
 
@@ -837,7 +862,7 @@ module ActiveRecord
837
862
 
838
863
  build_joins(arel, joins_values.flatten) unless joins_values.empty?
839
864
 
840
- collapse_wheres(arel, (where_values - ['']).uniq)
865
+ collapse_wheres(arel, (where_values - [''])) #TODO: Add uniq with real value comparison / ignore uniqs that have binds
841
866
 
842
867
  arel.having(*having_values.uniq.reject(&:blank?)) unless having_values.empty?
843
868
 
@@ -867,8 +892,9 @@ module ActiveRecord
867
892
 
868
893
  case scope
869
894
  when :order
870
- self.reverse_order_value = false
871
895
  result = []
896
+ when :where
897
+ self.bind_values = []
872
898
  else
873
899
  result = [] unless single_val_method
874
900
  end
@@ -881,7 +907,7 @@ module ActiveRecord
881
907
 
882
908
  where_values.reject! do |rel|
883
909
  case rel
884
- when Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual
910
+ when Arel::Nodes::Between, Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThanOrEqual
885
911
  subrelation = (rel.left.kind_of?(Arel::Attributes::Attribute) ? rel.left : rel.right)
886
912
  subrelation.name == target_value
887
913
  end
@@ -919,21 +945,15 @@ module ActiveRecord
919
945
  def build_where(opts, other = [])
920
946
  case opts
921
947
  when String, Array
922
- #TODO: Remove duplication with: /activerecord/lib/active_record/sanitization.rb:113
923
- values = Hash === other.first ? other.first.values : other
924
-
925
- values.grep(ActiveRecord::Relation) do |rel|
926
- self.bind_values += rel.bind_values
927
- end
928
-
929
948
  [@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))]
930
949
  when Hash
931
950
  opts = PredicateBuilder.resolve_column_aliases(klass, opts)
932
- attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts)
933
951
 
934
- attributes.values.grep(ActiveRecord::Relation) do |rel|
935
- self.bind_values += rel.bind_values
936
- end
952
+ tmp_opts, bind_values = create_binds(opts)
953
+ self.bind_values += bind_values
954
+
955
+ attributes = @klass.send(:expand_hash_conditions_for_aggregates, tmp_opts)
956
+ add_relations_to_bind_values(attributes)
937
957
 
938
958
  PredicateBuilder.build_from_hash(klass, attributes, table)
939
959
  else
@@ -941,6 +961,46 @@ module ActiveRecord
941
961
  end
942
962
  end
943
963
 
964
+ def create_binds(opts)
965
+ bindable, non_binds = opts.partition do |column, value|
966
+ case value
967
+ when String, Integer, ActiveRecord::StatementCache::Substitute
968
+ @klass.columns_hash.include? column.to_s
969
+ else
970
+ false
971
+ end
972
+ end
973
+
974
+ association_binds, non_binds = non_binds.partition do |column, value|
975
+ value.is_a?(Hash) && association_for_table(column)
976
+ end
977
+
978
+ new_opts = {}
979
+ binds = []
980
+
981
+ bindable.each do |(column,value)|
982
+ binds.push [@klass.columns_hash[column.to_s], value]
983
+ new_opts[column] = connection.substitute_at(column)
984
+ end
985
+
986
+ association_binds.each do |(column, value)|
987
+ association_relation = association_for_table(column).klass.send(:relation)
988
+ association_new_opts, association_bind = association_relation.send(:create_binds, value)
989
+ new_opts[column] = association_new_opts
990
+ binds += association_bind
991
+ end
992
+
993
+ non_binds.each { |column,value| new_opts[column] = value }
994
+
995
+ [new_opts, binds]
996
+ end
997
+
998
+ def association_for_table(table_name)
999
+ table_name = table_name.to_s
1000
+ @klass._reflect_on_association(table_name) ||
1001
+ @klass._reflect_on_association(table_name.singularize)
1002
+ end
1003
+
944
1004
  def build_from
945
1005
  opts, name = from_value
946
1006
  case opts
@@ -982,9 +1042,12 @@ module ActiveRecord
982
1042
  join_list
983
1043
  )
984
1044
 
985
- joins = join_dependency.join_constraints stashed_association_joins
1045
+ join_infos = join_dependency.join_constraints stashed_association_joins
986
1046
 
987
- joins.each { |join| manager.from(join) }
1047
+ join_infos.each do |info|
1048
+ info.joins.each { |join| manager.from(join) }
1049
+ manager.bind_values.concat info.binds
1050
+ end
988
1051
 
989
1052
  manager.join_sources.concat(join_list)
990
1053
 
@@ -994,8 +1057,13 @@ module ActiveRecord
994
1057
  def build_select(arel, selects)
995
1058
  if !selects.empty?
996
1059
  expanded_select = selects.map do |field|
997
- columns_hash.key?(field.to_s) ? arel_table[field] : field
1060
+ if (Symbol === field || String === field) && columns_hash.key?(field.to_s)
1061
+ arel_table[field]
1062
+ else
1063
+ field
1064
+ end
998
1065
  end
1066
+
999
1067
  arel.project(*expanded_select)
1000
1068
  else
1001
1069
  arel.project(@klass.arel_table[Arel.star])
@@ -1027,15 +1095,19 @@ module ActiveRecord
1027
1095
  def build_order(arel)
1028
1096
  orders = order_values.uniq
1029
1097
  orders.reject!(&:blank?)
1030
- orders = reverse_sql_order(orders) if reverse_order_value
1031
1098
 
1032
1099
  arel.order(*orders) unless orders.empty?
1033
1100
  end
1034
1101
 
1102
+ VALID_DIRECTIONS = [:asc, :desc, :ASC, :DESC,
1103
+ 'asc', 'desc', 'ASC', 'DESC'] # :nodoc:
1104
+
1035
1105
  def validate_order_args(args)
1036
- args.grep(Hash) do |h|
1037
- unless (h.values - [:asc, :desc]).empty?
1038
- raise ArgumentError, 'Direction should be :asc or :desc'
1106
+ args.each do |arg|
1107
+ next unless arg.is_a?(Hash)
1108
+ arg.each do |_key, value|
1109
+ raise ArgumentError, "Direction \"#{value}\" is invalid. Valid " \
1110
+ "directions are: #{VALID_DIRECTIONS.inspect}" unless VALID_DIRECTIONS.include?(value)
1039
1111
  end
1040
1112
  end
1041
1113
  end
@@ -1057,7 +1129,7 @@ module ActiveRecord
1057
1129
  when Hash
1058
1130
  arg.map { |field, dir|
1059
1131
  field = klass.attribute_alias(field) if klass.attribute_alias?(field)
1060
- table[field].send(dir)
1132
+ table[field].send(dir.downcase)
1061
1133
  }
1062
1134
  else
1063
1135
  arg
@@ -1086,5 +1158,19 @@ module ActiveRecord
1086
1158
  raise ArgumentError, "The method .#{method_name}() must contain arguments."
1087
1159
  end
1088
1160
  end
1161
+
1162
+ # This function is recursive just for better readablity.
1163
+ # #where argument doesn't support more than one level nested hash in real world.
1164
+ def add_relations_to_bind_values(attributes)
1165
+ if attributes.is_a?(Hash)
1166
+ attributes.each_value do |value|
1167
+ if value.is_a?(ActiveRecord::Relation)
1168
+ self.bind_values += value.bind_values
1169
+ else
1170
+ add_relations_to_bind_values(value)
1171
+ end
1172
+ end
1173
+ end
1174
+ end
1089
1175
  end
1090
1176
  end
@@ -58,6 +58,9 @@ module ActiveRecord
58
58
  # Post.order('id asc').only(:where) # discards the order condition
59
59
  # Post.order('id asc').only(:where, :order) # uses the specified order
60
60
  def only(*onlies)
61
+ if onlies.any? { |o| o == :where }
62
+ onlies << :bind
63
+ end
61
64
  relation_with values.slice(*onlies)
62
65
  end
63
66
 
@@ -1,16 +1,16 @@
1
1
  # -*- coding: utf-8 -*-
2
+ require 'arel/collectors/bind'
2
3
 
3
4
  module ActiveRecord
4
5
  # = Active Record Relation
5
6
  class Relation
6
- JoinOperation = Struct.new(:relation, :join_class, :on)
7
-
8
7
  MULTI_VALUE_METHODS = [:includes, :eager_load, :preload, :select, :group,
9
8
  :order, :joins, :where, :having, :bind, :references,
10
9
  :extending, :unscope]
11
10
 
12
11
  SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :from, :reordering,
13
12
  :reverse_order, :distinct, :create_with, :uniq]
13
+ INVALID_METHODS_FOR_DELETE_ALL = [:limit, :distinct, :offset, :group, :having]
14
14
 
15
15
  VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS
16
16
 
@@ -70,24 +70,35 @@ module ActiveRecord
70
70
  binds)
71
71
  end
72
72
 
73
- def update_record(values, id, id_was) # :nodoc:
73
+ def _update_record(values, id, id_was) # :nodoc:
74
74
  substitutes, binds = substitute_values values
75
- um = @klass.unscoped.where(@klass.arel_table[@klass.primary_key].eq(id_was || id)).arel.compile_update(substitutes, @klass.primary_key)
75
+
76
+ scope = @klass.unscoped
77
+
78
+ if @klass.finder_needs_type_condition?
79
+ scope.unscope!(where: @klass.inheritance_column)
80
+ end
81
+
82
+ relation = scope.where(@klass.primary_key => (id_was || id))
83
+ bvs = binds + relation.bind_values
84
+ um = relation
85
+ .arel
86
+ .compile_update(substitutes, @klass.primary_key)
76
87
 
77
88
  @klass.connection.update(
78
89
  um,
79
90
  'SQL',
80
- binds)
91
+ bvs,
92
+ )
81
93
  end
82
94
 
83
95
  def substitute_values(values) # :nodoc:
84
- substitutes = values.sort_by { |arel_attr,_| arel_attr.name }
85
- binds = substitutes.map do |arel_attr, value|
96
+ binds = values.map do |arel_attr, value|
86
97
  [@klass.columns_hash[arel_attr.name], value]
87
98
  end
88
99
 
89
- substitutes.each_with_index do |tuple, i|
90
- tuple[1] = @klass.connection.substitute_at(binds[i][0], i)
100
+ substitutes = values.each_with_index.map do |(arel_attr, _), i|
101
+ [arel_attr, @klass.connection.substitute_at(binds[i][0])]
91
102
  end
92
103
 
93
104
  [substitutes, binds]
@@ -223,6 +234,7 @@ module ActiveRecord
223
234
  # Please see further details in the
224
235
  # {Active Record Query Interface guide}[http://guides.rubyonrails.org/active_record_querying.html#running-explain].
225
236
  def explain
237
+ #TODO: Fix for binds.
226
238
  exec_explain(collecting_queries_for_explain { exec_queries })
227
239
  end
228
240
 
@@ -232,13 +244,18 @@ module ActiveRecord
232
244
  @records
233
245
  end
234
246
 
247
+ # Serializes the relation objects Array.
248
+ def encode_with(coder)
249
+ coder.represent_seq(nil, to_a)
250
+ end
251
+
235
252
  def as_json(options = nil) #:nodoc:
236
253
  to_a.as_json(options)
237
254
  end
238
255
 
239
256
  # Returns size of the records.
240
257
  def size
241
- loaded? ? @records.length : count
258
+ loaded? ? @records.length : count(:all)
242
259
  end
243
260
 
244
261
  # Returns true if there are no records.
@@ -248,8 +265,7 @@ module ActiveRecord
248
265
  if limit_value == 0
249
266
  true
250
267
  else
251
- # FIXME: This count is not compatible with #select('authors.*') or other select narrows
252
- c = count
268
+ c = count(:all)
253
269
  c.respond_to?(:zero?) ? c.zero? : c.empty?
254
270
  end
255
271
  end
@@ -288,10 +304,11 @@ module ActiveRecord
288
304
  klass.current_scope = previous
289
305
  end
290
306
 
291
- # Updates all records with details given if they match a set of conditions supplied, limits and order can
292
- # also be supplied. This method constructs a single SQL UPDATE statement and sends it straight to the
293
- # database. It does not instantiate the involved models and it does not trigger Active Record callbacks
294
- # or validations.
307
+ # Updates all records in the current relation with details given. This method constructs a single SQL UPDATE
308
+ # statement and sends it straight to the database. It does not instantiate the involved models and it does not
309
+ # trigger Active Record callbacks or validations. Values passed to `update_all` will not go through
310
+ # ActiveRecord's type-casting behavior. It should receive only values that can be passed as-is to the SQL
311
+ # database.
295
312
  #
296
313
  # ==== Parameters
297
314
  #
@@ -324,7 +341,8 @@ module ActiveRecord
324
341
  stmt.wheres = arel.constraints
325
342
  end
326
343
 
327
- @klass.connection.update stmt, 'SQL', bind_values
344
+ bvs = arel.bind_values + bind_values
345
+ @klass.connection.update stmt, 'SQL', bvs
328
346
  end
329
347
 
330
348
  # Updates an object (or multiple objects) and saves it to the database, if validations pass.
@@ -428,12 +446,21 @@ module ActiveRecord
428
446
  # If you need to destroy dependent associations or call your <tt>before_*</tt> or
429
447
  # +after_destroy+ callbacks, use the +destroy_all+ method instead.
430
448
  #
431
- # If a limit scope is supplied, +delete_all+ raises an ActiveRecord error:
449
+ # If an invalid method is supplied, +delete_all+ raises an ActiveRecord error:
432
450
  #
433
451
  # Post.limit(100).delete_all
434
- # # => ActiveRecord::ActiveRecordError: delete_all doesn't support limit scope
452
+ # # => ActiveRecord::ActiveRecordError: delete_all doesn't support limit
435
453
  def delete_all(conditions = nil)
436
- raise ActiveRecordError.new("delete_all doesn't support limit scope") if self.limit_value
454
+ invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select { |method|
455
+ if MULTI_VALUE_METHODS.include?(method)
456
+ send("#{method}_values").any?
457
+ else
458
+ send("#{method}_value")
459
+ end
460
+ }
461
+ if invalid_methods.any?
462
+ raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
463
+ end
437
464
 
438
465
  if conditions
439
466
  where(conditions).delete_all
@@ -517,11 +544,11 @@ module ActiveRecord
517
544
  find_with_associations { |rel| relation = rel }
518
545
  end
519
546
 
520
- ast = relation.arel.ast
521
- binds = relation.bind_values.dup
522
- visitor.accept(ast) do
523
- connection.quote(*binds.shift.reverse)
524
- end
547
+ arel = relation.arel
548
+ binds = (arel.bind_values + relation.bind_values).dup
549
+ binds.map! { |bv| connection.quote(*bv.reverse) }
550
+ collect = visitor.accept(arel.ast, Arel::Collectors::Bind.new)
551
+ collect.substitute_binds(binds).join
525
552
  end
526
553
  end
527
554
 
@@ -529,16 +556,22 @@ module ActiveRecord
529
556
  #
530
557
  # User.where(name: 'Oscar').where_values_hash
531
558
  # # => {name: "Oscar"}
532
- def where_values_hash
559
+ def where_values_hash(relation_table_name = table_name)
533
560
  equalities = where_values.grep(Arel::Nodes::Equality).find_all { |node|
534
- node.left.relation.name == table_name
561
+ node.left.relation.name == relation_table_name
535
562
  }
536
563
 
537
564
  binds = Hash[bind_values.find_all(&:first).map { |column, v| [column.name, v] }]
538
565
 
539
566
  Hash[equalities.map { |where|
540
567
  name = where.left.name
541
- [name, binds.fetch(name.to_s) { where.right }]
568
+ [name, binds.fetch(name.to_s) {
569
+ case where.right
570
+ when Array then where.right.map(&:val)
571
+ else
572
+ where.right.val
573
+ end
574
+ }]
542
575
  }]
543
576
  end
544
577
 
@@ -570,6 +603,8 @@ module ActiveRecord
570
603
  # Compares two relations for equality.
571
604
  def ==(other)
572
605
  case other
606
+ when Associations::CollectionProxy, AssociationRelation
607
+ self == other.to_a
573
608
  when Relation
574
609
  other.to_sql == to_sql
575
610
  when Array
@@ -600,11 +635,11 @@ module ActiveRecord
600
635
  private
601
636
 
602
637
  def exec_queries
603
- @records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel, bind_values)
638
+ @records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel, arel.bind_values + bind_values)
604
639
 
605
640
  preload = preload_values
606
641
  preload += includes_values unless eager_loading?
607
- preloader = ActiveRecord::Associations::Preloader.new
642
+ preloader = build_preloader
608
643
  preload.each do |associations|
609
644
  preloader.preload @records, associations
610
645
  end
@@ -615,6 +650,10 @@ module ActiveRecord
615
650
  @records
616
651
  end
617
652
 
653
+ def build_preloader
654
+ ActiveRecord::Associations::Preloader.new
655
+ end
656
+
618
657
  def references_eager_loaded_tables?
619
658
  joined_tables = arel.join_sources.map do |join|
620
659
  if join.is_a?(Arel::Nodes::StringJoin)