activerecord 4.1.16 → 4.2.11.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (185) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1162 -1801
  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 +83 -38
  9. data/lib/active_record/associations/belongs_to_association.rb +28 -10
  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/collection_association.rb +5 -1
  13. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +8 -13
  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 +63 -27
  18. data/lib/active_record/associations/collection_proxy.rb +29 -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 -13
  26. data/lib/active_record/associations/preloader/association.rb +14 -11
  27. data/lib/active_record/associations/preloader/through_association.rb +4 -3
  28. data/lib/active_record/associations/preloader.rb +36 -26
  29. data/lib/active_record/associations/singular_association.rb +17 -2
  30. data/lib/active_record/associations/through_association.rb +5 -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 +19 -11
  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 -40
  42. data/lib/active_record/attribute_methods/write.rb +9 -24
  43. data/lib/active_record/attribute_methods.rb +56 -94
  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 +19 -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 +84 -52
  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 +138 -56
  57. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
  58. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +268 -71
  59. data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
  60. data/lib/active_record/connection_adapters/abstract_adapter.rb +171 -59
  61. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +293 -139
  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 +16 -32
  65. data/lib/active_record/connection_adapters/mysql_adapter.rb +67 -144
  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 -388
  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 +131 -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 -39
  105. data/lib/active_record/counter_cache.rb +60 -6
  106. data/lib/active_record/enum.rb +9 -11
  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 +55 -69
  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 +71 -46
  119. data/lib/active_record/model_schema.rb +52 -58
  120. data/lib/active_record/nested_attributes.rb +5 -5
  121. data/lib/active_record/no_touching.rb +1 -1
  122. data/lib/active_record/persistence.rb +46 -26
  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 +18 -11
  126. data/lib/active_record/railties/databases.rake +50 -51
  127. data/lib/active_record/readonly_attributes.rb +0 -1
  128. data/lib/active_record/reflection.rb +273 -114
  129. data/lib/active_record/relation/batches.rb +0 -2
  130. data/lib/active_record/relation/calculations.rb +41 -37
  131. data/lib/active_record/relation/finder_methods.rb +70 -47
  132. data/lib/active_record/relation/merger.rb +39 -29
  133. data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
  134. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -5
  135. data/lib/active_record/relation/predicate_builder.rb +16 -8
  136. data/lib/active_record/relation/query_methods.rb +114 -65
  137. data/lib/active_record/relation/spawn_methods.rb +3 -0
  138. data/lib/active_record/relation.rb +57 -25
  139. data/lib/active_record/result.rb +18 -7
  140. data/lib/active_record/sanitization.rb +12 -2
  141. data/lib/active_record/schema.rb +0 -1
  142. data/lib/active_record/schema_dumper.rb +59 -28
  143. data/lib/active_record/schema_migration.rb +5 -4
  144. data/lib/active_record/scoping/default.rb +6 -4
  145. data/lib/active_record/scoping/named.rb +4 -0
  146. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  147. data/lib/active_record/statement_cache.rb +95 -10
  148. data/lib/active_record/store.rb +5 -5
  149. data/lib/active_record/tasks/database_tasks.rb +61 -6
  150. data/lib/active_record/tasks/mysql_database_tasks.rb +20 -11
  151. data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -9
  152. data/lib/active_record/timestamp.rb +9 -7
  153. data/lib/active_record/transactions.rb +53 -27
  154. data/lib/active_record/type/big_integer.rb +13 -0
  155. data/lib/active_record/type/binary.rb +50 -0
  156. data/lib/active_record/type/boolean.rb +31 -0
  157. data/lib/active_record/type/date.rb +50 -0
  158. data/lib/active_record/type/date_time.rb +54 -0
  159. data/lib/active_record/type/decimal.rb +64 -0
  160. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  161. data/lib/active_record/type/decorator.rb +14 -0
  162. data/lib/active_record/type/float.rb +19 -0
  163. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  164. data/lib/active_record/type/integer.rb +59 -0
  165. data/lib/active_record/type/mutable.rb +16 -0
  166. data/lib/active_record/type/numeric.rb +36 -0
  167. data/lib/active_record/type/serialized.rb +62 -0
  168. data/lib/active_record/type/string.rb +40 -0
  169. data/lib/active_record/type/text.rb +11 -0
  170. data/lib/active_record/type/time.rb +26 -0
  171. data/lib/active_record/type/time_value.rb +38 -0
  172. data/lib/active_record/type/type_map.rb +64 -0
  173. data/lib/active_record/type/unsigned_integer.rb +15 -0
  174. data/lib/active_record/type/value.rb +110 -0
  175. data/lib/active_record/type.rb +23 -0
  176. data/lib/active_record/validations/associated.rb +5 -3
  177. data/lib/active_record/validations/presence.rb +5 -3
  178. data/lib/active_record/validations/uniqueness.rb +25 -29
  179. data/lib/active_record/validations.rb +25 -19
  180. data/lib/active_record.rb +4 -0
  181. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
  182. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
  183. data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
  184. metadata +66 -11
  185. 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
  #
