activerecord 4.2.11.3 → 5.0.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 (229) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1029 -1349
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -7
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record.rb +7 -3
  7. data/lib/active_record/aggregations.rb +35 -25
  8. data/lib/active_record/association_relation.rb +2 -2
  9. data/lib/active_record/associations.rb +305 -204
  10. data/lib/active_record/associations/alias_tracker.rb +19 -16
  11. data/lib/active_record/associations/association.rb +10 -8
  12. data/lib/active_record/associations/association_scope.rb +73 -102
  13. data/lib/active_record/associations/belongs_to_association.rb +20 -32
  14. data/lib/active_record/associations/builder/association.rb +28 -34
  15. data/lib/active_record/associations/builder/belongs_to.rb +41 -18
  16. data/lib/active_record/associations/builder/collection_association.rb +8 -24
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +11 -11
  18. data/lib/active_record/associations/builder/has_many.rb +4 -4
  19. data/lib/active_record/associations/builder/has_one.rb +10 -5
  20. data/lib/active_record/associations/builder/singular_association.rb +2 -9
  21. data/lib/active_record/associations/collection_association.rb +40 -43
  22. data/lib/active_record/associations/collection_proxy.rb +55 -29
  23. data/lib/active_record/associations/foreign_association.rb +1 -1
  24. data/lib/active_record/associations/has_many_association.rb +20 -71
  25. data/lib/active_record/associations/has_many_through_association.rb +8 -52
  26. data/lib/active_record/associations/has_one_association.rb +12 -5
  27. data/lib/active_record/associations/join_dependency.rb +28 -18
  28. data/lib/active_record/associations/join_dependency/join_association.rb +13 -12
  29. data/lib/active_record/associations/preloader.rb +13 -4
  30. data/lib/active_record/associations/preloader/association.rb +45 -51
  31. data/lib/active_record/associations/preloader/collection_association.rb +0 -6
  32. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  33. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  34. data/lib/active_record/associations/preloader/through_association.rb +5 -4
  35. data/lib/active_record/associations/singular_association.rb +6 -0
  36. data/lib/active_record/associations/through_association.rb +11 -3
  37. data/lib/active_record/attribute.rb +61 -17
  38. data/lib/active_record/attribute/user_provided_default.rb +23 -0
  39. data/lib/active_record/attribute_assignment.rb +27 -140
  40. data/lib/active_record/attribute_decorators.rb +6 -5
  41. data/lib/active_record/attribute_methods.rb +79 -26
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
  43. data/lib/active_record/attribute_methods/dirty.rb +46 -86
  44. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  45. data/lib/active_record/attribute_methods/query.rb +2 -2
  46. data/lib/active_record/attribute_methods/read.rb +26 -42
  47. data/lib/active_record/attribute_methods/serialization.rb +13 -16
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +42 -9
  49. data/lib/active_record/attribute_methods/write.rb +13 -24
  50. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  51. data/lib/active_record/attribute_set.rb +30 -3
  52. data/lib/active_record/attribute_set/builder.rb +6 -4
  53. data/lib/active_record/attributes.rb +194 -81
  54. data/lib/active_record/autosave_association.rb +33 -15
  55. data/lib/active_record/base.rb +30 -18
  56. data/lib/active_record/callbacks.rb +36 -40
  57. data/lib/active_record/coders/yaml_column.rb +20 -8
  58. data/lib/active_record/collection_cache_key.rb +31 -0
  59. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +431 -122
  60. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  61. data/lib/active_record/connection_adapters/abstract/database_statements.rb +40 -22
  62. data/lib/active_record/connection_adapters/abstract/quoting.rb +62 -8
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -38
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +229 -185
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +52 -13
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +275 -115
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +32 -33
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +83 -32
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +384 -221
  70. data/lib/active_record/connection_adapters/column.rb +27 -41
  71. data/lib/active_record/connection_adapters/connection_specification.rb +2 -21
  72. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +57 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +69 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +59 -0
  76. data/lib/active_record/connection_adapters/mysql2_adapter.rb +22 -101
  77. data/lib/active_record/connection_adapters/postgresql/column.rb +6 -10
  78. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +3 -3
  79. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  80. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +23 -57
  81. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
  84. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
  85. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
  86. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  87. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  90. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +23 -16
  92. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
  93. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  96. data/lib/active_record/connection_adapters/postgresql/quoting.rb +18 -11
  97. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  99. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +54 -0
  100. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +174 -128
  101. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  102. data/lib/active_record/connection_adapters/postgresql_adapter.rb +184 -112
  103. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  104. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  105. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +15 -0
  106. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +134 -110
  107. data/lib/active_record/connection_adapters/statement_pool.rb +28 -11
  108. data/lib/active_record/connection_handling.rb +5 -5
  109. data/lib/active_record/core.rb +72 -104
  110. data/lib/active_record/counter_cache.rb +9 -20
  111. data/lib/active_record/dynamic_matchers.rb +1 -20
  112. data/lib/active_record/enum.rb +110 -76
  113. data/lib/active_record/errors.rb +72 -47
  114. data/lib/active_record/explain_registry.rb +1 -1
  115. data/lib/active_record/explain_subscriber.rb +1 -1
  116. data/lib/active_record/fixture_set/file.rb +19 -4
  117. data/lib/active_record/fixtures.rb +76 -40
  118. data/lib/active_record/gem_version.rb +4 -4
  119. data/lib/active_record/inheritance.rb +27 -40
  120. data/lib/active_record/integration.rb +4 -4
  121. data/lib/active_record/legacy_yaml_adapter.rb +18 -2
  122. data/lib/active_record/locale/en.yml +3 -2
  123. data/lib/active_record/locking/optimistic.rb +10 -14
  124. data/lib/active_record/locking/pessimistic.rb +1 -1
  125. data/lib/active_record/log_subscriber.rb +40 -22
  126. data/lib/active_record/migration.rb +304 -133
  127. data/lib/active_record/migration/command_recorder.rb +59 -18
  128. data/lib/active_record/migration/compatibility.rb +90 -0
  129. data/lib/active_record/model_schema.rb +92 -40
  130. data/lib/active_record/nested_attributes.rb +45 -34
  131. data/lib/active_record/null_relation.rb +15 -7
  132. data/lib/active_record/persistence.rb +112 -72
  133. data/lib/active_record/querying.rb +6 -5
  134. data/lib/active_record/railtie.rb +20 -13
  135. data/lib/active_record/railties/controller_runtime.rb +1 -1
  136. data/lib/active_record/railties/databases.rake +47 -38
  137. data/lib/active_record/readonly_attributes.rb +1 -1
  138. data/lib/active_record/reflection.rb +182 -57
  139. data/lib/active_record/relation.rb +152 -100
  140. data/lib/active_record/relation/batches.rb +133 -33
  141. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  142. data/lib/active_record/relation/calculations.rb +80 -101
  143. data/lib/active_record/relation/delegation.rb +6 -19
  144. data/lib/active_record/relation/finder_methods.rb +58 -46
  145. data/lib/active_record/relation/from_clause.rb +32 -0
  146. data/lib/active_record/relation/merger.rb +13 -42
  147. data/lib/active_record/relation/predicate_builder.rb +99 -105
  148. data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
  149. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +78 -0
  150. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  151. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  152. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  153. data/lib/active_record/relation/predicate_builder/range_handler.rb +17 -0
  154. data/lib/active_record/relation/query_attribute.rb +19 -0
  155. data/lib/active_record/relation/query_methods.rb +274 -238
  156. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  157. data/lib/active_record/relation/spawn_methods.rb +3 -6
  158. data/lib/active_record/relation/where_clause.rb +173 -0
  159. data/lib/active_record/relation/where_clause_factory.rb +37 -0
  160. data/lib/active_record/result.rb +4 -3
  161. data/lib/active_record/runtime_registry.rb +1 -1
  162. data/lib/active_record/sanitization.rb +94 -65
  163. data/lib/active_record/schema.rb +23 -22
  164. data/lib/active_record/schema_dumper.rb +33 -22
  165. data/lib/active_record/schema_migration.rb +10 -4
  166. data/lib/active_record/scoping.rb +17 -6
  167. data/lib/active_record/scoping/default.rb +19 -6
  168. data/lib/active_record/scoping/named.rb +39 -28
  169. data/lib/active_record/secure_token.rb +38 -0
  170. data/lib/active_record/serialization.rb +2 -4
  171. data/lib/active_record/statement_cache.rb +15 -13
  172. data/lib/active_record/store.rb +8 -3
  173. data/lib/active_record/suppressor.rb +54 -0
  174. data/lib/active_record/table_metadata.rb +64 -0
  175. data/lib/active_record/tasks/database_tasks.rb +30 -40
  176. data/lib/active_record/tasks/mysql_database_tasks.rb +7 -15
  177. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -2
  178. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  179. data/lib/active_record/timestamp.rb +16 -9
  180. data/lib/active_record/touch_later.rb +58 -0
  181. data/lib/active_record/transactions.rb +138 -56
  182. data/lib/active_record/type.rb +66 -17
  183. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  184. data/lib/active_record/type/date.rb +2 -45
  185. data/lib/active_record/type/date_time.rb +2 -49
  186. data/lib/active_record/type/internal/abstract_json.rb +33 -0
  187. data/lib/active_record/type/internal/timezone.rb +15 -0
  188. data/lib/active_record/type/serialized.rb +9 -14
  189. data/lib/active_record/type/time.rb +3 -21
  190. data/lib/active_record/type/type_map.rb +4 -4
  191. data/lib/active_record/type_caster.rb +7 -0
  192. data/lib/active_record/type_caster/connection.rb +29 -0
  193. data/lib/active_record/type_caster/map.rb +19 -0
  194. data/lib/active_record/validations.rb +33 -32
  195. data/lib/active_record/validations/absence.rb +24 -0
  196. data/lib/active_record/validations/associated.rb +10 -3
  197. data/lib/active_record/validations/length.rb +36 -0
  198. data/lib/active_record/validations/presence.rb +12 -12
  199. data/lib/active_record/validations/uniqueness.rb +24 -21
  200. data/lib/rails/generators/active_record/migration.rb +7 -0
  201. data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
  202. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
  203. data/lib/rails/generators/active_record/migration/templates/migration.rb +4 -1
  204. data/lib/rails/generators/active_record/model/model_generator.rb +21 -15
  205. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  206. metadata +50 -35
  207. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  208. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  209. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  210. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  211. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  212. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  213. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  214. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  215. data/lib/active_record/type/big_integer.rb +0 -13
  216. data/lib/active_record/type/binary.rb +0 -50
  217. data/lib/active_record/type/boolean.rb +0 -31
  218. data/lib/active_record/type/decimal.rb +0 -64
  219. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  220. data/lib/active_record/type/decorator.rb +0 -14
  221. data/lib/active_record/type/float.rb +0 -19
  222. data/lib/active_record/type/integer.rb +0 -59
  223. data/lib/active_record/type/mutable.rb +0 -16
  224. data/lib/active_record/type/numeric.rb +0 -36
  225. data/lib/active_record/type/string.rb +0 -40
  226. data/lib/active_record/type/text.rb +0 -11
  227. data/lib/active_record/type/time_value.rb +0 -38
  228. data/lib/active_record/type/unsigned_integer.rb +0 -15
  229. data/lib/active_record/type/value.rb +0 -110
