activerecord 4.2.11.1 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


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

Files changed (246) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1282 -1195
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +7 -8
  5. data/examples/performance.rb +2 -3
  6. data/examples/simple.rb +0 -1
  7. data/lib/active_record.rb +8 -4
  8. data/lib/active_record/aggregations.rb +35 -24
  9. data/lib/active_record/association_relation.rb +3 -3
  10. data/lib/active_record/associations.rb +317 -209
  11. data/lib/active_record/associations/alias_tracker.rb +19 -16
  12. data/lib/active_record/associations/association.rb +11 -9
  13. data/lib/active_record/associations/association_scope.rb +73 -102
  14. data/lib/active_record/associations/belongs_to_association.rb +21 -32
  15. data/lib/active_record/associations/builder/association.rb +28 -34
  16. data/lib/active_record/associations/builder/belongs_to.rb +43 -18
  17. data/lib/active_record/associations/builder/collection_association.rb +7 -19
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +14 -11
  19. data/lib/active_record/associations/builder/has_many.rb +4 -4
  20. data/lib/active_record/associations/builder/has_one.rb +11 -6
  21. data/lib/active_record/associations/builder/singular_association.rb +3 -10
  22. data/lib/active_record/associations/collection_association.rb +49 -41
  23. data/lib/active_record/associations/collection_proxy.rb +67 -27
  24. data/lib/active_record/associations/foreign_association.rb +1 -1
  25. data/lib/active_record/associations/has_many_association.rb +20 -71
  26. data/lib/active_record/associations/has_many_through_association.rb +8 -47
  27. data/lib/active_record/associations/has_one_association.rb +12 -5
  28. data/lib/active_record/associations/join_dependency.rb +29 -19
  29. data/lib/active_record/associations/join_dependency/join_association.rb +16 -10
  30. data/lib/active_record/associations/preloader.rb +14 -4
  31. data/lib/active_record/associations/preloader/association.rb +46 -52
  32. data/lib/active_record/associations/preloader/collection_association.rb +0 -6
  33. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  34. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  35. data/lib/active_record/associations/preloader/through_association.rb +27 -14
  36. data/lib/active_record/associations/singular_association.rb +7 -1
  37. data/lib/active_record/associations/through_association.rb +11 -3
  38. data/lib/active_record/attribute.rb +68 -18
  39. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  40. data/lib/active_record/attribute_assignment.rb +19 -140
  41. data/lib/active_record/attribute_decorators.rb +6 -5
  42. data/lib/active_record/attribute_methods.rb +76 -47
  43. data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
  44. data/lib/active_record/attribute_methods/dirty.rb +46 -86
  45. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  46. data/lib/active_record/attribute_methods/query.rb +2 -2
  47. data/lib/active_record/attribute_methods/read.rb +31 -59
  48. data/lib/active_record/attribute_methods/serialization.rb +13 -16
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -14
  50. data/lib/active_record/attribute_methods/write.rb +13 -37
  51. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  52. data/lib/active_record/attribute_set.rb +30 -3
  53. data/lib/active_record/attribute_set/builder.rb +6 -4
  54. data/lib/active_record/attributes.rb +199 -81
  55. data/lib/active_record/autosave_association.rb +49 -16
  56. data/lib/active_record/base.rb +32 -23
  57. data/lib/active_record/callbacks.rb +39 -43
  58. data/lib/active_record/coders/json.rb +1 -1
  59. data/lib/active_record/coders/yaml_column.rb +20 -8
  60. data/lib/active_record/collection_cache_key.rb +40 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +452 -182
  62. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  63. data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -61
  64. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -2
  65. data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -10
  66. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  67. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
  68. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -185
  69. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
  70. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +380 -141
  71. data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
  72. data/lib/active_record/connection_adapters/abstract_adapter.rb +141 -59
  73. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +401 -370
  74. data/lib/active_record/connection_adapters/column.rb +28 -43
  75. data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
  76. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  77. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  78. data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
  79. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  80. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  81. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  82. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  83. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  84. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  85. data/lib/active_record/connection_adapters/mysql2_adapter.rb +29 -166
  86. data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
  87. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +10 -72
  88. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  90. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -57
  91. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  92. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
  94. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
  95. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
  96. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  100. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
  103. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  104. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  106. data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
  107. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  108. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  109. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +234 -148
  111. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  112. data/lib/active_record/connection_adapters/postgresql_adapter.rb +248 -160
  113. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  114. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  115. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  116. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  117. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +149 -192
  119. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  120. data/lib/active_record/connection_handling.rb +37 -14
  121. data/lib/active_record/core.rb +89 -107
  122. data/lib/active_record/counter_cache.rb +13 -24
  123. data/lib/active_record/dynamic_matchers.rb +1 -20
  124. data/lib/active_record/enum.rb +113 -76
  125. data/lib/active_record/errors.rb +87 -48
  126. data/lib/active_record/explain_registry.rb +1 -1
  127. data/lib/active_record/explain_subscriber.rb +1 -1
  128. data/lib/active_record/fixture_set/file.rb +26 -5
  129. data/lib/active_record/fixtures.rb +76 -40
  130. data/lib/active_record/gem_version.rb +4 -4
  131. data/lib/active_record/inheritance.rb +32 -40
  132. data/lib/active_record/integration.rb +4 -4
  133. data/lib/active_record/internal_metadata.rb +56 -0
  134. data/lib/active_record/legacy_yaml_adapter.rb +18 -2
  135. data/lib/active_record/locale/en.yml +3 -2
  136. data/lib/active_record/locking/optimistic.rb +15 -15
  137. data/lib/active_record/locking/pessimistic.rb +1 -1
  138. data/lib/active_record/log_subscriber.rb +43 -21
  139. data/lib/active_record/migration.rb +363 -133
  140. data/lib/active_record/migration/command_recorder.rb +59 -18
  141. data/lib/active_record/migration/compatibility.rb +126 -0
  142. data/lib/active_record/model_schema.rb +129 -41
  143. data/lib/active_record/nested_attributes.rb +58 -29
  144. data/lib/active_record/null_relation.rb +16 -8
  145. data/lib/active_record/persistence.rb +121 -80
  146. data/lib/active_record/query_cache.rb +15 -18
  147. data/lib/active_record/querying.rb +10 -9
  148. data/lib/active_record/railtie.rb +23 -16
  149. data/lib/active_record/railties/controller_runtime.rb +1 -1
  150. data/lib/active_record/railties/databases.rake +69 -46
  151. data/lib/active_record/readonly_attributes.rb +1 -1
  152. data/lib/active_record/reflection.rb +282 -115
  153. data/lib/active_record/relation.rb +176 -116
  154. data/lib/active_record/relation/batches.rb +139 -34
  155. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  156. data/lib/active_record/relation/calculations.rb +79 -108
  157. data/lib/active_record/relation/delegation.rb +7 -20
  158. data/lib/active_record/relation/finder_methods.rb +163 -81
  159. data/lib/active_record/relation/from_clause.rb +32 -0
  160. data/lib/active_record/relation/merger.rb +16 -42
  161. data/lib/active_record/relation/predicate_builder.rb +120 -107
  162. data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
  163. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  164. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  165. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  166. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  167. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  168. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  169. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  170. data/lib/active_record/relation/query_attribute.rb +19 -0
  171. data/lib/active_record/relation/query_methods.rb +308 -244
  172. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  173. data/lib/active_record/relation/spawn_methods.rb +4 -7
  174. data/lib/active_record/relation/where_clause.rb +174 -0
  175. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  176. data/lib/active_record/result.rb +4 -3
  177. data/lib/active_record/runtime_registry.rb +1 -1
  178. data/lib/active_record/sanitization.rb +95 -66
  179. data/lib/active_record/schema.rb +26 -22
  180. data/lib/active_record/schema_dumper.rb +62 -38
  181. data/lib/active_record/schema_migration.rb +11 -14
  182. data/lib/active_record/scoping.rb +32 -15
  183. data/lib/active_record/scoping/default.rb +23 -9
  184. data/lib/active_record/scoping/named.rb +49 -28
  185. data/lib/active_record/secure_token.rb +38 -0
  186. data/lib/active_record/serialization.rb +2 -4
  187. data/lib/active_record/statement_cache.rb +16 -14
  188. data/lib/active_record/store.rb +8 -3
  189. data/lib/active_record/suppressor.rb +58 -0
  190. data/lib/active_record/table_metadata.rb +68 -0
  191. data/lib/active_record/tasks/database_tasks.rb +57 -43
  192. data/lib/active_record/tasks/mysql_database_tasks.rb +6 -14
  193. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -2
  194. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  195. data/lib/active_record/timestamp.rb +20 -9
  196. data/lib/active_record/touch_later.rb +58 -0
  197. data/lib/active_record/transactions.rb +138 -56
  198. data/lib/active_record/type.rb +66 -17
  199. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  200. data/lib/active_record/type/date.rb +2 -45
  201. data/lib/active_record/type/date_time.rb +2 -49
  202. data/lib/active_record/type/internal/abstract_json.rb +29 -0
  203. data/lib/active_record/type/internal/timezone.rb +15 -0
  204. data/lib/active_record/type/serialized.rb +15 -14
  205. data/lib/active_record/type/time.rb +10 -16
  206. data/lib/active_record/type/type_map.rb +4 -4
  207. data/lib/active_record/type_caster.rb +7 -0
  208. data/lib/active_record/type_caster/connection.rb +29 -0
  209. data/lib/active_record/type_caster/map.rb +19 -0
  210. data/lib/active_record/validations.rb +33 -32
  211. data/lib/active_record/validations/absence.rb +23 -0
  212. data/lib/active_record/validations/associated.rb +10 -3
  213. data/lib/active_record/validations/length.rb +24 -0
  214. data/lib/active_record/validations/presence.rb +11 -12
  215. data/lib/active_record/validations/uniqueness.rb +30 -29
  216. data/lib/rails/generators/active_record/migration.rb +7 -0
  217. data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
  218. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
  219. data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -1
  220. data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
  221. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  222. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  223. metadata +59 -34
  224. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  225. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  226. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  227. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  228. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  229. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  230. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  231. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  232. data/lib/active_record/type/big_integer.rb +0 -13
  233. data/lib/active_record/type/binary.rb +0 -50
  234. data/lib/active_record/type/boolean.rb +0 -31
  235. data/lib/active_record/type/decimal.rb +0 -64
  236. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  237. data/lib/active_record/type/decorator.rb +0 -14
  238. data/lib/active_record/type/float.rb +0 -19
  239. data/lib/active_record/type/integer.rb +0 -59
  240. data/lib/active_record/type/mutable.rb +0 -16
  241. data/lib/active_record/type/numeric.rb +0 -36
  242. data/lib/active_record/type/string.rb +0 -40
  243. data/lib/active_record/type/text.rb +0 -11
  244. data/lib/active_record/type/time_value.rb +0 -38
  245. data/lib/active_record/type/unsigned_integer.rb +0 -15
  246. data/lib/active_record/type/value.rb +0 -110