@@ -88,11 +88,14 @@ module ActiveRecord
88
88
  mattr_accessor :maintain_test_schema, instance_accessor: false
89
89
 
90
90
  def self.disable_implicit_join_references=(value)
91
- ActiveSupport::Deprecation.warn("Implicit join references were removed with Rails 4.1." \
92
- "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
93
95
  end
94
96
 
95
97
  class_attribute :default_connection_handler, instance_writer: false
98
+ class_attribute :find_by_statement_cache
96
99
 
97
100
  def self.connection_handler
98
101
  ActiveRecord::RuntimeRegistry.connection_handler || default_connection_handler
@@ -106,7 +109,94 @@ module ActiveRecord
106
109
  end
107
110
 
108
111
  module ClassMethods
109
- 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:
110
200
  generated_association_methods
111
201
  end
112
202
 
@@ -180,12 +270,8 @@ module ActiveRecord
180
270
  # # Instantiates a single new object
181
271
  # User.new(first_name: 'Jamie')
182
272
  def initialize(attributes = nil, options = {})
183
- defaults = self.class.column_defaults.dup
184
- defaults.each { |k, v| defaults[k] = v.dup if v.duplicable? }
185
-
186
- @attributes = self.class.initialize_attributes(defaults)
187
- @column_types_override = nil
188
- @column_types = self.class.column_types
273
+ @attributes = self.class._default_attributes.dup
274
+ self.class.define_attribute_methods
189
275
 
190
276
  init_internals
191
277
  initialize_internals_callback
@@ -195,32 +281,35 @@ module ActiveRecord
195
281
  init_attributes(attributes, options) if attributes
196
282
 
197
283
  yield self if block_given?
198
- run_callbacks :initialize unless _initialize_callbacks.empty?
284
+ _run_initialize_callbacks
199
285
  end
200
286
 
201
- # Initialize an empty model object from +coder+. +coder+ must contain
202
- # the attributes necessary for initializing an empty model object. For
203
- # 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`
204
290
  #
205
291
  # class Post < ActiveRecord::Base
206
292
  # end
207
293
  #
294
+ # old_post = Post.new(title: "hello world")
295
+ # coder = {}
296
+ # old_post.encode_with(coder)
297
+ #
208
298
  # post = Post.allocate
209
- # post.init_with('attributes' => { 'title' => 'hello world' })
299
+ # post.init_with(coder)
210
300
  # post.title # => 'hello world'
211
301
  def init_with(coder)
212
- @attributes = self.class.initialize_attributes(coder['attributes'])
213
- @column_types_override = coder['column_types']
214
- @column_types = self.class.column_types
302
+ coder = LegacyYamlAdapter.convert(self.class, coder)
303
+ @attributes = coder['attributes']
215
304
 
216
305
  init_internals
217
306
 
218
- @new_record = false
307
+ @new_record = coder['new_record']
219
308
 
220
309
  self.class.define_attribute_methods
221
310
 
222
- run_callbacks :find
223
- run_callbacks :initialize
311
+ _run_find_callbacks
312
+ _run_initialize_callbacks
224
313
 
225
314
  self
226
315
  end
@@ -253,17 +342,13 @@ module ActiveRecord
253
342
 
254
343
  ##
255
344
  def initialize_dup(other) # :nodoc:
256
- cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast)
257
- self.class.initialize_attributes(cloned_attributes, :serialized => false)
258
-
259
- @attributes = cloned_attributes
260
- @attributes[self.class.primary_key] = nil
345
+ @attributes = @attributes.dup
346
+ @attributes.reset(self.class.primary_key)
261
347
 