@@ -1,38 +1,39 @@
1
- # -*- coding: utf-8 -*-
2
- require 'arel/collectors/bind'
1
+ require "arel/collectors/bind"
3
2
 
4
3
  module ActiveRecord
5
- # = Active Record Relation
4
+ # = Active Record \Relation
6
5
  class Relation
7
6
  MULTI_VALUE_METHODS = [:includes, :eager_load, :preload, :select, :group,
8
- :order, :joins, :where, :having, :bind, :references,
7
+ :order, :joins, :left_joins, :left_outer_joins, :references,
9
8
  :extending, :unscope]
10
9
 
11
- SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :from, :reordering,
12
- :reverse_order, :distinct, :create_with, :uniq]
10
+ SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering,
11
+ :reverse_order, :distinct, :create_with]
12
+ CLAUSE_METHODS = [:where, :having, :from]
13
13
  INVALID_METHODS_FOR_DELETE_ALL = [:limit, :distinct, :offset, :group, :having]
14
14
 
15
- VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS
15
+ VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS + CLAUSE_METHODS
16
16
 
17
+ include Enumerable
17
18
  include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
18
19
 
19
- attr_reader :table, :klass, :loaded
20
+ attr_reader :table, :klass, :loaded, :predicate_builder
20
21
  alias :model :klass