@@ -15,7 +15,7 @@ module ActiveRecord
15
15
  end
16
16
 
17
17
  def decorate_matching_attribute_types(matcher, decorator_name, &block)
18
- clear_caches_calculated_from_columns
18
+ reload_schema_from_cache
19
19
  decorator_name = decorator_name.to_s
20
20
 
21
21
  # Create new hashes so we don't modify parent classes
@@ -24,10 +24,11 @@ module ActiveRecord
24
24
 
25
25
  private
26
26
 
27
- def add_user_provided_columns(*)
28
- super.map do |column|
29
- decorated_type = attribute_type_decorations.apply(column.name, column.cast_type)
30
- column.with_type(decorated_type)
27
+ def load_schema!
28
+ super
29
+ attribute_types.each do |name, type|
30
+ decorated_type = attribute_type_decorations.apply(name, type)
31
+ define_attribute(name, decorated_type)
31
32
  end
32
33
  end
33
34
  end
@@ -1,7 +1,7 @@
1
1
  require 'active_support/core_ext/enumerable'
2
2
  require 'active_support/core_ext/string/filters'
3
3
  require 'mutex_m'
4
- require 'thread_safe'
4
+ require 'concurrent/map'
5
5
 
6
6
  module ActiveRecord
7
7
  # = Active Record Attribute Methods
