activerecord 4.1.8 → 4.2.11.3

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 (186) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1165 -1591
  3. data/README.rdoc +15 -10
  4. data/lib/active_record/aggregations.rb +15 -8
  5. data/lib/active_record/association_relation.rb +13 -0
  6. data/lib/active_record/associations/alias_tracker.rb +3 -12
  7. data/lib/active_record/associations/association.rb +16 -4
  8. data/lib/active_record/associations/association_scope.rb +84 -43
  9. data/lib/active_record/associations/belongs_to_association.rb +28 -10
  10. data/lib/active_record/associations/builder/association.rb +16 -5
  11. data/lib/active_record/associations/builder/belongs_to.rb +7 -29
  12. data/lib/active_record/associations/builder/collection_association.rb +5 -1
  13. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +9 -14
  14. data/lib/active_record/associations/builder/has_many.rb +1 -1
  15. data/lib/active_record/associations/builder/has_one.rb +2 -2
  16. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  17. data/lib/active_record/associations/collection_association.rb +87 -30
  18. data/lib/active_record/associations/collection_proxy.rb +33 -35
  19. data/lib/active_record/associations/foreign_association.rb +11 -0
  20. data/lib/active_record/associations/has_many_association.rb +83 -22
  21. data/lib/active_record/associations/has_many_through_association.rb +49 -26
  22. data/lib/active_record/associations/has_one_association.rb +1 -1
  23. data/lib/active_record/associations/join_dependency/join_association.rb +25 -15
  24. data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
  25. data/lib/active_record/associations/join_dependency.rb +26 -12
  26. data/lib/active_record/associations/preloader/association.rb +14 -10
  27. data/lib/active_record/associations/preloader/through_association.rb +4 -3
  28. data/lib/active_record/associations/preloader.rb +37 -26
  29. data/lib/active_record/associations/singular_association.rb +17 -2
  30. data/lib/active_record/associations/through_association.rb +16 -12
  31. data/lib/active_record/associations.rb +158 -49
  32. data/lib/active_record/attribute.rb +163 -0
  33. data/lib/active_record/attribute_assignment.rb +20 -12
  34. data/lib/active_record/attribute_decorators.rb +66 -0
  35. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  36. data/lib/active_record/attribute_methods/dirty.rb +107 -43
  37. data/lib/active_record/attribute_methods/primary_key.rb +7 -8
  38. data/lib/active_record/attribute_methods/query.rb +1 -1
  39. data/lib/active_record/attribute_methods/read.rb +22 -59
  40. data/lib/active_record/attribute_methods/serialization.rb +16 -150
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -28
  42. data/lib/active_record/attribute_methods/write.rb +9 -24
  43. data/lib/active_record/attribute_methods.rb +57 -95
  44. data/lib/active_record/attribute_set/builder.rb +106 -0
  45. data/lib/active_record/attribute_set.rb +81 -0
  46. data/lib/active_record/attributes.rb +147 -0
  47. data/lib/active_record/autosave_association.rb +30 -12
  48. data/lib/active_record/base.rb +13 -24
  49. data/lib/active_record/callbacks.rb +6 -6
  50. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +85 -53
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +52 -50
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +60 -60
  54. data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +39 -4
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +139 -57
  57. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
  58. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +271 -74
  59. data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
  60. data/lib/active_record/connection_adapters/abstract_adapter.rb +177 -60
  61. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +295 -141
  62. data/lib/active_record/connection_adapters/column.rb +29 -240
  63. data/lib/active_record/connection_adapters/connection_specification.rb +15 -24
  64. data/lib/active_record/connection_adapters/mysql2_adapter.rb +17 -33
  65. data/lib/active_record/connection_adapters/mysql_adapter.rb +68 -145
  66. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  67. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  68. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -25
  69. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -385
  95. data/lib/active_record/connection_adapters/postgresql/quoting.rb +46 -136
  96. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  97. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  98. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +134 -43
  99. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  100. data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -477
  101. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  102. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -75
  103. data/lib/active_record/connection_handling.rb +1 -1
  104. data/lib/active_record/core.rb +163 -40
  105. data/lib/active_record/counter_cache.rb +60 -6
  106. data/lib/active_record/enum.rb +10 -12
  107. data/lib/active_record/errors.rb +53 -30
  108. data/lib/active_record/explain.rb +1 -1
  109. data/lib/active_record/explain_subscriber.rb +1 -1
  110. data/lib/active_record/fixtures.rb +62 -74
  111. data/lib/active_record/gem_version.rb +4 -4
  112. data/lib/active_record/inheritance.rb +35 -10
  113. data/lib/active_record/integration.rb +4 -4
  114. data/lib/active_record/legacy_yaml_adapter.rb +30 -0
  115. data/lib/active_record/locking/optimistic.rb +46 -26
  116. data/lib/active_record/migration/command_recorder.rb +19 -2
  117. data/lib/active_record/migration/join_table.rb +1 -1
  118. data/lib/active_record/migration.rb +79 -47
  119. data/lib/active_record/model_schema.rb +52 -58
  120. data/lib/active_record/nested_attributes.rb +18 -8
  121. data/lib/active_record/no_touching.rb +1 -1
  122. data/lib/active_record/persistence.rb +48 -27
  123. data/lib/active_record/query_cache.rb +3 -3
  124. data/lib/active_record/querying.rb +10 -7
  125. data/lib/active_record/railtie.rb +19 -14
  126. data/lib/active_record/railties/databases.rake +55 -56
  127. data/lib/active_record/readonly_attributes.rb +0 -1
  128. data/lib/active_record/reflection.rb +281 -117
  129. data/lib/active_record/relation/batches.rb +0 -1
  130. data/lib/active_record/relation/calculations.rb +41 -37
  131. data/lib/active_record/relation/delegation.rb +1 -1
  132. data/lib/active_record/relation/finder_methods.rb +71 -48
  133. data/lib/active_record/relation/merger.rb +39 -29
  134. data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
  135. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  136. data/lib/active_record/relation/predicate_builder.rb +42 -12
  137. data/lib/active_record/relation/query_methods.rb +130 -73
  138. data/lib/active_record/relation/spawn_methods.rb +10 -3
  139. data/lib/active_record/relation.rb +57 -25
  140. data/lib/active_record/result.rb +18 -7
  141. data/lib/active_record/sanitization.rb +12 -2
  142. data/lib/active_record/schema.rb +0 -1
  143. data/lib/active_record/schema_dumper.rb +59 -28
  144. data/lib/active_record/schema_migration.rb +5 -4
  145. data/lib/active_record/scoping/default.rb +6 -4
  146. data/lib/active_record/scoping/named.rb +4 -0
  147. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  148. data/lib/active_record/statement_cache.rb +95 -10
  149. data/lib/active_record/store.rb +5 -5
  150. data/lib/active_record/tasks/database_tasks.rb +61 -8
  151. data/lib/active_record/tasks/mysql_database_tasks.rb +32 -17
  152. data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -9
  153. data/lib/active_record/timestamp.rb +9 -7
  154. data/lib/active_record/transactions.rb +54 -28
  155. data/lib/active_record/type/big_integer.rb +13 -0
  156. data/lib/active_record/type/binary.rb +50 -0
  157. data/lib/active_record/type/boolean.rb +31 -0
  158. data/lib/active_record/type/date.rb +50 -0
  159. data/lib/active_record/type/date_time.rb +54 -0
  160. data/lib/active_record/type/decimal.rb +64 -0
  161. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  162. data/lib/active_record/type/decorator.rb +14 -0
  163. data/lib/active_record/type/float.rb +19 -0
  164. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  165. data/lib/active_record/type/integer.rb +59 -0
  166. data/lib/active_record/type/mutable.rb +16 -0
  167. data/lib/active_record/type/numeric.rb +36 -0
  168. data/lib/active_record/type/serialized.rb +62 -0
  169. data/lib/active_record/type/string.rb +40 -0
  170. data/lib/active_record/type/text.rb +11 -0
  171. data/lib/active_record/type/time.rb +26 -0
  172. data/lib/active_record/type/time_value.rb +38 -0
  173. data/lib/active_record/type/type_map.rb +64 -0
  174. data/lib/active_record/type/unsigned_integer.rb +15 -0
  175. data/lib/active_record/type/value.rb +110 -0
  176. data/lib/active_record/type.rb +23 -0
  177. data/lib/active_record/validations/associated.rb +5 -3
  178. data/lib/active_record/validations/presence.rb +5 -3
  179. data/lib/active_record/validations/uniqueness.rb +24 -20
  180. data/lib/active_record/validations.rb +25 -19
  181. data/lib/active_record.rb +5 -0
  182. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
  183. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
  184. data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
  185. metadata +66 -11
  186. data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -1,6 +1,7 @@