21
22
  alias :loaded? :loaded
22
23
 
23
- def initialize(klass, table, values = {})
24
+ def initialize(klass, table, predicate_builder, values = {})
24
25
  @klass = klass
25
26
  @table = table
26
27
  @values = values
27
28
  @offsets = {}
28
29
  @loaded = false
30
+ @predicate_builder = predicate_builder
29
31
  end
30
32
 
31
33
  def initialize_copy(other)
32
34
  # This method is a hot spot, so for now, use Hash[] to dup the hash.
33
35
  # https://bugs.ruby-lang.org/issues/7166
34
36
  @values = Hash[@values]
35
- @values[:bind] = @values[:bind].dup if @values.key? :bind
36
37
  reset
37
38
  end
38
39
 
@@ -80,7 +81,7 @@ module ActiveRecord
80
81
  end
81
82
 
82
83
  relation = scope.where(@klass.primary_key => (id_was || id))
83
- bvs = binds + relation.bind_values
84
+ bvs = binds + relation.bound_attributes
84
85
  um = relation
85
86
  .arel
86
87
  .compile_update(substitutes, @klass.primary_key)
@@ -94,11 +95,11 @@ module ActiveRecord
94
95
 
95
96
  def substitute_values(values) # :nodoc:
96
97
  binds = values.map do |arel_attr, value|