@@ -34,30 +34,6 @@ module ActiveRecord
34
34
 
35
35
  BLACKLISTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
36
36
 
37
- class AttributeMethodCache
38
- def initialize
39
- @module = Module.new
40
- @method_cache = ThreadSafe::Cache.new
41
- end
42
-
43
- def [](name)
44
- @method_cache.compute_if_absent(name) do
45
- safe_name = name.unpack('h*').first
46
- temp_method = "__temp__#{safe_name}"
47
- ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
48
- @module.module_eval method_body(temp_method, safe_name), __FILE__, __LINE__
49
- @module.instance_method temp_method
50
- end
51
- end
52
-
53
- private
54
-
55
- # Override this method in the subclasses for method body.
56
- def method_body(method_name, const_name)
57
- raise NotImplementedError, "Subclasses must implement a method_body(method_name, const_name) method."
58
- end
59
- end
60
-
61
37
  class GeneratedAttributeMethods < Module; end # :nodoc:
62
38
 
63
39
  module ClassMethods
@@ -83,7 +59,7 @@ module ActiveRecord
83
59
  generated_attribute_methods.synchronize do
84
60
  return false if @attribute_methods_generated
85
61
  superclass.define_attribute_methods unless self == base_class
86
- super(column_names)
62
+ super(attribute_names)
87
63
  @attribute_methods_generated = true