1
+ require 'thread'
1
2
  require 'active_support/core_ext/hash/indifferent_access'
2
3
  require 'active_support/core_ext/object/duplicable'
3
- require 'thread'
4
+ require 'active_support/core_ext/string/filters'
4
5
 
5
6
  module ActiveRecord
6
7
  module Core
@@ -16,7 +17,6 @@ module ActiveRecord
16
17
  mattr_accessor :logger, instance_writer: false
17
18
 
18
19
  ##
19
- # :singleton-method:
20
20
  # Contains the database configuration - as is typically stored in config/database.yml -
21
21
  # as a Hash.
22
22
  #
@@ -85,15 +85,17 @@ module ActiveRecord
85
85
  mattr_accessor :dump_schema_after_migration, instance_writer: false
86
86
  self.dump_schema_after_migration = true
87
87
 
88
- # :nodoc:
89
88
  mattr_accessor :maintain_test_schema, instance_accessor: false
90
89
 
91
90
  def self.disable_implicit_join_references=(value)
92
- ActiveSupport::Deprecation.warn("Implicit join references were removed with Rails 4.1." \
93
- "Make sure to remove this configuration because it does nothing.")
91
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
92
+ Implicit join references were removed with Rails 4.1.
93
+ Make sure to remove this configuration because it does nothing.
94
+ MSG
94
95
  end
