activerecord 4.1.15 → 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 -1792
  3. data/README.rdoc +15 -10
  4. data/lib/active_record.rb +4 -0
  5. data/lib/active_record/aggregations.rb +15 -8
  6. data/lib/active_record/association_relation.rb +13 -0
  7. data/lib/active_record/associations.rb +158 -49
  8. data/lib/active_record/associations/alias_tracker.rb +3 -12
  9. data/lib/active_record/associations/association.rb +16 -4
  10. data/lib/active_record/associations/association_scope.rb +83 -38
  11. data/lib/active_record/associations/belongs_to_association.rb +28 -10
  12. data/lib/active_record/associations/builder/association.rb +15 -4
  13. data/lib/active_record/associations/builder/belongs_to.rb +7 -29
  14. data/lib/active_record/associations/builder/collection_association.rb +5 -1
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +8 -13
  16. data/lib/active_record/associations/builder/has_many.rb +1 -1
  17. data/lib/active_record/associations/builder/has_one.rb +2 -2
  18. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  19. data/lib/active_record/associations/collection_association.rb +63 -27
  20. data/lib/active_record/associations/collection_proxy.rb +29 -35
  21. data/lib/active_record/associations/foreign_association.rb +11 -0
  22. data/lib/active_record/associations/has_many_association.rb +83 -22
  23. data/lib/active_record/associations/has_many_through_association.rb +49 -26
  24. data/lib/active_record/associations/has_one_association.rb +1 -1
  25. data/lib/active_record/associations/join_dependency.rb +26 -13
  26. data/lib/active_record/associations/join_dependency/join_association.rb +25 -15
  27. data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
  28. data/lib/active_record/associations/preloader.rb +36 -26
  29. data/lib/active_record/associations/preloader/association.rb +14 -11
  30. data/lib/active_record/associations/preloader/through_association.rb +4 -3
  31. data/lib/active_record/associations/singular_association.rb +17 -2
  32. data/lib/active_record/associations/through_association.rb +5 -12
  33. data/lib/active_record/attribute.rb +163 -0
  34. data/lib/active_record/attribute_assignment.rb +19 -11
  35. data/lib/active_record/attribute_decorators.rb +66 -0
  36. data/lib/active_record/attribute_methods.rb +56 -94
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  38. data/lib/active_record/attribute_methods/dirty.rb +107 -43
  39. data/lib/active_record/attribute_methods/primary_key.rb +7 -8
  40. data/lib/active_record/attribute_methods/query.rb +1 -1
  41. data/lib/active_record/attribute_methods/read.rb +22 -59
  42. data/lib/active_record/attribute_methods/serialization.rb +16 -150
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -40
  44. data/lib/active_record/attribute_methods/write.rb +9 -24
  45. data/lib/active_record/attribute_set.rb +81 -0
  46. data/lib/active_record/attribute_set/builder.rb +106 -0
  47. data/lib/active_record/attributes.rb +147 -0
  48. data/lib/active_record/autosave_association.rb +19 -12
  49. data/lib/active_record/base.rb +13 -24
  50. data/lib/active_record/callbacks.rb +6 -6
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +84 -52
  52. data/lib/active_record/connection_adapters/abstract/database_statements.rb +52 -50
  53. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
  54. data/lib/active_record/connection_adapters/abstract/quoting.rb +60 -60
  55. data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
  56. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +39 -4
  57. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +138 -56
  58. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +268 -71
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +171 -59
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +293 -139
  63. data/lib/active_record/connection_adapters/column.rb +29 -240
  64. data/lib/active_record/connection_adapters/connection_specification.rb +15 -24
  65. data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
  66. data/lib/active_record/connection_adapters/mysql_adapter.rb +67 -144
  67. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  68. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  69. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -25
  70. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
  71. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  96. data/lib/active_record/connection_adapters/postgresql/quoting.rb +46 -136
  97. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  99. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +131 -43
  100. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  101. data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -477
  102. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  103. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -75
  104. data/lib/active_record/connection_handling.rb +1 -1
  105. data/lib/active_record/core.rb +163 -39
  106. data/lib/active_record/counter_cache.rb +60 -6
  107. data/lib/active_record/enum.rb +9 -11
  108. data/lib/active_record/errors.rb +53 -30
  109. data/lib/active_record/explain.rb +1 -1
  110. data/lib/active_record/explain_subscriber.rb +1 -1
  111. data/lib/active_record/fixtures.rb +55 -69
  112. data/lib/active_record/gem_version.rb +4 -4
  113. data/lib/active_record/inheritance.rb +35 -10
  114. data/lib/active_record/integration.rb +4 -4
  115. data/lib/active_record/legacy_yaml_adapter.rb +30 -0
  116. data/lib/active_record/locking/optimistic.rb +46 -26
  117. data/lib/active_record/migration.rb +71 -46
  118. data/lib/active_record/migration/command_recorder.rb +19 -2
  119. data/lib/active_record/migration/join_table.rb +1 -1
  120. data/lib/active_record/model_schema.rb +52 -58
  121. data/lib/active_record/nested_attributes.rb +5 -5
  122. data/lib/active_record/no_touching.rb +1 -1
  123. data/lib/active_record/persistence.rb +46 -26
  124. data/lib/active_record/query_cache.rb +3 -3
  125. data/lib/active_record/querying.rb +10 -7
  126. data/lib/active_record/railtie.rb +18 -11
  127. data/lib/active_record/railties/databases.rake +50 -51
  128. data/lib/active_record/readonly_attributes.rb +0 -1
  129. data/lib/active_record/reflection.rb +273 -114
  130. data/lib/active_record/relation.rb +57 -25
  131. data/lib/active_record/relation/batches.rb +0 -2
  132. data/lib/active_record/relation/calculations.rb +41 -37
  133. data/lib/active_record/relation/finder_methods.rb +70 -47
  134. data/lib/active_record/relation/merger.rb +39 -29
  135. data/lib/active_record/relation/predicate_builder.rb +16 -8
  136. data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
  137. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -5
  138. data/lib/active_record/relation/query_methods.rb +114 -65
  139. data/lib/active_record/relation/spawn_methods.rb +3 -0
  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 -6
  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 +53 -27
  155. data/lib/active_record/type.rb +23 -0
  156. data/lib/active_record/type/big_integer.rb +13 -0
  157. data/lib/active_record/type/binary.rb +50 -0
  158. data/lib/active_record/type/boolean.rb +31 -0
  159. data/lib/active_record/type/date.rb +50 -0
  160. data/lib/active_record/type/date_time.rb +54 -0
  161. data/lib/active_record/type/decimal.rb +64 -0
  162. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  163. data/lib/active_record/type/decorator.rb +14 -0
  164. data/lib/active_record/type/float.rb +19 -0
  165. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  166. data/lib/active_record/type/integer.rb +59 -0
  167. data/lib/active_record/type/mutable.rb +16 -0
  168. data/lib/active_record/type/numeric.rb +36 -0
  169. data/lib/active_record/type/serialized.rb +62 -0
  170. data/lib/active_record/type/string.rb +40 -0
  171. data/lib/active_record/type/text.rb +11 -0
  172. data/lib/active_record/type/time.rb +26 -0
  173. data/lib/active_record/type/time_value.rb +38 -0
  174. data/lib/active_record/type/type_map.rb +64 -0
  175. data/lib/active_record/type/unsigned_integer.rb +15 -0
  176. data/lib/active_record/type/value.rb +110 -0
  177. data/lib/active_record/validations.rb +25 -19
  178. data/lib/active_record/validations/associated.rb +5 -3
  179. data/lib/active_record/validations/presence.rb +5 -3
  180. data/lib/active_record/validations/uniqueness.rb +25 -29
  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