97
- [@klass.columns_hash[arel_attr.name], value]
98
+ QueryAttribute.new(arel_attr.name, value, klass.type_for_attribute(arel_attr.name))
98
99
  end
99
100
 
100
- substitutes = values.each_with_index.map do |(arel_attr, _), i|
101
- [arel_attr, @klass.connection.substitute_at(binds[i][0])]
101
+ substitutes = values.map do |(arel_attr, _)|
102
+ [arel_attr, connection.substitute_at(klass.columns_hash[arel_attr.name])]
102
103
  end
103
104
 
104
105
  [substitutes, binds]
@@ -107,7 +108,7 @@ module ActiveRecord
107
108
  # Initializes new record from relation while maintaining the current
108
109
  # scope.
109
110
  #
110
- # Expects arguments in the same format as +Base.new+.
111
+ # Expects arguments in the same format as {ActiveRecord::Base.new}[rdoc-ref:Core.new].
111
112
  #
112
113
  # users = User.where(name: 'DHH')
113
114
  # user = users.new # => #<User id: nil, name: "DHH", created_at: nil, updated_at: nil>
@@ -125,28 +126,32 @@ module ActiveRecord
125
126
  # Tries to create a new record with the same scoped attributes
126
127
  # defined in the relation. Returns the initialized object if validation fails.
127
128
  #
128
- # Expects arguments in the same format as +Base.create+.
129
+ # Expects arguments in the same format as
130
+ # {ActiveRecord::Base.create}[rdoc-ref:Persistence::ClassMethods#create].
129
131
  #
130
132
  # ==== Examples
133
+ #
131
134
  # users = User.where(name: 'Oscar')
132
- # users.create # #<User id: 3, name: "oscar", ...>
135
+ # users.create # => #<User id: 3, name: "oscar", ...>
133
136
  #
134
137
  # users.create(name: 'fxn')
135
- # users.create # #<User id: 4, name: "fxn", ...>
138
+ # users.create # => #<User id: 4, name: "fxn", ...>
136
139
  #
137
140
  # users.create { |user| user.name = 'tenderlove' }
138
- # # #<User id: 5, name: "tenderlove", ...>
141
+ # # => #<User id: 5, name: "tenderlove", ...>
139
142
  #
140
143
  # users.create(name: nil) # validation on name
141
- # # #<User id: nil, name: nil, ...>
144
+ # # => #<User id: nil, name: nil, ...>
142
145
  def create(*args, &block)
143
146
  scoping { @klass.create(*args, &block) }
144
147
  end
145
148
 
146
- # Similar to #create, but calls +create!+ on the base class. Raises
147
- # an exception if a validation error occurs.
149
+ # Similar to #create, but calls
150
+ # {create!}[rdoc-ref:Persistence::ClassMethods#create!]
151
+ # on the base class. Raises an exception if a validation error occurs.
148
152
  #
149
- # Expects arguments in the same format as <tt>Base.create!</tt>.
153
+ # Expects arguments in the same format as
154
+ # {ActiveRecord::Base.create!}[rdoc-ref:Persistence::ClassMethods#create!].
150
155
  def create!(*args, &block)
151
156
  scoping { @klass.create!(*args, &block) }
152
157
  end
@@ -180,7 +185,7 @@ module ActiveRecord
180
185
  # User.create_with(last_name: 'Johansson').find_or_create_by(first_name: 'Scarlett')
181
186
  # # => #<User id: 2, first_name: "Scarlett", last_name: "Johansson">
182
187
  #
183
- # This method accepts a block, which is passed down to +create+. The last example
188
+ # This method accepts a block, which is passed down to #create. The last example
184
189
  # above can be alternatively written this way:
185
190
  #