88
64
  end
89
65
  true
@@ -96,7 +72,7 @@ module ActiveRecord
96
72
  end
97
73
  end
98
74
 
99
- # Raises a <tt>ActiveRecord::DangerousAttributeError</tt> exception when an
75
+ # Raises an ActiveRecord::DangerousAttributeError exception when an
100
76
  # \Active \Record method is defined in the model, otherwise +false+.
101
77
  #
102
78
  # class Person < ActiveRecord::Base
@@ -106,7 +82,7 @@ module ActiveRecord
106
82
  # end
107
83
  #
108
84
  # Person.instance_method_already_implemented?(:save)
109
- # # => ActiveRecord::DangerousAttributeError: save is defined by ActiveRecord
85
+ # # => ActiveRecord::DangerousAttributeError: save is defined by Active Record. Check to make sure that you don't have an attribute or method with the same name.
110
86
  #
111
87
  # Person.instance_method_already_implemented?(:name)
112
88
  # # => false
@@ -150,7 +126,7 @@ module ActiveRecord
150
126
  BLACKLISTED_CLASS_METHODS.include?(method_name.to_s) || class_method_defined_within?(method_name, Base)
151
127
  end
152
128
 
153
- def class_method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc
129
+ def class_method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc:
154
130
  if klass.respond_to?(name, true)
155
131
  if superklass.respond_to?(name, true)
156
132
  klass.method(name).owner != superklass.method(name).owner
@@ -185,14 +161,27 @@ module ActiveRecord
185
161
  # # => ["id", "created_at", "updated_at", "name", "age"]
186
162
  def attribute_names
187
163
  @attribute_names ||= if !abstract_class? && table_exists?
188
- column_names
164
+ attribute_types.keys
189
165
  else
190
166
  []
191
167
  end
192
168
  end
193
169
 
170
+ # Returns true if the given attribute exists, otherwise false.
171
+ #
172
+ # class Person < ActiveRecord::Base
173
+ # end
174
+ #
175
+ # Person.has_attribute?('name') # => true
176
+ # Person.has_attribute?(:age) # => true
177
+ # Person.has_attribute?(:nothing) # => false
178
+ def has_attribute?(attr_name)
179
+ attribute_types.key?(attr_name.to_s)
180
+ end
181
+
194
182
  # Returns the column object for the named attribute.
195
- # Returns nil if the named attribute does not exist.
183
+ # Returns a +ActiveRecord::ConnectionAdapters::NullColumn+ if the
184
+ # named attribute does not exist.
196
185
  #
197
186
  # class Person < ActiveRecord::Base
198
187
  # end
@@ -202,23 +191,18 @@ module ActiveRecord
202
191
  # # => #<ActiveRecord::ConnectionAdapters::Column:0x007ff4ab083980 @name="name", @sql_type="varchar(255)", @null=true, ...>
203
192
  #
204
193
  # person.column_for_attribute(:nothing)