95
96
 
96
97
  class_attribute :default_connection_handler, instance_writer: false
98
+ class_attribute :find_by_statement_cache
97
99
 
98
100
  def self.connection_handler
99
101
  ActiveRecord::RuntimeRegistry.connection_handler || default_connection_handler
@@ -107,7 +109,94 @@ module ActiveRecord
107
109
  end
108
110
 
109
111
  module ClassMethods
110
- def initialize_generated_modules
112
+ def allocate
113
+ define_attribute_methods
114
+ super
115
+ end
116
+
117
+ def initialize_find_by_cache # :nodoc:
118
+ self.find_by_statement_cache = {}.extend(Mutex_m)
119
+ end
120
+
121
+ def inherited(child_class) # :nodoc:
122
+ child_class.initialize_find_by_cache
123
+ super
124
+ end
125
+
126
+ def find(*ids) # :nodoc:
127
+ # We don't have cache keys for this stuff yet
128
+ return super unless ids.length == 1
129
+ # Allow symbols to super to maintain compatibility for deprecated finders until Rails 5
130
+ return super if ids.first.kind_of?(Symbol)
131
+ return super if block_given? ||
132
+ primary_key.nil? ||
133
+ default_scopes.any? ||
134
+ current_scope ||
135
+ columns_hash.include?(inheritance_column) ||
136
+ ids.first.kind_of?(Array)
137
+
138
+ id = ids.first
139
+ if ActiveRecord::Base === id
140
+ id = id.id
141
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
142
+ You are passing an instance of ActiveRecord::Base to `find`.
143
+ Please pass the id of the object by calling `.id`
144
+ MSG
145
+ end
146
+ key = primary_key
147
+
148
+ s = find_by_statement_cache[key] || find_by_statement_cache.synchronize {
149
+ find_by_statement_cache[key] ||= StatementCache.create(connection) { |params|
150
+ where(key => params.bind).limit(1)
151
+ }
152
+ }
153
+ record = s.execute([id], self, connection).first
154
+ unless record
155
+ raise RecordNotFound, "Couldn't find #{name} with '#{primary_key}'=#{id}"
156
+ end
157
+ record
158
+ rescue RangeError
159
+ raise RecordNotFound, "Couldn't find #{name} with an out of range value for '#{primary_key}'"
160
+ end
161
+
162
+ def find_by(*args) # :nodoc:
163
+ return super if current_scope || !(Hash === args.first) || reflect_on_all_aggregations.any?
164
+ return super if default_scopes.any?
165
+
166
+ hash = args.first
167
+
168
+ return super if hash.values.any? { |v|
169
+ v.nil? || Array === v || Hash === v
170
+ }
171
+
172
+ # We can't cache Post.find_by(author: david) ...yet
173
+ return super unless hash.keys.all? { |k| columns_hash.has_key?(k.to_s) }
174
+
175
+ key = hash.keys
176
+
177
+ klass = self
178
+ s = find_by_statement_cache[key] || find_by_statement_cache.synchronize {
179
+ find_by_statement_cache[key] ||= StatementCache.create(connection) { |params|
180
+ wheres = key.each_with_object({}) { |param,o|
181
+ o[param] = params.bind
182
+ }
183
+ klass.where(wheres).limit(1)
184
+ }
185
+ }
186
+ begin
187
+ s.execute(hash.values, self, connection).first
188
+ rescue TypeError => e
189
+ raise ActiveRecord::StatementInvalid.new(e.message, e)
190
+ rescue RangeError
191
+ nil
192
+ end
193
+ end
194
+
195
+ def find_by!(*args) # :nodoc:
196
+ find_by(*args) or raise RecordNotFound.new("Couldn't find #{name}")
197
+ end
198
+
199
+ def initialize_generated_modules # :nodoc:
111
200
  generated_association_methods