186
191
  # # Find the first user named "Scarlett" or create a new one with a
@@ -192,7 +197,7 @@ module ActiveRecord
192
197
  #
193
198
  # This method always returns a record, but if creation was attempted and
194
199
  # failed due to validation errors it won't be persisted, you get what
195
- # +create+ returns in such situation.
200
+ # #create returns in such situation.
196
201
  #
197
202
  # Please note *this method is not atomic*, it runs first a SELECT, and if
198
203
  # there are no results an INSERT is attempted. If there are other threads
@@ -204,7 +209,9 @@ module ActiveRecord
204
209
  # constraint an exception may be raised, just retry:
205
210
  #
206
211
  # begin
207
- # CreditAccount.find_or_create_by(user_id: user.id)
212
+ # CreditAccount.transaction(requires_new: true) do
213
+ # CreditAccount.find_or_create_by(user_id: user.id)
214
+ # end
208
215
  # rescue ActiveRecord::RecordNotUnique
209
216
  # retry
210
217
  # end
@@ -213,13 +220,15 @@ module ActiveRecord
213
220
  find_by(attributes) || create(attributes, &block)
214
221
  end
215
222
 
216
- # Like <tt>find_or_create_by</tt>, but calls <tt>create!</tt> so an exception
223
+ # Like #find_or_create_by, but calls
224
+ # {create!}[rdoc-ref:Persistence::ClassMethods#create!] so an exception
217
225
  # is raised if the created record is invalid.
218
226
  def find_or_create_by!(attributes, &block)
219
227
  find_by(attributes) || create!(attributes, &block)
220
228
  end
221
229
 
222
- # Like <tt>find_or_create_by</tt>, but calls <tt>new</tt> instead of <tt>create</tt>.
230
+ # Like #find_or_create_by, but calls {new}[rdoc-ref:Core#new]
231
+ # instead of {create}[rdoc-ref:Persistence::ClassMethods#create].
223
232
  def find_or_initialize_by(attributes, &block)
224
233
  find_by(attributes) || new(attributes, &block)
225
234
  end
@@ -270,22 +279,54 @@ module ActiveRecord
270
279
  end
271
280
  end
272
281
 
282
+ # Returns true if there are no records.
283
+ def none?
284
+ return super if block_given?
285
+ empty?
286
+ end
287
+
273
288
  # Returns true if there are any records.
274
289
  def any?
275
- if block_given?
276
- to_a.any? { |*block_args| yield(*block_args) }
277
- else
278
- !empty?
279
- end
290
+ return super if block_given?
291
+ !empty?
292
+ end
293
+
294
+ # Returns true if there is exactly one record.
295
+ def one?
296
+ return super if block_given?
297
+ limit_value ? to_a.one? : size == 1
280
298
  end
281
299
 
282
300
  # Returns true if there is more than one record.
283
301
  def many?
284
- if block_given?
285
- to_a.many? { |*block_args| yield(*block_args) }
286
- else
287
- limit_value ? to_a.many? : size > 1
288
- end
302
+ return super if block_given?
303
+ limit_value ? to_a.many? : size > 1
304
+ end
305
+
306
+ # Returns a cache key that can be used to identify the records fetched by
307
+ # this query. The cache key is built with a fingerprint of the sql query,
308
+ # the number of records matched by the query and a timestamp of the last
309
+ # updated record. When a new record comes to match the query, or any of
310
+ # the existing records is updated or deleted, the cache key changes.
311
+ #
312
+ # Product.where("name like ?", "%Cosmic Encounter%").cache_key
313
+ # # => "products/query-1850ab3d302391b85b8693e941286659-1-20150714212553907087000"
314
+ #
315
+ # If the collection is loaded, the method will iterate through the records
316
+ # to generate the timestamp, otherwise it will trigger one SQL query like:
317
+ #
318
+ # SELECT COUNT(*), MAX("products"."updated_at") FROM "products" WHERE (name like '%Cosmic Encounter%')
319
+ #
320
+ # You can also pass a custom timestamp column to fetch the timestamp of the
321
+ # last updated record.
322
+ #
323
+ # Product.where("name like ?", "%Game%").cache_key(:last_reviewed_at)
324
+ #
325
+ # You can customize the strategy to generate the key on a per model basis
326
+ # overriding ActiveRecord::Base#collection_cache_key.
327
+ def cache_key(timestamp_column = :updated_at)
328
+ @cache_keys ||= {}
329
+ @cache_keys[timestamp_column] ||= @klass.collection_cache_key(self, timestamp_column)
289
330
  end