205
- # # => nil
194
+ # # => #<ActiveRecord::ConnectionAdapters::NullColumn:0xXXX @name=nil, @sql_type=nil, @cast_type=#<Type::Value>, ...>
206
195
  def column_for_attribute(name)
207
- column = columns_hash[name.to_s]
208
- if column.nil?
209
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
210
- `#column_for_attribute` will return a null object for non-existent
211
- columns in Rails 5. Use `#has_attribute?` if you need to check for
212
- an attribute's existence.
213
- MSG
196
+ name = name.to_s
197
+ columns_hash.fetch(name) do
198
+ ConnectionAdapters::NullColumn.new(name)
214
199
  end
215
- column
216
200
  end
217
201
  end
218
202
 
219
203
  # A Person object with a name attribute can ask <tt>person.respond_to?(:name)</tt>,
220
204
  # <tt>person.respond_to?(:name=)</tt>, and <tt>person.respond_to?(:name?)</tt>
221
- # which will all return +true+. It also define the attribute methods if they have
205
+ # which will all return +true+. It also defines the attribute methods if they have
222
206
  # not been generated.
223
207
  #
224
208
  # class Person < ActiveRecord::Base
@@ -234,7 +218,15 @@ module ActiveRecord
234
218
  # person.respond_to(:nothing) # => false
235
219
  def respond_to?(name, include_private = false)
236
220
  return false unless super
237
- name = name.to_s
221
+
222
+ case name
223
+ when :to_partial_path
224
+ name = "to_partial_path".freeze
225
+ when :to_model
226
+ name = "to_model".freeze
227
+ else
228
+ name = name.to_s
229
+ end
238
230
 
239
231
  # If the result is true then check for the select case.
240
232
  # For queries selecting a subset of columns, return false for unselected columns.
@@ -287,8 +279,9 @@ module ActiveRecord
287
279
  # Returns an <tt>#inspect</tt>-like string for the value of the
288
280
  # attribute +attr_name+. String attributes are truncated up to 50
289
281
  # characters, Date and Time attributes are returned in the
290
- # <tt>:db</tt> format. Other attributes return the value of
291
- # <tt>#inspect</tt> without modification.
282
+ # <tt>:db</tt> format, Array attributes are truncated up to 10 values.
283
+ # Other attributes return the value of <tt>#inspect</tt> without
284
+ # modification.
292
285
  #
293
286
  # person = Person.create!(name: 'David Heinemeier Hansson ' * 3)
294
287
  #
@@ -299,7 +292,7 @@ module ActiveRecord
299
292
  # # => "\"2012-10-22 00:15:07\""
300
293
  #
301
294
  # person.attribute_for_inspect(:tag_ids)
302
- # # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]"
295
+ # # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...]"
303
296
  def attribute_for_inspect(attr_name)
304
297
  value = read_attribute(attr_name)
305
298
 
@@ -307,6 +300,9 @@ module ActiveRecord
307
300
  "#{value[0, 50]}...".inspect
308
301
  elsif value.is_a?(Date) || value.is_a?(Time)
309
302
  %("#{value.to_s(:db)}")