@@ -74,7 +74,9 @@ module ActiveRecord
74
74
  :rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps,
75
75
  :change_column_default, :add_reference, :remove_reference, :transaction,
76
76
  :drop_join_table, :drop_table, :execute_block, :enable_extension,
77
- :change_column, :execute, :remove_columns, :change_column_null # irreversible methods need to be here too
77
+ :change_column, :execute, :remove_columns, :change_column_null,
78
+ :add_foreign_key, :remove_foreign_key
79
+ # irreversible methods need to be here too
78
80
  ].each do |method|
79
81
  class_eval <<-EOV, __FILE__, __LINE__ + 1
80
82
  def #{method}(*args, &block) # def create_table(*args, &block)
@@ -85,7 +87,7 @@ module ActiveRecord
85
87
  alias :add_belongs_to :add_reference
86
88
  alias :remove_belongs_to :remove_reference
87
89
 
88
- def change_table(table_name, options = {})
90
+ def change_table(table_name, options = {}) # :nodoc:
89
91
  yield delegate.update_table_definition(table_name, self)
90
92
  end
91
93
 
@@ -167,6 +169,21 @@ module ActiveRecord
167
169
  [:change_column_null, args]
168
170
  end
169
171
 
172
+ def invert_add_foreign_key(args)
173
+ from_table, to_table, add_options = args
174
+ add_options ||= {}
175
+
176
+ if add_options[:name]
177
+ options = { name: add_options[:name] }
178
+ elsif add_options[:column]
179
+ options = { column: add_options[:column] }
180
+ else
181
+ options = to_table
182
+ end
183
+
184
+ [:remove_foreign_key, [from_table, options]]
185
+ end
186
+
170
187
  # Forwards any missing method call to the \target.