112
201
  end
113
202
 
@@ -181,12 +270,8 @@ module ActiveRecord
181
270
  # # Instantiates a single new object
182
271
  # User.new(first_name: 'Jamie')
183
272
  def initialize(attributes = nil, options = {})
184
- defaults = self.class.column_defaults.dup
185
- defaults.each { |k, v| defaults[k] = v.dup if v.duplicable? }
186
-
187
- @attributes = self.class.initialize_attributes(defaults)
188
- @column_types_override = nil
189
- @column_types = self.class.column_types
273
+ @attributes = self.class._default_attributes.dup
274
+ self.class.define_attribute_methods
190
275
 
191
276
  init_internals
192
277
  initialize_internals_callback
@@ -196,32 +281,35 @@ module ActiveRecord
196
281
  init_attributes(attributes, options) if attributes
197
282
 
198
283
  yield self if block_given?
199
- run_callbacks :initialize unless _initialize_callbacks.empty?
284
+ _run_initialize_callbacks
200
285
  end
201
286
 
202
- # Initialize an empty model object from +coder+. +coder+ must contain
203
- # the attributes necessary for initializing an empty model object. For
204
- # example:
287
+ # Initialize an empty model object from +coder+. +coder+ should be
288
+ # the result of previously encoding an Active Record model, using
289
+ # `encode_with`
205
290
  #
206
291
  # class Post < ActiveRecord::Base
207
292
  # end
208
293
  #
294
+ # old_post = Post.new(title: "hello world")
295
+ # coder = {}
296
+ # old_post.encode_with(coder)
297
+ #
209
298
  # post = Post.allocate
210
- # post.init_with('attributes' => { 'title' => 'hello world' })
299
+ # post.init_with(coder)
211
300
  # post.title # => 'hello world'
212
301
  def init_with(coder)
213
- @attributes = self.class.initialize_attributes(coder['attributes'])
214
- @column_types_override = coder['column_types']
215
- @column_types = self.class.column_types
302
+ coder = LegacyYamlAdapter.convert(self.class, coder)
303
+ @attributes = coder['attributes']
216
304
 
217
305
  init_internals
218
306
 
219
- @new_record = false
307
+ @new_record = coder['new_record']
220
308
 
221
309
  self.class.define_attribute_methods
222
310
 
223
- run_callbacks :find
224
- run_callbacks :initialize
311
+ _run_find_callbacks
312
+ _run_initialize_callbacks
225
313
 
226
314
  self
227
315
  end
@@ -254,17 +342,13 @@ module ActiveRecord
254
342
 
255
343
  ##
256
344
  def initialize_dup(other) # :nodoc:
257
- cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast)
258
- self.class.initialize_attributes(cloned_attributes, :serialized => false)
259
-
260
- @attributes = cloned_attributes
261
- @attributes[self.class.primary_key] = nil
345
+ @attributes = @attributes.dup
346
+ @attributes.reset(self.class.primary_key)
262
347
 
263
- run_callbacks(:initialize) unless _initialize_callbacks.empty?
348
+ _run_initialize_callbacks
264
349
 
265
350
  @aggregation_cache = {}
266
351
  @association_cache = {}
267
- @attributes_cache = {}
268
352
 
269
353
  @new_record = true
270
354
  @destroyed = false
@@ -285,7 +369,11 @@ module ActiveRecord
285
369
  # Post.new.encode_with(coder)
286
370
  # coder # => {"attributes" => {"id" => nil, ... }}
287
371
  def encode_with(coder)
288
- coder['attributes'] = attributes_for_coder
372
+ # FIXME: Remove this when we better serialize attributes
373
+ coder['raw_attributes'] = attributes_before_type_cast
374
+ coder['attributes'] = @attributes
375
+ coder['new_record'] = new_record?
376
+ coder['active_record_yaml_version'] = 0
289
377
  end
290
378
 
291
379
  # Returns true if +comparison_object+ is the same exact object, or +comparison_object+
@@ -308,7 +396,11 @@ module ActiveRecord
308
396
  # Delegates to id in order to allow two records of the same type and id to work with something like:
309
397
  # [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
310
398
  def hash