303
+ elsif value.is_a?(Array) && value.size > 10
304
+ inspected = value.first(10).inspect
305
+ %(#{inspected[0...-1]}, ...])
310
306
  else
311
307
  value.inspect
312
308
  end
@@ -338,7 +334,7 @@ module ActiveRecord
338
334
  #
339
335
  # Note: +:id+ is always present.
340
336
  #
341
- # Alias for the <tt>read_attribute</tt> method.
337
+ # Alias for the #read_attribute method.
342
338
  #
343
339
  # class Person < ActiveRecord::Base
344
340
  # belongs_to :organization
@@ -356,7 +352,7 @@ module ActiveRecord
356
352
  end
357
353
 
358
354
  # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
359
- # (Alias for the protected <tt>write_attribute</tt> method).
355
+ # (Alias for the protected #write_attribute method).
360
356
  #
361
357
  # class Person < ActiveRecord::Base
362
358
  # end
@@ -369,6 +365,39 @@ module ActiveRecord
369
365
  write_attribute(attr_name, value)
370
366
  end
371
367
 
368
+ # Returns the name of all database fields which have been read from this
369
+ # model. This can be useful in development mode to determine which fields
370
+ # need to be selected. For performance critical pages, selecting only the
371
+ # required fields can be an easy performance win (assuming you aren't using
372
+ # all of the fields on the model).
373
+ #
374
+ # For example:
375
+ #
376
+ # class PostsController < ActionController::Base
377
+ # after_action :print_accessed_fields, only: :index
378
+ #
379
+ # def index
380
+ # @posts = Post.all
381
+ # end
382
+ #
383
+ # private
384
+ #
385
+ # def print_accessed_fields
386
+ # p @posts.first.accessed_fields
387
+ # end
388
+ # end
389
+ #
390
+ # Which allows you to quickly change your code to:
391
+ #
392
+ # class PostsController < ActionController::Base
393
+ # def index
394
+ # @posts = Post.select(:id, :title, :author_id, :updated_at)
395
+ # end
396
+ # end
397
+ def accessed_fields
398
+ @attributes.accessed
399
+ end
400
+
372
401
  protected
373
402
 
374
403
  def clone_attribute_value(reader_method, attribute_name) # :nodoc:
@@ -2,7 +2,7 @@ module ActiveRecord
2
2
  module AttributeMethods
3
3
  # = Active Record Attribute Methods Before Type Cast
4
4
  #
5
- # <tt>ActiveRecord::AttributeMethods::BeforeTypeCast</tt> provides a way to
5
+ # ActiveRecord::AttributeMethods::BeforeTypeCast provides a way to
6
6
  # read the value of the attributes before typecasting and deserialization.
7
7
  #
8
8
  # class Task < ActiveRecord::Base
@@ -1,4 +1,5 @@
1
1
  require 'active_support/core_ext/module/attribute_accessors'
2
+ require 'active_record/attribute_mutation_tracker'
2
3
 
3
4
  module ActiveRecord
4
5
  module AttributeMethods
@@ -34,24 +35,43 @@ module ActiveRecord
34
35
  # <tt>reload</tt> the record and clears changed attributes.
35
36
  def reload(*)
36
37
  super.tap do
37
- clear_changes_information
38
+ @mutation_tracker = nil
39
+ @previous_mutation_tracker = nil
40
+ @changed_attributes = HashWithIndifferentAccess.new
38
41
  end
39
42
  end
40
43
 
41
44
  def initialize_dup(other) # :nodoc:
42
45
  super
43
- @original_raw_attributes = nil
44
- calculate_changes_from_defaults
46
+ @attributes = self.class._default_attributes.map do |attr|
47
+ attr.with_value_from_user(@attributes.fetch_value(attr.name))
48
+ end
49
+ @mutation_tracker = nil
45
50
  end
46
51
 
47
52
  def changes_applied
48
- super
49
- store_original_raw_attributes
53
+ @previous_mutation_tracker = mutation_tracker
54
+ @changed_attributes = HashWithIndifferentAccess.new
55
+ store_original_attributes
50
56
  end
51
57
 
52
58
  def clear_changes_information
59
+ @previous_mutation_tracker = nil
60
+ @changed_attributes = HashWithIndifferentAccess.new
61
+ store_original_attributes
62
+ end
63
+
64
+ def raw_write_attribute(attr_name, *)
65
+ result = super
66
+ clear_attribute_change(attr_name)
67
+ result
68
+ end
69
+
70
+ def clear_attribute_changes(attr_names)
53
71
  super
54
- original_raw_attributes.clear
72
+ attr_names.each do |attr_name|
73
+ clear_attribute_change(attr_name)
74
+ end
55
75
  end
56
76
 
57
77
  def changed_attributes
@@ -60,7 +80,7 @@ module ActiveRecord
60
80
  if defined?(@cached_changed_attributes)
61
81
  @cached_changed_attributes
62
82
  else
63
- super.reverse_merge(attributes_changed_in_place).freeze
83
+ super.reverse_merge(mutation_tracker.changed_values).freeze
64
84
  end
65
85
  end
66
86
 
@@ -70,59 +90,29 @@ module ActiveRecord
70
90
  end
71
91
  end
72
92
 
93
+ def previous_changes
94
+ previous_mutation_tracker.changes
95
+ end
96
+
73
97
  def attribute_changed_in_place?(attr_name)
74
- old_value = original_raw_attribute(attr_name)
75
- @attributes[attr_name].changed_in_place_from?(old_value)
98
+ mutation_tracker.changed_in_place?(attr_name)
76
99
  end
77
100
 
78
101
  private
79
102
 
80
- def changes_include?(attr_name)
81
- super || attribute_changed_in_place?(attr_name)
82
- end
83
-
84
- def calculate_changes_from_defaults
85
- @changed_attributes = nil
86
- self.class.column_defaults.each do |attr, orig_value|
87
- set_attribute_was(attr, orig_value) if _field_changed?(attr, orig_value)
103
+ def mutation_tracker
104
+ unless defined?(@mutation_tracker)
105
+ @mutation_tracker = nil
88
106
  end
107
+ @mutation_tracker ||= AttributeMutationTracker.new(@attributes)
89
108
  end
90
109
 
91
- # Wrap write_attribute to remember original attribute value.
92
- def write_attribute(attr, value)
93
- attr = attr.to_s
94
-
95
- old_value = old_attribute_value(attr)
96
-
97
- result = super
98
- store_original_raw_attribute(attr)
99
- save_changed_attribute(attr, old_value)
100
- result
101
- end
102
-
103
- def raw_write_attribute(attr, value)
104
- attr = attr.to_s
105
-
106
- result = super
107
- original_raw_attributes[attr] = value
108
- result
110
+ def changes_include?(attr_name)
111
+ super || mutation_tracker.changed?(attr_name)
109
112
  end
110
113
 
111
- def save_changed_attribute(attr, old_value)
112
- clear_changed_attributes_cache
113
- if attribute_changed_by_setter?(attr)
114
- clear_attribute_changes(attr) unless _field_changed?(attr, old_value)
115
- else
116
- set_attribute_was(attr, old_value) if _field_changed?(attr, old_value)
117
- end
118
- end
119
-
120
- def old_attribute_value(attr)
121
- if attribute_changed?(attr)
122
- changed_attributes[attr]
123
- else
124
- clone_attribute_value(:_read_attribute, attr)
125
- end
114
+ def clear_attribute_change(attr_name)
115
+ mutation_tracker.forget_change(attr_name)
126
116
  end
127
117
 
128
118
  def _update_record(*)
@@ -133,47 +123,17 @@ module ActiveRecord
133
123
  partial_writes? ? super(keys_for_partial_write) : super
134
124
  end
135
125
 
136
- # Serialized attributes should always be written in case they've been
137
- # changed in place.
138
126
  def keys_for_partial_write
139
- changed & persistable_attribute_names
140
- end
141
-
142
- def _field_changed?(attr, old_value)
143
- @attributes[attr].changed_from?(old_value)
144
- end
145
-
146
- def attributes_changed_in_place
147
- changed_in_place.each_with_object({}) do |attr_name, h|
148
- orig = @attributes[attr_name].original_value
149
- h[attr_name] = orig
150
- end
151
- end
152
-
153
- def changed_in_place
154
- self.class.attribute_names.select do |attr_name|
155
- attribute_changed_in_place?(attr_name)
156
- end
127
+ changed & self.class.column_names
157
128
  end
158
129
 
159
- def original_raw_attribute(attr_name)
160
- original_raw_attributes.fetch(attr_name) do
161
- read_attribute_before_type_cast(attr_name)
162
- end
130
+ def store_original_attributes
131
+ @attributes = @attributes.map(&:forgetting_assignment)
132
+ @mutation_tracker = nil
163
133
  end
164
134
 
165
- def original_raw_attributes
166
- @original_raw_attributes ||= {}
167
- end
168
-
169
- def store_original_raw_attribute(attr_name)
170
- original_raw_attributes[attr_name] = @attributes[attr_name].value_for_database rescue nil
171
- end
172
-
173
- def store_original_raw_attributes
174
- attribute_names.each do |attr|
175
- store_original_raw_attribute(attr)
176
- end
135
+ def previous_mutation_tracker
136
+ @previous_mutation_tracker ||= NullMutationTracker.instance
177
137
  end
178
138
 
179
139
  def cache_changed_attributes