171
188
  def method_missing(method, *args, &block)
172
189
  if @delegate.respond_to?(method)
@@ -8,7 +8,7 @@ module ActiveRecord
8
8
  end
9
9
 
10
10
  def join_table_name(table_1, table_2)
11
- [table_1.to_s, table_2.to_s].sort.join("_").to_sym
11
+ ModelSchema.derive_join_table_name(table_1, table_2).to_sym
12
12
  end
13
13
  end
14
14
  end
@@ -29,6 +29,10 @@ module ActiveRecord
29
29
  # :singleton-method:
30
30
  # Works like +table_name_prefix+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
31
31
  # "people_basecamp"). By default, the suffix is the empty string.
32
+ #
33
+ # If you are organising your models within modules, you can add a suffix to the models within
34
+ # a namespace by defining a singleton method in the parent module called table_name_suffix which
35
+ # returns your chosen suffix.
32
36
  class_attribute :table_name_suffix, instance_writer: false
33
37
  self.table_name_suffix = ""
34
38
 
@@ -47,6 +51,19 @@ module ActiveRecord
47
51
  self.pluralize_table_names = true
48
52
 
49
53
  self.inheritance_column = 'type'
54
+
55
+ delegate :type_for_attribute, to: :class
56
+ end
57
+
58
+ # Derives the join table name for +first_table+ and +second_table+. The
59
+ # table names appear in alphabetical order. A common prefix is removed
60
+ # (useful for namespaced models like Music::Artist and Music::Record):
61
+ #
62
+ # artists, records => artists_records
63
+ # records, artists => artists_records
64
+ # music_artists, music_records => music_artists_records
65
+ def self.derive_join_table_name(first_table, second_table) # :nodoc:
66
+ [first_table.to_s, second_table.to_s].sort.join("\0").gsub(/^(.*_)(.+)\0\1(.+)/, '\1\2_\3').tr("\0", "_")
50
67
  end
51
68
 
52
69
  module ClassMethods