290
331
 
291
332
  # Scope all queries to the current scope.
@@ -306,9 +347,8 @@ module ActiveRecord
306
347
 
307
348
  # Updates all records in the current relation with details given. This method constructs a single SQL UPDATE
308
349
  # 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.
350
+ # trigger Active Record callbacks or validations. However, values passed to #update_all will still go through
351
+ # Active Record's normal type casting and serialization.
312
352
  #
313
353
  # ==== Parameters
314
354
  #
@@ -327,7 +367,7 @@ module ActiveRecord
327
367
  def update_all(updates)
328
368
  raise ArgumentError, "Empty list of attributes to change" if updates.blank?
329
369
 
330
- stmt = Arel::UpdateManager.new(arel.engine)
370
+ stmt = Arel::UpdateManager.new
331
371
 
332
372
  stmt.set Arel.sql(@klass.send(:sanitize_sql_for_assignment, updates))
333
373
  stmt.table(table)
@@ -341,8 +381,7 @@ module ActiveRecord
341
381
  stmt.wheres = arel.constraints
342
382
  end
343
383
 
344
- bvs = arel.bind_values + bind_values
345
- @klass.connection.update stmt, 'SQL', bvs
384
+ @klass.connection.update stmt, 'SQL', bound_attributes
346
385
  end
347
386
 
348
387
  # Updates an object (or multiple objects) and saves it to the database, if validations pass.
@@ -361,20 +400,39 @@ module ActiveRecord
361
400
  # # Updates multiple records
362
401
  # people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
363
402
  # Person.update(people.keys, people.values)
364
- def update(id, attributes)
403
+ #
404
+ # # Updates multiple records from the result of a relation
405
+ # people = Person.where(group: 'expert')
406
+ # people.update(group: 'masters')
407
+ #
408
+ # Note: Updating a large number of records will run an
409
+ # UPDATE query for each record, which may cause a performance
410
+ # issue. So if it is not needed to run callbacks for each update, it is
411
+ # preferred to use #update_all for updating all records using
412
+ # a single query.
413
+ def update(id = :all, attributes)
365
414
  if id.is_a?(Array)
366
415
  id.map.with_index { |one_id, idx| update(one_id, attributes[idx]) }
416
+ elsif id == :all
417
+ to_a.each { |record| record.update(attributes) }
367
418
  else
419
+ if ActiveRecord::Base === id
420
+ id = id.id
421
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
422
+ You are passing an instance of ActiveRecord::Base to `update`.
423
+ Please pass the id of the object by calling `.id`
424
+ MSG
425
+ end
368
426
  object = find(id)
369
427
  object.update(attributes)
370
428
  object
371
429
  end
372
430
  end
373
431
 
374
- # Destroys the records matching +conditions+ by instantiating each
375
- # record and calling its +destroy+ method. Each object's callbacks are
376
- # executed (including <tt>:dependent</tt> association options). Returns the
377
- # collection of objects that were destroyed; each will be frozen, to
432
+ # Destroys the records by instantiating each
433
+ # record and calling its {#destroy}[rdoc-ref:Persistence#destroy] method.
434
+ # Each object's callbacks are executed (including <tt>:dependent</tt> association options).
435
+ # Returns the collection of objects that were destroyed; each will be frozen, to
378
436
  # reflect that no changes should be made (since they can't be persisted).
379
437
  #
380
438
  # Note: Instantiation, callback execution, and deletion of each
@@ -382,31 +440,26 @@ module ActiveRecord
382
440
  # once. It generates at least one SQL +DELETE+ query per record (or
383
441
  # possibly more, to enforce your callbacks). If you want to delete many
384
442
  # rows quickly, without concern for their associations or callbacks, use
385
- # +delete_all+ instead.
386
- #
387
- # ==== Parameters
388
- #
389
- # * +conditions+ - A string, array, or hash that specifies which records
390
- # to destroy. If omitted, all records are destroyed. See the
391
- # Conditions section in the introduction to ActiveRecord::Base for
392
- # more information.
443
+ # #delete_all instead.
393
444
  #
394
445
  # ==== Examples
395
446
  #
396
- # Person.destroy_all("last_login < '2004-04-04'")
397
- # Person.destroy_all(status: "inactive")
398
447
  # Person.where(age: 0..18).destroy_all
399
448
  def destroy_all(conditions = nil)
400
449
  if conditions