311
- id.hash
399
+ if id
400
+ id.hash
401
+ else
402
+ super
403
+ end
312
404
  end
313
405
 
314
406
  # Clone and freeze the attributes hash such that associations are still
@@ -364,21 +456,45 @@ module ActiveRecord
364
456
  "#<#{self.class} #{inspection}>"
365
457
  end
366
458
 
459
+ # Takes a PP and prettily prints this record to it, allowing you to get a nice result from `pp record`
460
+ # when pp is required.
461
+ def pretty_print(pp)
462
+ return super if custom_inspect_method_defined?
463
+ pp.object_address_group(self) do
464
+ if defined?(@attributes) && @attributes
465
+ column_names = self.class.column_names.select { |name| has_attribute?(name) || new_record? }
466
+ pp.seplist(column_names, proc { pp.text ',' }) do |column_name|
467
+ column_value = read_attribute(column_name)
468
+ pp.breakable ' '
469
+ pp.group(1) do
470
+ pp.text column_name
471
+ pp.text ':'
472
+ pp.breakable
473
+ pp.pp column_value
474
+ end
475
+ end
476
+ else
477
+ pp.breakable ' '
478
+ pp.text 'not initialized'
479
+ end
480
+ end
481
+ end
482
+
367
483
  # Returns a hash of the given methods with their names as keys and returned values as values.
368
484
  def slice(*methods)
369
485
  Hash[methods.map! { |method| [method, public_send(method)] }].with_indifferent_access
370
486
  end
371
487
 
488
+ private
489
+
372
490
  def set_transaction_state(state) # :nodoc:
373
491
  @transaction_state = state
374
492
  end
375
493
 
376
494
  def has_transactional_callbacks? # :nodoc:
377
- !_rollback_callbacks.empty? || !_commit_callbacks.empty? || !_create_callbacks.empty?
495
+ !_rollback_callbacks.empty? || !_commit_callbacks.empty?
378
496
  end
379
497
 
380
- private
381
-
382
498
  # Updates the attributes on this particular ActiveRecord object so that
383
499
  # if it is associated with a transaction, then the state of the AR object
384
500
  # will be updated to reflect the current state of the transaction
@@ -401,6 +517,8 @@ module ActiveRecord
401
517
  end
402
518
 
403
519
  def update_attributes_from_transaction_state(transaction_state, depth)
520
+ @reflects_state = [false] if depth == 0
521
+
404
522
  if transaction_state && transaction_state.finalized? && !has_transactional_callbacks?
405
523
  unless @reflects_state[depth]
406
524
  restore_transaction_record_state if transaction_state.rolledback?
@@ -427,12 +545,8 @@ module ActiveRecord
427
545
  end
428
546
 
429
547
  def init_internals
430
- pk = self.class.primary_key
431
- @attributes[pk] = nil unless @attributes.key?(pk)
432
-
433
548
  @aggregation_cache = {}
434
549
  @association_cache = {}
435
- @attributes_cache = {}
436
550
  @readonly = false
437
551
  @destroyed = false
438
552
  @marked_for_destruction = false
@@ -441,7 +555,6 @@ module ActiveRecord
441
555
  @txn = nil
442
556
  @_start_transaction_state = {}
443
557
  @transaction_state = nil
444
- @reflects_state = [false]
445
558
  end
446
559
 
447
560
  def initialize_internals_callback
@@ -452,5 +565,15 @@ module ActiveRecord
452
565
  def init_attributes(attributes, options)
453
566
  assign_attributes(attributes)
454
567
  end
568
+
569
+ def thaw
570
+ if frozen?
571
+ @attributes = @attributes.dup
572
+ end
573
+ end
574
+
575
+ def custom_inspect_method_defined?
576
+ self.class.instance_method(:inspect).owner != ActiveRecord::Base.instance_method(:inspect).owner
577
+ end
455
578
  end
456
579
  end
@@ -11,7 +11,7 @@ module ActiveRecord
11
11
  # ==== Parameters
12
12
  #
13
13
  # * +id+ - The id of the object you wish to reset a counter on.
14
- # * +counters+ - One or more association counters to reset
14
+ # * +counters+ - One or more association counters to reset. Association name or counter name can be given.
15
15
  #
16
16
  # ==== Examples
17
17
  #
@@ -19,9 +19,14 @@ module ActiveRecord
19
19
  # Post.reset_counters(1, :comments)
20
20
  def reset_counters(id, *counters)
21
21
  object = find(id)