@@ -153,6 +170,10 @@ module ActiveRecord
153
170
  (parents.detect{ |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix
154
171
  end
155
172
 
173
+ def full_table_name_suffix #:nodoc:
174
+ (parents.detect {|p| p.respond_to?(:table_name_suffix) } || self).table_name_suffix
175
+ end
176
+
156
177
  # Defines the name of the table column which will store the class name on single-table
157
178
  # inheritance situations.
158
179
  #
@@ -190,7 +211,7 @@ module ActiveRecord
190
211
  # given block. This is required for Oracle and is useful for any
191
212
  # database which relies on sequences for primary key generation.
192
213
  #
193
- # If a sequence name is not explicitly set when using Oracle or Firebird,
214
+ # If a sequence name is not explicitly set when using Oracle,
194
215
  # it will default to the commonly used pattern of: #{table_name}_seq
195
216
  #
196
217
  # If a sequence name is not explicitly set when using PostgreSQL, it
@@ -209,50 +230,29 @@ module ActiveRecord
209
230
  connection.schema_cache.table_exists?(table_name)
210
231
  end
211
232
 
212
- # Returns an array of column objects for the table associated with this class.
213
- def columns
214
- @columns ||= connection.schema_cache.columns(table_name).map do |col|
215
- col = col.dup
216
- col.primary = (col.name == primary_key)
217
- col
218
- end
219
- end
220
-
221
- # Returns a hash of column objects for the table associated with this class.
222
- def columns_hash
223
- @columns_hash ||= Hash[columns.map { |c| [c.name, c] }]
233
+ def attributes_builder # :nodoc:
234
+ @attributes_builder ||= AttributeSet::Builder.new(column_types, primary_key)
224
235
  end
225
236
 
226
237
  def column_types # :nodoc:
227
- @column_types ||= decorate_columns(columns_hash.dup)
228
- end
229
-
230
- def decorate_columns(columns_hash) # :nodoc:
231
- return if columns_hash.empty?
232
-
233
- @serialized_column_names ||= self.columns_hash.keys.find_all do |name|
234
- serialized_attributes.key?(name)
235
- end
236
-
237
- @serialized_column_names.each do |name|
238
- columns_hash[name] = AttributeMethods::Serialization::Type.new(columns_hash[name])
239
- end
240
-
241
- @time_zone_column_names ||= self.columns_hash.find_all do |name, col|
242
- create_time_zone_conversion_attribute?(name, col)
243
- end.map!(&:first)
244
-
245
- @time_zone_column_names.each do |name|
246
- columns_hash[name] = AttributeMethods::TimeZoneConversion::Type.new(columns_hash[name])
238
+ @column_types ||= columns_hash.transform_values(&:cast_type).tap do |h|
239
+ h.default = Type::Value.new
247
240
  end
241
+ end
248
242
 
249
- columns_hash
243
+ def type_for_attribute(attr_name) # :nodoc:
244
+ column_types[attr_name]
250
245
  end
251
246
 
252
247
  # Returns a hash where the keys are column names and the values are
253
248
  # default values when instantiating the AR object for this table.
254
249
  def column_defaults
255
- @column_defaults ||= Hash[columns.map { |c| [c.name, c.default] }]
250
+ _default_attributes.dup.to_hash
251
+ end
252
+
253
+ def _default_attributes # :nodoc:
254
+ @default_attributes ||= attributes_builder.build_from_database(
255
+ raw_default_values)
256
256
  end
257
257
 
258
258
  # Returns an array of column names as strings.
@@ -263,7 +263,7 @@ module ActiveRecord
263
263
  # Returns an array of column objects where the primary id, all columns ending in "_id" or "_count",
264
264
  # and columns used for single table inheritance have been removed.
265
265
  def content_columns
266
- @content_columns ||= columns.reject { |c| c.primary || c.name =~ /(_id|_count)$/ || c.name == inheritance_column }
266
+ @content_columns ||= columns.reject { |c| c.name == primary_key || c.name =~ /(_id|_count)$/ || c.name == inheritance_column }
267
267
  end
268
268
 
269
269
  # Resets all the cached information about columns, which will cause them
@@ -295,28 +295,17 @@ module ActiveRecord
295
295
  def reset_column_information
296
296
  connection.clear_cache!
297
297
  undefine_attribute_methods
298
- connection.schema_cache.clear_table_cache!(table_name) if table_exists?
299
-
300
- @arel_engine = nil
301
- @column_defaults = nil
302
- @column_names = nil
303
- @columns = nil
304
- @columns_hash = nil
305
- @column_types = nil
306
- @content_columns = nil
307
- @dynamic_methods_hash = nil
308
- @inheritance_column = nil unless defined?(@explicit_inheritance_column) && @explicit_inheritance_column
309
- @relation = nil
310
- @serialized_column_names = nil
311
- @time_zone_column_names = nil
312
- @cached_time_zone = nil
313
- end
298
+ connection.schema_cache.clear_table_cache!(table_name)
299
+
300
+ @arel_engine = nil
301
+ @column_names = nil
302
+ @column_types = nil
303
+ @content_columns = nil
304
+ @default_attributes = nil
305
+ @inheritance_column = nil unless defined?(@explicit_inheritance_column) && @explicit_inheritance_column
306
+ @relation = nil
314
307
 
315
- # This is a hook for use by modules that need to do extra stuff to
316
- # attributes when they are initialized. (e.g. attribute
317
- # serialization)
318
- def initialize_attributes(attributes, options = {}) #:nodoc:
319
- attributes
308
+ initialize_find_by_cache
320
309
  end
321
310
 
322
311
  private
@@ -337,12 +326,17 @@ module ActiveRecord
337
326
  contained = contained.singularize if parent.pluralize_table_names
338
327
  contained += '_'
339
328
  end
340
- "#{full_table_name_prefix}#{contained}#{undecorated_table_name(name)}#{table_name_suffix}"
329
+
330
+ "#{full_table_name_prefix}#{contained}#{undecorated_table_name(name)}#{full_table_name_suffix}"
341
331
  else
342
332
  # STI subclasses always use their superclass' table.
343
333
  base.table_name
344
334
  end
345
335
  end
336
+
337
+ def raw_default_values
338
+ columns_hash.transform_values(&:default)
339
+ end
346
340
  end
347
341
  end
348
342
  end
@@ -485,10 +485,10 @@ module ActiveRecord
485
485
  end
486
486
 
487
487
  # Takes in a limit and checks if the attributes_collection has too many
488
- # records. The method will take limits in the form of symbols, procs, and
489
- # number-like objects (anything that can be compared with an integer).
488
+ # records. It accepts limit in the form of symbol, proc, or
489
+ # number-like object (anything that can be compared with an integer).
490
490
  #
491
- # Will raise an TooManyRecords error if the attributes_collection is
491
+ # Raises TooManyRecords error if the attributes_collection is
492
492
  # larger than the limit.
493
493
  def check_record_limit!(limit, attributes_collection)
494
494
  if limit
@@ -516,10 +516,10 @@ module ActiveRecord
516
516
 
517
517
  # Determines if a hash contains a truthy _destroy key.
518
518
  def has_destroy_flag?(hash)
519
- ConnectionAdapters::Column.value_to_boolean(hash['_destroy'])
519
+ Type::Boolean.new.type_cast_from_user(hash['_destroy'])
520
520
  end
521
521
 
522
- # Determines if a new record should be build by checking for
522
+ # Determines if a new record should be rejected by checking
523
523
  # has_destroy_flag? or if a <tt>:reject_if</tt> proc exists for this
524
524
  # association and evaluates to +true+.
525
525
  def reject_new_record?(association_name, attributes)
@@ -45,7 +45,7 @@ module ActiveRecord
45
45
  NoTouching.applied_to?(self.class)
46
46
  end
47
47
 
48
- def touch(*)
48
+ def touch(*) # :nodoc:
49
49
  super unless no_touching?
50
50
  end
51
51
  end
@@ -36,6 +36,23 @@ module ActiveRecord
36
36
  end
37
37
  end
38
38
 
39
+ # Creates an object (or multiple objects) and saves it to the database,
40
+ # if validations pass. Raises a RecordInvalid error if validations fail,
41
+ # unlike Base#create.
42
+ #
43
+ # The +attributes+ parameter can be either a Hash or an Array of Hashes.
44
+ # These describe which attributes to be created on the object, or
45
+ # multiple objects when given an Array of Hashes.
46
+ def create!(attributes = nil, &block)
47
+ if attributes.is_a?(Array)
48
+ attributes.collect { |attr| create!(attr, &block) }
49
+ else
50
+ object = new(attributes, &block)
51
+ object.save!
52
+ object
53
+ end
54
+ end
55
+
39
56
  # Given an attributes hash, +instantiate+ returns a new instance of
40
57
  # the appropriate class. Accepts only keys as strings.
41
58
  #
@@ -48,8 +65,8 @@ module ActiveRecord
48
65
  # how this "single-table" inheritance mapping is implemented.
49
66
  def instantiate(attributes, column_types = {})
50
67
  klass = discriminate_class_for_record(attributes)
51
- column_types = klass.decorate_columns(column_types.dup)
52
- klass.allocate.init_with('attributes' => attributes, 'column_types' => column_types)
68
+ attributes = klass.attributes_builder.build_from_database(attributes, column_types)
69
+ klass.allocate.init_with('attributes' => attributes, 'new_record' => false)
53
70
  end
54
71
 
55
72
  private
@@ -122,7 +139,7 @@ module ActiveRecord
122
139
  # Attributes marked as readonly are silently ignored if the record is
123
140
  # being updated.
124
141
  def save!(*)
125
- create_or_update || raise(RecordNotSaved)
142
+ create_or_update || raise(RecordNotSaved.new("Failed to save the record", self))
126
143
  end
127
144
 
128
145
  # Deletes the record in the database and freezes this instance to
@@ -149,8 +166,9 @@ module ActiveRecord
149
166
  # and <tt>destroy</tt> returns +false+. See
150
167
  # ActiveRecord::Callbacks for further details.
151
168
  def destroy
152
- raise ReadOnlyRecord if readonly?
169
+ raise ReadOnlyRecord, "#{self.class} is marked as readonly" if readonly?
153
170
  destroy_associations
171
+ self.class.connection.add_transaction_record(self)
154
172
  destroy_row if persisted?
155
173
  @destroyed = true
156
174
  freeze
@@ -164,7 +182,7 @@ module ActiveRecord
164
182
  # and <tt>destroy!</tt> raises ActiveRecord::RecordNotDestroyed. See
165
183
  # ActiveRecord::Callbacks for further details.
166
184
  def destroy!
167
- destroy || raise(ActiveRecord::RecordNotDestroyed)
185
+ destroy || raise(RecordNotDestroyed.new("Failed to destroy the record", self))
168
186
  end
169
187
 
170
188
  # Returns an instance of the specified +klass+ with the attributes of the
@@ -180,7 +198,6 @@ module ActiveRecord
180
198
  def becomes(klass)
181
199
  became = klass.new
182
200
  became.instance_variable_set("@attributes", @attributes)
183
- became.instance_variable_set("@attributes_cache", @attributes_cache)
184
201
  changed_attributes = @changed_attributes if defined?(@changed_attributes)
185
202
  became.instance_variable_set("@changed_attributes", changed_attributes || {})
186
203
  became.instance_variable_set("@new_record", new_record?)
@@ -215,6 +232,8 @@ module ActiveRecord
215
232
  #
216
233
  # This method raises an +ActiveRecord::ActiveRecordError+ if the
217
234
  # attribute is marked as readonly.
235
+ #
236
+ # See also +update_column+.
218
237
  def update_attribute(name, value)
219
238
  name = name.to_s
220
239
  verify_readonly_attribute(name)
@@ -270,7 +289,8 @@ module ActiveRecord
270
289
  # This method raises an +ActiveRecord::ActiveRecordError+ when called on new
271
290
  # objects, or when at least one of the attributes is marked as readonly.
272
291
  def update_columns(attributes)
273
- raise ActiveRecordError, "cannot update on a new record object" unless persisted?
292
+ raise ActiveRecordError, "cannot update a new record" if new_record?
293
+ raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
274
294
 
275
295
  attributes.each_key do |key|
276
296
  verify_readonly_attribute(key.to_s)
@@ -349,7 +369,7 @@ module ActiveRecord
349
369
  # # => #<Account id: 1, email: 'account@example.com'>
350
370
  #
351
371
  # Attributes are reloaded from the database, and caches busted, in
352
- # particular the associations cache.
372
+ # particular the associations cache and the QueryCache.
353
373
  #
354
374
  # If the record no longer exists in the database <tt>ActiveRecord::RecordNotFound</tt>
355
375
  # is raised. Otherwise, in addition to the in-place modification the method
@@ -387,6 +407,7 @@ module ActiveRecord
387
407
  def reload(options = nil)
388
408
  clear_aggregation_cache
389
409
  clear_association_cache
410
+ self.class.connection.clear_query_cache
390
411
 
391
412
  fresh_object =
392
413
  if options && options[:lock]
@@ -395,25 +416,24 @@ module ActiveRecord
395
416
  self.class.unscoped { self.class.find(id) }
396
417
  end
397
418
 
398
- @attributes.update(fresh_object.instance_variable_get('@attributes'))
399
-
400
- @column_types = self.class.column_types
401
- @column_types_override = fresh_object.instance_variable_get('@column_types_override')
402
- @attributes_cache = {}
403
- @new_record = false
419
+ @attributes = fresh_object.instance_variable_get('@attributes')
420
+ @new_record = false
404
421
  self
405
422
  end
406
423
 
407
424
  # Saves the record with the updated_at/on attributes set to the current time.
408
425
  # Please note that no validation is performed and only the +after_touch+,
409
426
  # +after_commit+ and +after_rollback+ callbacks are executed.
410
- # If an attribute name is passed, that attribute is updated along with
411
- # updated_at/on attributes.
412
427
  #
413
- # product.touch # updates updated_at/on
414
- # product.touch(:designed_at) # updates the designed_at attribute and updated_at/on
428
+ # If attribute names are passed, they are updated along with updated_at/on
429
+ # attributes.
430
+ #
431
+ # product.touch # updates updated_at/on
432
+ # product.touch(:designed_at) # updates the designed_at attribute and updated_at/on
433
+ # product.touch(:started_at, :ended_at) # updates started_at, ended_at and updated_at/on attributes
415
434
  #
416
- # If used along with +belongs_to+ then +touch+ will invoke +touch+ method on associated object.
435
+ # If used along with +belongs_to+ then +touch+ will invoke +touch+ method on
436
+ # associated object.
417
437
  #
418
438
  # class Brake < ActiveRecord::Base
419
439
  # belongs_to :car, touch: true
@@ -432,11 +452,11 @@ module ActiveRecord
432
452
  # ball = Ball.new
433
453
  # ball.touch(:updated_at) # => raises ActiveRecordError
434
454
  #
435
- def touch(name = nil)
455
+ def touch(*names)
436
456
  raise ActiveRecordError, "cannot touch on a new record object" unless persisted?
437
457
 
438
458
  attributes = timestamp_attributes_for_update_in_model
439
- attributes << name if name
459
+ attributes.concat(names)
440
460
 
441
461
  unless attributes.empty?
442
462
  current_time = current_time_from_proper_timezone
@@ -449,7 +469,7 @@ module ActiveRecord
449
469
 
450
470
  changes[self.class.locking_column] = increment_lock if locking_enabled?
451
471
 
452
- changed_attributes.except!(*changes.keys)
472
+ clear_attribute_changes(changes.keys)
453
473
  primary_key = self.class.primary_key
454
474
  self.class.unscoped.where(primary_key => self[primary_key]).update_all(changes) == 1
455
475
  else
@@ -470,7 +490,7 @@ module ActiveRecord
470
490
  def relation_for_destroy
471
491
  pk = self.class.primary_key
472
492
  column = self.class.columns_hash[pk]
473
- substitute = self.class.connection.substitute_at(column, 0)
493
+ substitute = self.class.connection.substitute_at(column)
474
494
 
475
495
  relation = self.class.unscoped.where(
476
496
  self.class.arel_table[pk].eq(substitute))
@@ -480,14 +500,14 @@ module ActiveRecord
480
500
  end
481
501
 
482
502
  def create_or_update
483
- raise ReadOnlyRecord if readonly?
503
+ raise ReadOnlyRecord, "#{self.class} is marked as readonly" if readonly?
484
504
  result = new_record? ? _create_record : _update_record
485
505
  result != false
486
506
  end
487
507
 
488
508
  # Updates the associated record with values matching those of the instance attributes.
489
509
  # Returns the number of affected rows.
490
- def _update_record(attribute_names = @attributes.keys)
510
+ def _update_record(attribute_names = self.attribute_names)
491
511
  attributes_values = arel_attributes_with_values_for_update(attribute_names)
492
512
  if attributes_values.empty?
493
513
  0
@@ -498,7 +518,7 @@ module ActiveRecord
498
518
 
499
519
  # Creates a record with values matching those of the instance attributes
500
520
  # and returns its id.
501
- def _create_record(attribute_names = @attributes.keys)
521
+ def _create_record(attribute_names = self.attribute_names)
502
522
  attributes_values = arel_attributes_with_values_for_create(attribute_names)
503
523
 
504
524
  new_id = self.class.unscoped.insert attributes_values