450
+ ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
451
+ Passing conditions to destroy_all is deprecated and will be removed in Rails 5.1.
452
+ To achieve the same use where(conditions).destroy_all
453
+ MESSAGE
401
454
  where(conditions).destroy_all
402
455
  else
403
- to_a.each {|object| object.destroy }.tap { reset }
456
+ to_a.each(&:destroy).tap { reset }
404
457
  end
405
458
  end
406
459
 
407
460
  # Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
408
461
  # therefore all callbacks and filters are fired off before the object is deleted. This method is
409
- # less efficient than ActiveRecord#delete but allows cleanup methods and other actions to be run.
462
+ # less efficient than #delete but allows cleanup methods and other actions to be run.
410
463
  #
411
464
  # This essentially finds the object (or multiple objects) with the given id, creates a new object
412
465
  # from the attributes, and then calls destroy on it.
@@ -431,22 +484,21 @@ module ActiveRecord
431
484
  end
432
485
  end
433
486
 
434
- # Deletes the records matching +conditions+ without instantiating the records
435
- # first, and hence not calling the +destroy+ method nor invoking callbacks. This
436
- # is a single SQL DELETE statement that goes straight to the database, much more
437
- # efficient than +destroy_all+. Be careful with relations though, in particular
487
+ # Deletes the records without instantiating the records
488
+ # first, and hence not calling the {#destroy}[rdoc-ref:Persistence#destroy]
489
+ # method nor invoking callbacks.
490
+ # This is a single SQL DELETE statement that goes straight to the database, much more
491
+ # efficient than #destroy_all. Be careful with relations though, in particular
438
492
  # <tt>:dependent</tt> rules defined on associations are not honored. Returns the
439
493
  # number of rows affected.
440
494
  #
441
- # Post.delete_all("person_id = 5 AND (category = 'Something' OR category = 'Else')")
442
- # Post.delete_all(["person_id = ? AND (category = ? OR category = ?)", 5, 'Something', 'Else'])
443
495
  # Post.where(person_id: 5).where(category: ['Something', 'Else']).delete_all
444
496
  #
445
497
  # Both calls delete the affected posts all at once with a single DELETE statement.
446
498
  # If you need to destroy dependent associations or call your <tt>before_*</tt> or
447
- # +after_destroy+ callbacks, use the +destroy_all+ method instead.
499
+ # +after_destroy+ callbacks, use the #destroy_all method instead.
448
500
  #
449
- # If an invalid method is supplied, +delete_all+ raises an ActiveRecord error:
501
+ # If an invalid method is supplied, #delete_all raises an ActiveRecordError:
450
502
  #
451
503
  # Post.limit(100).delete_all
452
504
  # # => ActiveRecord::ActiveRecordError: delete_all doesn't support limit
@@ -454,8 +506,10 @@ module ActiveRecord
454
506
  invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select { |method|
455
507
  if MULTI_VALUE_METHODS.include?(method)
456
508
  send("#{method}_values").any?
457
- else
509
+ elsif SINGLE_VALUE_METHODS.include?(method)
458
510
  send("#{method}_value")
511
+ elsif CLAUSE_METHODS.include?(method)
512
+ send("#{method}_clause").any?
459
513
  end
460
514
  }
461
515
  if invalid_methods.any?
@@ -463,9 +517,13 @@ module ActiveRecord
463
517
  end
464
518
 
465
519
  if conditions
520
+ ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
521
+ Passing conditions to delete_all is deprecated and will be removed in Rails 5.1.
522
+ To achieve the same use where(conditions).delete_all
523
+ MESSAGE
466
524
  where(conditions).delete_all
467
525
  else
468
- stmt = Arel::DeleteManager.new(arel.engine)
526
+ stmt = Arel::DeleteManager.new
469
527
  stmt.from(table)
470
528
 
471
529
  if joins_values.any?
@@ -474,8 +532,7 @@ module ActiveRecord
474
532
  stmt.wheres = arel.constraints
475
533
  end
476
534
 
477
- bvs = arel.bind_values + bind_values
478
- affected = @klass.connection.delete(stmt, 'SQL', bvs)
535
+ affected = @klass.connection.delete(stmt, 'SQL', bound_attributes)
479
536
 
480
537
  reset
481
538
  affected
@@ -490,7 +547,7 @@ module ActiveRecord
490
547
  # You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
491
548
  #
492
549
  # Note: Although it is often much faster than the alternative,
493
- # <tt>#destroy</tt>, skipping callbacks might bypass business logic in
550
+ # #destroy, skipping callbacks might bypass business logic in
494
551
  # your application that ensures referential integrity or performs other