22
- counters.each do |association|
23
- has_many_association = _reflect_on_association(association.to_sym)
24
- raise ArgumentError, "'#{self.name}' has no association called '#{association}'" unless has_many_association
22
+ counters.each do |counter_association|
23
+ has_many_association = _reflect_on_association(counter_association)
24
+ unless has_many_association
25
+ has_many = reflect_on_all_associations(:has_many)
26
+ has_many_association = has_many.find { |association| association.counter_cache_column && association.counter_cache_column.to_sym == counter_association.to_sym }
27
+ counter_association = has_many_association.plural_name if has_many_association
28
+ end
29
+ raise ArgumentError, "'#{self.name}' has no association called '#{counter_association}'" unless has_many_association
25
30
 
26
31
  if has_many_association.is_a? ActiveRecord::Reflection::ThroughReflection
27
32
  has_many_association = has_many_association.through_reflection
@@ -29,11 +34,11 @@ module ActiveRecord
29
34
 
30
35
  foreign_key = has_many_association.foreign_key.to_s
31
36
  child_class = has_many_association.klass
32
- reflection = child_class._reflections.values.find { |e| :belongs_to == e.macro && e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? }
37
+ reflection = child_class._reflections.values.find { |e| e.belongs_to? && e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? }
33
38
  counter_name = reflection.counter_cache_column
34
39
 
35
40
  stmt = unscoped.where(arel_table[primary_key].eq(object.id)).arel.compile_update({
36
- arel_table[counter_name] => object.send(association).count(:all)
41
+ arel_table[counter_name] => object.send(counter_association).count(:all)
37
42
  }, primary_key)
38
43
  connection.update stmt
39
44
  end
@@ -117,5 +122,54 @@ module ActiveRecord
117
122
  update_counters(id, counter_name => -1)
118
123
  end
119
124
  end
125
+
126
+ protected
127
+
128
+ def actually_destroyed?
129
+ @_actually_destroyed
130
+ end
131
+
132
+ def clear_destroy_state
133
+ @_actually_destroyed = nil
134
+ end
135
+
136
+ private
137
+
138
+ def _create_record(*)
139
+ id = super
140
+
141
+ each_counter_cached_associations do |association|
142
+ if send(association.reflection.name)
143
+ association.increment_counters
144
+ @_after_create_counter_called = true
145
+ end
146
+ end
147
+
148
+ id
149
+ end
150
+
151
+ def destroy_row
152
+ affected_rows = super
153
+
154
+ if affected_rows > 0
155
+ each_counter_cached_associations do |association|
156
+ foreign_key = association.reflection.foreign_key.to_sym
157
+ unless destroyed_by_association && destroyed_by_association.foreign_key.to_sym == foreign_key
158
+ if send(association.reflection.name)
159
+ association.decrement_counters
160
+ end
161
+ end
162
+ end
163
+ end
164
+
165
+ affected_rows
166
+ end
167
+
168
+ def each_counter_cached_associations
169
+ _reflections.each do |name, reflection|
170
+ yield association(name.to_sym) if reflection.belongs_to? && reflection.counter_cache_column
171
+ end
172
+ end
173
+
120
174
  end
121
175
  end
@@ -18,17 +18,18 @@ module ActiveRecord
18
18
  # conversation.archived? # => true
19
19
  # conversation.status # => "archived"
20
20
  #
21
- # # conversation.update! status: 1
21
+ # # conversation.status = 1
22
22
  # conversation.status = "archived"
23
23
  #
24
- # # conversation.update! status: nil
25
24
  # conversation.status = nil
26
25
  # conversation.status.nil? # => true
27
26
  # conversation.status # => nil
28
27
  #
29
28
  # Scopes based on the allowed values of the enum field will be provided
30
- # as well. With the above example, it will create an +active+ and +archived+
31
- # scope.
29
+ # as well. With the above example:
30
+ #
31
+ # Conversation.active
32
+ # Conversation.archived
32
33
  #
33
34
  # You can set the default value from the database declaration, like:
34
35
  #
@@ -68,7 +69,7 @@ module ActiveRecord
68
69
  # Where conditions on an enum attribute must use the ordinal value of an enum.
69
70
  module Enum
70
71
  def self.extended(base) # :nodoc:
71
- base.class_attribute(:defined_enums)
72
+ base.class_attribute(:defined_enums, instance_writer: false)
72
73
  base.defined_enums = {}
73
74
  end
74
75
 
@@ -138,19 +139,16 @@ module ActiveRecord
138
139
  @_enum_methods_module ||= begin
139
140
  mod = Module.new do
140
141
  private
141
- def save_changed_attribute(attr_name, value)
142
+ def save_changed_attribute(attr_name, old)
142
143
  if (mapping = self.class.defined_enums[attr_name.to_s])