262
- run_callbacks(:initialize) unless _initialize_callbacks.empty?
348
+ _run_initialize_callbacks
263
349
 
264
350
  @aggregation_cache = {}
265
351
  @association_cache = {}
266
- @attributes_cache = {}
267
352
 
268
353
  @new_record = true
269
354
  @destroyed = false
@@ -284,7 +369,11 @@ module ActiveRecord
284
369
  # Post.new.encode_with(coder)
285
370
  # coder # => {"attributes" => {"id" => nil, ... }}
286
371
  def encode_with(coder)
287
- 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
288
377
  end
289
378
 
290
379
  # Returns true if +comparison_object+ is the same exact object, or +comparison_object+
@@ -307,7 +396,11 @@ module ActiveRecord
307
396
  # Delegates to id in order to allow two records of the same type and id to work with something like:
308
397
  # [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
309
398
  def hash
310
- id.hash
399
+ if id
400
+ id.hash
401
+ else
402
+ super
403
+ end
311
404
  end
312
405
 
313
406
  # Clone and freeze the attributes hash such that associations are still
@@ -363,21 +456,45 @@ module ActiveRecord
363
456
  "#<#{self.class} #{inspection}>"
364
457
  end
365
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
+
366
483
  # Returns a hash of the given methods with their names as keys and returned values as values.
367
484
  def slice(*methods)
368
485
  Hash[methods.map! { |method| [method, public_send(method)] }].with_indifferent_access
369
486
  end
370
487
 
488
+ private
489
+
371
490
  def set_transaction_state(state) # :nodoc:
372
491
  @transaction_state = state
373
492
  end
374
493
 
375
494
  def has_transactional_callbacks? # :nodoc:
376
- !_rollback_callbacks.empty? || !_commit_callbacks.empty? || !_create_callbacks.empty?
495
+ !_rollback_callbacks.empty? || !_commit_callbacks.empty?
377
496
  end
378
497
 
379
- private
380
-
381
498
  # Updates the attributes on this particular ActiveRecord object so that
382
499
  # if it is associated with a transaction, then the state of the AR object
383
500
  # will be updated to reflect the current state of the transaction
@@ -400,6 +517,8 @@ module ActiveRecord
400
517
  end
401
518
 
402
519
  def update_attributes_from_transaction_state(transaction_state, depth)
520
+ @reflects_state = [false] if depth == 0
521
+
403
522
  if transaction_state && transaction_state.finalized? && !has_transactional_callbacks?
404
523
  unless @reflects_state[depth]
405
524
  restore_transaction_record_state if transaction_state.rolledback?
@@ -426,12 +545,8 @@ module ActiveRecord
426
545
  end
427
546
 
428
547
  def init_internals
429
- pk = self.class.primary_key
430
- @attributes[pk] = nil unless @attributes.key?(pk)
431
-
432
548
  @aggregation_cache = {}
433
549
  @association_cache = {}
434
- @attributes_cache = {}
435
550
  @readonly = false
436
551
  @destroyed = false
437
552
  @marked_for_destruction = false
@@ -440,7 +555,6 @@ module ActiveRecord
440
555
  @txn = nil
441
556
  @_start_transaction_state = {}
442
557
  @transaction_state = nil
443
- @reflects_state = [false]
444
558
  end
445
559
 
446
560
  def initialize_internals_callback
@@ -451,5 +565,15 @@ module ActiveRecord
451
565
  def init_attributes(attributes, options)
452
566
  assign_attributes(attributes)
453
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
454
578
  end
455
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
  #
@@ -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