495
552
  # essential jobs.
496
553
  #
@@ -545,10 +602,10 @@ module ActiveRecord
545
602
  find_with_associations { |rel| relation = rel }
546
603
  end
547
604
 
548
- arel = relation.arel
549
- binds = (arel.bind_values + relation.bind_values).dup
550
- binds.map! { |bv| connection.quote(*bv.reverse) }
551
- collect = visitor.accept(arel.ast, Arel::Collectors::Bind.new)
605
+ binds = relation.bound_attributes
606
+ binds = connection.prepare_binds_for_database(binds)
607
+ binds.map! { |value| connection.quote(value) }
608
+ collect = visitor.accept(relation.arel.ast, Arel::Collectors::Bind.new)
552
609
  collect.substitute_binds(binds).join
553
610
  end
554
611
  end
@@ -558,22 +615,7 @@ module ActiveRecord
558
615
  # User.where(name: 'Oscar').where_values_hash
559
616
  # # => {name: "Oscar"}
560
617
  def where_values_hash(relation_table_name = table_name)
561
- equalities = where_values.grep(Arel::Nodes::Equality).find_all { |node|
562
- node.left.relation.name == relation_table_name
563
- }
564
-
565
- binds = Hash[bind_values.find_all(&:first).map { |column, v| [column.name, v] }]
566
-
567
- Hash[equalities.map { |where|
568
- name = where.left.name
569
- [name, binds.fetch(name.to_s) {
570
- case where.right
571
- when Array then where.right.map(&:val)
572
- when Arel::Nodes::Casted
573
- where.right.val
574
- end
575
- }]
576
- }]
618
+ where_clause.to_h(relation_table_name)
577
619
  end
578
620
 
579
621
  def scope_for_create
@@ -595,11 +637,14 @@ module ActiveRecord
595
637
  includes_values & joins_values
596
638
  end
597
639
 
598
- # +uniq+ and +uniq!+ are silently deprecated. +uniq_value+ delegates to +distinct_value+
599
- # to maintain backwards compatibility. Use +distinct_value+ instead.
640
+ # {#uniq}[rdoc-ref:QueryMethods#uniq] and
641
+ # {#uniq!}[rdoc-ref:QueryMethods#uniq!] are silently deprecated.
642
+ # #uniq_value delegates to #distinct_value to maintain backwards compatibility.
643
+ # Use #distinct_value instead.
600
644
  def uniq_value
601
645
  distinct_value
602
646
  end
647
+ deprecate uniq_value: :distinct_value
603
648
 
604
649
  # Compares two relations for equality.
605
650
  def ==(other)
@@ -633,10 +678,17 @@ module ActiveRecord
633
678
  "#<#{self.class.name} [#{entries.join(', ')}]>"
634
679
  end
635
680
 
681
+ protected
682
+
683
+ def load_records(records)
684
+ @records = records
685
+ @loaded = true
686
+ end
687
+
636
688
  private
637
689
 
638
690
  def exec_queries
639
- @records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel, arel.bind_values + bind_values)
691
+ @records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel, bound_attributes)
640
692
 
641
693
  preload = preload_values
642
694
  preload += includes_values unless eager_loading?
@@ -645,7 +697,7 @@ module ActiveRecord
645
697
  preloader.preload @records, associations
646
698
  end
647
699
 
648
- @records.each { |record| record.readonly! } if readonly_value
700
+ @records.each(&:readonly!) if readonly_value
649
701
 
650
702
  @loaded = true
651
703
  @records
@@ -667,7 +719,7 @@ module ActiveRecord
667
719
  joined_tables += [table.name, table.table_alias]
668
720
 
669
721
  # always convert table names to downcase as in Oracle quoted table names are in uppercase
670
- joined_tables = joined_tables.flatten.compact.map { |t| t.downcase }.uniq
722
+ joined_tables = joined_tables.flatten.compact.map(&:downcase).uniq
671
723
 
672
724
  (references_values - joined_tables).any?
673
725
  end
@@ -676,7 +728,7 @@ module ActiveRecord
676
728
  return [] if string.blank?
677
729
  # always convert table names to downcase as in Oracle quoted table names are in uppercase
678
730
  # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
679
- string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map{ |s| s.downcase }.uniq - ['raw_sql_']
731
+ string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map(&:downcase).uniq - ['raw_sql_']
680
732
  end
681
733
  end
682
734
  end