144
+ value = _read_attribute(attr_name)
143
145
  if attribute_changed?(attr_name)
144
- old = changed_attributes[attr_name]
145
-
146
146
  if mapping[old] == value
147
- changed_attributes.delete(attr_name)
147
+ clear_attribute_changes([attr_name])
148
148
  end
149
149
  else
150
- old = clone_attribute_value(:read_attribute, attr_name)
151
-
152
150
  if old != value
153
- changed_attributes[attr_name] = mapping.key old
151
+ set_attribute_was(attr_name, mapping.key(old))
154
152
  end
155
153
  end
156
154
  else
@@ -30,17 +30,18 @@ module ActiveRecord
30
30
  class SerializationTypeMismatch < ActiveRecordError
31
31
  end
32
32
 
33
- # Raised when adapter not specified on connection (or configuration file <tt>config/database.yml</tt>
34
- # misses adapter field).
33
+ # Raised when adapter not specified on connection (or configuration file
34
+ # +config/database.yml+ misses adapter field).
35
35
  class AdapterNotSpecified < ActiveRecordError
36
36
  end
37
37
 
38
- # Raised when Active Record cannot find database adapter specified in <tt>config/database.yml</tt> or programmatically.
38
+ # Raised when Active Record cannot find database adapter specified in
39
+ # +config/database.yml+ or programmatically.
39
40
  class AdapterNotFound < ActiveRecordError
40
41
  end
41
42
 
42
- # Raised when connection to the database could not been established (for example when <tt>connection=</tt>
43
- # is given a nil object).
43
+ # Raised when connection to the database could not been established (for
44
+ # example when +connection=+ is given a nil object).
44
45
  class ConnectionNotEstablished < ActiveRecordError
45
46
  end
46
47
 
@@ -51,10 +52,29 @@ module ActiveRecord
51
52
  # Raised by ActiveRecord::Base.save! and ActiveRecord::Base.create! methods when record cannot be
52
53
  # saved because record is invalid.
53
54
  class RecordNotSaved < ActiveRecordError
55
+ attr_reader :record
56
+
57
+ def initialize(message, record = nil)
58
+ @record = record
59
+ super(message)
60
+ end
54
61
  end
55
62
 
56
63
  # Raised by ActiveRecord::Base.destroy! when a call to destroy would return false.
64
+ #
65
+ # begin
66
+ # complex_operation_that_internally_calls_destroy!
67
+ # rescue ActiveRecord::RecordNotDestroyed => invalid
68
+ # puts invalid.record.errors
69
+ # end
70
+ #
57
71
  class RecordNotDestroyed < ActiveRecordError
72
+ attr_reader :record
73
+
74
+ def initialize(message, record = nil)
75
+ @record = record
76
+ super(message)
77
+ end
58
78
  end
59
79
 
60
80
  # Superclass for all database execution errors.
@@ -82,35 +102,26 @@ module ActiveRecord
82
102
  class InvalidForeignKey < WrappedDatabaseException
83
103
  end
84
104
 
85
- # Raised when number of bind variables in statement given to <tt>:condition</tt> key (for example,
86
- # when using +find+ method)
87
- # does not match number of expected variables.
105
+ # Raised when number of bind variables in statement given to +:condition+ key
106
+ # (for example, when using +find+ method) does not match number of expected
107
+ # values supplied.
88
108
  #
89
- # For example, in
109
+ # For example, when there are two placeholders with only one value supplied:
90
110
  #
91
111
  # Location.where("lat = ? AND lng = ?", 53.7362)
92
- #
93
- # two placeholders are given but only one variable to fill them.
94
112
  class PreparedStatementInvalid < ActiveRecordError
95
113
  end
96
114
 
97
- # Raised when a given database does not exist
98
- class NoDatabaseError < ActiveRecordError
99
- def initialize(message)
100
- super extend_message(message)
101
- end
102
-
103
- # can be over written to add additional error information.
104
- def extend_message(message)
105
- message
106
- end
115
+ # Raised when a given database does not exist.
116
+ class NoDatabaseError < StatementInvalid
107
117
  end
108
118
 
109
119
  # Raised on attempt to save stale record. Record is stale when it's being saved in another query after
110
120
  # instantiation, for example, when two users edit the same wiki page and one starts editing and saves
111
121
  # the page before the other.
112
122
  #
113
- # Read more about optimistic locking in ActiveRecord::Locking module RDoc.
123
+ # Read more about optimistic locking in ActiveRecord::Locking module
124
+ # documentation.
114
125
  class StaleObjectError < ActiveRecordError
115
126
  attr_reader :record, :attempted_action
116
127
 
@@ -122,8 +133,9 @@ module ActiveRecord
122
133
 
123
134
  end
124
135
 
125
- # Raised when association is being configured improperly or
126
- # user tries to use offset and limit together with has_many or has_and_belongs_to_many associations.
136
+ # Raised when association is being configured improperly or user tries to use
137
+ # offset and limit together with +has_many+ or +has_and_belongs_to_many+
138
+ # associations.
127
139
  class ConfigurationError < ActiveRecordError
128
140
  end
129
141
 
@@ -161,7 +173,8 @@ module ActiveRecord
161
173
  class Rollback < ActiveRecordError
162
174
  end
163
175
 
164
- # Raised when attribute has a name reserved by Active Record (when attribute has name of one of Active Record instance methods).
176
+ # Raised when attribute has a name reserved by Active Record (when attribute
177
+ # has name of one of Active Record instance methods).
165
178
  class DangerousAttributeError < ActiveRecordError
166
179
  end
167
180
 
@@ -173,16 +186,17 @@ module ActiveRecord
173
186
  def initialize(record, attribute)
174
187
  @record = record
175
188
  @attribute = attribute.to_s
176
- super("unknown attribute: #{attribute}")
189
+ super("unknown attribute '#{attribute}' for #{@record.class}.")
177
190
  end
178
191
 
179
192
  end
180
193
 
181
194
  # Raised when an error occurred while doing a mass assignment to an attribute through the
182
- # <tt>attributes=</tt> method. The exception has an +attribute+ property that is the name of the
195
+ # +attributes=+ method. The exception has an +attribute+ property that is the name of the
183
196
  # offending attribute.
184
197
  class AttributeAssignmentError < ActiveRecordError
185
198
  attr_reader :exception, :attribute
199
+
186
200
  def initialize(message, exception, attribute)
187
201
  super(message)
188
202
  @exception = exception
@@ -195,6 +209,7 @@ module ActiveRecord
195
209
  # objects, each corresponding to the error while assigning to an attribute.
196
210
  class MultiparameterAssignmentErrors < ActiveRecordError
197
211
  attr_reader :errors
212
+
198
213
  def initialize(errors)
199
214
  @errors = errors
200
215
  end
@@ -204,11 +219,12 @@ module ActiveRecord
204
219
  class UnknownPrimaryKey < ActiveRecordError
205
220
  attr_reader :model
206
221
 
207
- def initialize(model)
208
- super("Unknown primary key for table #{model.table_name} in model #{model}.")
222
+ def initialize(model, description = nil)
223
+ message = "Unknown primary key for table #{model.table_name} in model #{model}."
224
+ message += "\n#{description}" if description
225
+ super(message)
209
226
  @model = model
210
227
  end
211
-
212
228
  end
213
229
 
214
230
  # Raised when a relation cannot be mutated because it's already loaded.
@@ -225,6 +241,13 @@ module ActiveRecord
225
241
  class ImmutableRelation < ActiveRecordError
226
242
  end
227
243
 
244
+ # TransactionIsolationError will be raised under the following conditions:
245
+ #
246
+ # * The adapter does not support setting the isolation level
247
+ # * You are joining an existing open transaction
248
+ # * You are creating a nested (savepoint) transaction
249
+ #
250
+ # The mysql, mysql2 and postgresql adapters support setting the transaction isolation level.
228
251
  class TransactionIsolationError < ActiveRecordError
229
252
  end
230
253
  end
@@ -27,7 +27,7 @@ module ActiveRecord
27
27
  end.join("\n")
28
28
  end.join("\n")
29
29
 
30
- # Overriding inspect to be more human readable, specially in the console.
30
+ # Overriding inspect to be more human readable, especially in the console.
31
31
  def str.inspect
32
32
  self
33
33
  end
@@ -19,7 +19,7 @@ module ActiveRecord
19
19
  # On the other hand, we want to monitor the performance of our real database
20
20
  # queries, not the performance of the access to the query cache.
21
21
  IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN CACHE)
22
- EXPLAINED_SQLS = /\A\s*(select|update|delete|insert)\b/i
22
+ EXPLAINED_SQLS = /\A\s*(with|select|update|delete|insert)\b/i
23
23
  def ignore_payload?(payload)
24
24
  payload[:exception] || IGNORED_PAYLOADS.include?(payload[:name]) || payload[:sql] !~ EXPLAINED_SQLS
25
25
  end