activerecord 4.2.11.3 → 5.0.7.2

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 (251) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1638 -1132
  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 +7 -2
  8. data/lib/active_record/aggregations.rb +34 -21
  9. data/lib/active_record/association_relation.rb +7 -4
  10. data/lib/active_record/associations.rb +347 -218
  11. data/lib/active_record/associations/alias_tracker.rb +19 -16
  12. data/lib/active_record/associations/association.rb +22 -10
  13. data/lib/active_record/associations/association_scope.rb +75 -104
  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 +16 -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 +13 -11
  22. data/lib/active_record/associations/collection_association.rb +85 -69
  23. data/lib/active_record/associations/collection_proxy.rb +104 -46
  24. data/lib/active_record/associations/foreign_association.rb +1 -1
  25. data/lib/active_record/associations/has_many_association.rb +21 -78
  26. data/lib/active_record/associations/has_many_through_association.rb +6 -47
  27. data/lib/active_record/associations/has_one_association.rb +12 -5
  28. data/lib/active_record/associations/join_dependency.rb +38 -22
  29. data/lib/active_record/associations/join_dependency/join_association.rb +15 -14
  30. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  31. data/lib/active_record/associations/preloader.rb +14 -4
  32. data/lib/active_record/associations/preloader/association.rb +52 -71
  33. data/lib/active_record/associations/preloader/collection_association.rb +0 -7
  34. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  35. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  36. data/lib/active_record/associations/preloader/singular_association.rb +0 -1
  37. data/lib/active_record/associations/preloader/through_association.rb +36 -17
  38. data/lib/active_record/associations/singular_association.rb +13 -1
  39. data/lib/active_record/associations/through_association.rb +12 -4
  40. data/lib/active_record/attribute.rb +69 -19
  41. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  42. data/lib/active_record/attribute_assignment.rb +19 -140
  43. data/lib/active_record/attribute_decorators.rb +6 -5
  44. data/lib/active_record/attribute_methods.rb +69 -44
  45. data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
  46. data/lib/active_record/attribute_methods/dirty.rb +46 -86
  47. data/lib/active_record/attribute_methods/primary_key.rb +16 -3
  48. data/lib/active_record/attribute_methods/query.rb +2 -2
  49. data/lib/active_record/attribute_methods/read.rb +31 -59
  50. data/lib/active_record/attribute_methods/serialization.rb +13 -16
  51. data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -14
  52. data/lib/active_record/attribute_methods/write.rb +13 -37
  53. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  54. data/lib/active_record/attribute_set.rb +32 -3
  55. data/lib/active_record/attribute_set/builder.rb +42 -16
  56. data/lib/active_record/attributes.rb +199 -81
  57. data/lib/active_record/autosave_association.rb +54 -17
  58. data/lib/active_record/base.rb +32 -23
  59. data/lib/active_record/callbacks.rb +39 -43
  60. data/lib/active_record/coders/json.rb +1 -1
  61. data/lib/active_record/coders/yaml_column.rb +20 -8
  62. data/lib/active_record/collection_cache_key.rb +50 -0
  63. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +467 -189
  64. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  65. data/lib/active_record/connection_adapters/abstract/database_statements.rb +66 -62
  66. data/lib/active_record/connection_adapters/abstract/query_cache.rb +39 -4
  67. data/lib/active_record/connection_adapters/abstract/quoting.rb +86 -13
  68. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  69. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
  70. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -188
  71. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
  72. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +407 -156
  73. data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
  74. data/lib/active_record/connection_adapters/abstract_adapter.rb +177 -71
  75. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +433 -399
  76. data/lib/active_record/connection_adapters/column.rb +28 -43
  77. data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
  78. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  79. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  80. data/lib/active_record/connection_adapters/mysql/database_statements.rb +108 -0
  81. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  82. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  83. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  84. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  85. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  86. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  87. data/lib/active_record/connection_adapters/mysql2_adapter.rb +25 -166
  88. data/lib/active_record/connection_adapters/postgresql/column.rb +33 -11
  89. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +18 -72
  90. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  92. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +37 -57
  93. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +3 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -2
  95. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
  96. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
  97. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +13 -3
  98. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  99. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  100. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
  101. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  102. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
  104. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
  105. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  106. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +56 -19
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  111. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +250 -154
  113. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  114. data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -2
  115. data/lib/active_record/connection_adapters/postgresql_adapter.rb +264 -170
  116. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  117. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  118. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  119. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  120. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  121. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +151 -194
  122. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  123. data/lib/active_record/connection_handling.rb +37 -14
  124. data/lib/active_record/core.rb +92 -108
  125. data/lib/active_record/counter_cache.rb +13 -24
  126. data/lib/active_record/dynamic_matchers.rb +1 -20
  127. data/lib/active_record/enum.rb +116 -76
  128. data/lib/active_record/errors.rb +87 -48
  129. data/lib/active_record/explain.rb +20 -9
  130. data/lib/active_record/explain_registry.rb +1 -1
  131. data/lib/active_record/explain_subscriber.rb +1 -1
  132. data/lib/active_record/fixture_set/file.rb +26 -5
  133. data/lib/active_record/fixtures.rb +77 -41
  134. data/lib/active_record/gem_version.rb +4 -4
  135. data/lib/active_record/inheritance.rb +32 -40
  136. data/lib/active_record/integration.rb +17 -14
  137. data/lib/active_record/internal_metadata.rb +56 -0
  138. data/lib/active_record/legacy_yaml_adapter.rb +18 -2
  139. data/lib/active_record/locale/en.yml +3 -2
  140. data/lib/active_record/locking/optimistic.rb +15 -15
  141. data/lib/active_record/locking/pessimistic.rb +1 -1
  142. data/lib/active_record/log_subscriber.rb +48 -24
  143. data/lib/active_record/migration.rb +362 -111
  144. data/lib/active_record/migration/command_recorder.rb +59 -18
  145. data/lib/active_record/migration/compatibility.rb +126 -0
  146. data/lib/active_record/model_schema.rb +270 -73
  147. data/lib/active_record/nested_attributes.rb +58 -29
  148. data/lib/active_record/no_touching.rb +4 -0
  149. data/lib/active_record/null_relation.rb +16 -8
  150. data/lib/active_record/persistence.rb +152 -90
  151. data/lib/active_record/query_cache.rb +18 -23
  152. data/lib/active_record/querying.rb +12 -11
  153. data/lib/active_record/railtie.rb +23 -16
  154. data/lib/active_record/railties/controller_runtime.rb +1 -1
  155. data/lib/active_record/railties/databases.rake +52 -41
  156. data/lib/active_record/readonly_attributes.rb +1 -1
  157. data/lib/active_record/reflection.rb +302 -115
  158. data/lib/active_record/relation.rb +187 -120
  159. data/lib/active_record/relation/batches.rb +141 -36
  160. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  161. data/lib/active_record/relation/calculations.rb +92 -117
  162. data/lib/active_record/relation/delegation.rb +8 -20
  163. data/lib/active_record/relation/finder_methods.rb +173 -89
  164. data/lib/active_record/relation/from_clause.rb +32 -0
  165. data/lib/active_record/relation/merger.rb +16 -42
  166. data/lib/active_record/relation/predicate_builder.rb +120 -107
  167. data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
  168. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  169. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  170. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  171. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  172. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  173. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  174. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  175. data/lib/active_record/relation/query_attribute.rb +19 -0
  176. data/lib/active_record/relation/query_methods.rb +308 -244
  177. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  178. data/lib/active_record/relation/spawn_methods.rb +4 -7
  179. data/lib/active_record/relation/where_clause.rb +174 -0
  180. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  181. data/lib/active_record/result.rb +11 -4
  182. data/lib/active_record/runtime_registry.rb +1 -1
  183. data/lib/active_record/sanitization.rb +105 -66
  184. data/lib/active_record/schema.rb +26 -22
  185. data/lib/active_record/schema_dumper.rb +54 -37
  186. data/lib/active_record/schema_migration.rb +11 -14
  187. data/lib/active_record/scoping.rb +34 -16
  188. data/lib/active_record/scoping/default.rb +28 -10
  189. data/lib/active_record/scoping/named.rb +59 -26
  190. data/lib/active_record/secure_token.rb +38 -0
  191. data/lib/active_record/serialization.rb +3 -5
  192. data/lib/active_record/statement_cache.rb +17 -15
  193. data/lib/active_record/store.rb +8 -3
  194. data/lib/active_record/suppressor.rb +58 -0
  195. data/lib/active_record/table_metadata.rb +69 -0
  196. data/lib/active_record/tasks/database_tasks.rb +66 -49
  197. data/lib/active_record/tasks/mysql_database_tasks.rb +6 -14
  198. data/lib/active_record/tasks/postgresql_database_tasks.rb +12 -3
  199. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  200. data/lib/active_record/timestamp.rb +20 -9
  201. data/lib/active_record/touch_later.rb +63 -0
  202. data/lib/active_record/transactions.rb +139 -57
  203. data/lib/active_record/type.rb +66 -17
  204. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  205. data/lib/active_record/type/date.rb +2 -45
  206. data/lib/active_record/type/date_time.rb +2 -49
  207. data/lib/active_record/type/internal/abstract_json.rb +33 -0
  208. data/lib/active_record/type/internal/timezone.rb +15 -0
  209. data/lib/active_record/type/serialized.rb +15 -14
  210. data/lib/active_record/type/time.rb +10 -16
  211. data/lib/active_record/type/type_map.rb +4 -4
  212. data/lib/active_record/type_caster.rb +7 -0
  213. data/lib/active_record/type_caster/connection.rb +29 -0
  214. data/lib/active_record/type_caster/map.rb +19 -0
  215. data/lib/active_record/validations.rb +33 -32
  216. data/lib/active_record/validations/absence.rb +23 -0
  217. data/lib/active_record/validations/associated.rb +10 -3
  218. data/lib/active_record/validations/length.rb +24 -0
  219. data/lib/active_record/validations/presence.rb +11 -12
  220. data/lib/active_record/validations/uniqueness.rb +33 -33
  221. data/lib/rails/generators/active_record/migration.rb +15 -0
  222. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -5
  223. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
  224. data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -1
  225. data/lib/rails/generators/active_record/model/model_generator.rb +33 -16
  226. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  227. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  228. metadata +58 -34
  229. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  230. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  231. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  232. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  233. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  234. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  235. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  236. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  237. data/lib/active_record/type/big_integer.rb +0 -13
  238. data/lib/active_record/type/binary.rb +0 -50
  239. data/lib/active_record/type/boolean.rb +0 -31
  240. data/lib/active_record/type/decimal.rb +0 -64
  241. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  242. data/lib/active_record/type/decorator.rb +0 -14
  243. data/lib/active_record/type/float.rb +0 -19
  244. data/lib/active_record/type/integer.rb +0 -59
  245. data/lib/active_record/type/mutable.rb +0 -16
  246. data/lib/active_record/type/numeric.rb +0 -36
  247. data/lib/active_record/type/string.rb +0 -40
  248. data/lib/active_record/type/text.rb +0 -11
  249. data/lib/active_record/type/time_value.rb +0 -38
  250. data/lib/active_record/type/unsigned_integer.rb +0 -15
  251. 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.
@@ -338,7 +330,7 @@ module ActiveRecord
338
330
  #
339
331
  # Note: +:id+ is always present.
340
332
  #
341
- # Alias for the <tt>read_attribute</tt> method.
333
+ # Alias for the #read_attribute method.
342
334
  #
343
335
  # class Person < ActiveRecord::Base
344
336
  # belongs_to :organization
@@ -356,7 +348,7 @@ module ActiveRecord
356
348
  end
357
349
 
358
350
  # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
359
- # (Alias for the protected <tt>write_attribute</tt> method).
351
+ # (Alias for the protected #write_attribute method).
360
352
  #
361
353
  # class Person < ActiveRecord::Base
362
354
  # end
@@ -369,6 +361,39 @@ module ActiveRecord
369
361
  write_attribute(attr_name, value)
370
362
  end
371
363
 
364
+ # Returns the name of all database fields which have been read from this
365
+ # model. This can be useful in development mode to determine which fields
366
+ # need to be selected. For performance critical pages, selecting only the
367
+ # required fields can be an easy performance win (assuming you aren't using
368
+ # all of the fields on the model).
369
+ #
370
+ # For example:
371
+ #
372
+ # class PostsController < ActionController::Base
373
+ # after_action :print_accessed_fields, only: :index
374
+ #
375
+ # def index
376
+ # @posts = Post.all
377
+ # end
378
+ #
379
+ # private
380
+ #
381
+ # def print_accessed_fields
382
+ # p @posts.first.accessed_fields
383
+ # end
384
+ # end
385
+ #
386
+ # Which allows you to quickly change your code to:
387
+ #
388
+ # class PostsController < ActionController::Base
389
+ # def index
390
+ # @posts = Post.select(:id, :title, :author_id, :updated_at)
391
+ # end
392
+ # end
393
+ def accessed_fields
394
+ @attributes.accessed
395
+ end
396
+
372
397
  protected
373
398
 
374
399
  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
@@ -5,7 +5,7 @@ module ActiveRecord
5
5
  module PrimaryKey
6
6
  extend ActiveSupport::Concern
7
7
 
8
- # Returns this record's primary key value wrapped in an Array if one is
8
+ # Returns this record's primary key value wrapped in an array if one is
9
9
  # available.
10
10
  def to_key
11
11
  sync_with_transaction_state
@@ -95,7 +95,8 @@ module ActiveRecord
95
95
  base_name.foreign_key
96
96
  else
97
97
  if ActiveRecord::Base != self && table_exists?
98
- connection.schema_cache.primary_keys(table_name)
98
+ pk = connection.schema_cache.primary_keys(table_name)
99
+ suppress_composite_primary_key(pk)
99
100
  else
100
101
  'id'
101
102
  end
@@ -108,7 +109,7 @@ module ActiveRecord
108
109
  # self.primary_key = 'sysid'
109
110
  # end
110
111
  #
111
- # You can also define the +primary_key+ method yourself:
112
+ # You can also define the #primary_key method yourself:
112
113
  #
113
114
  # class Project < ActiveRecord::Base
114
115
  # def self.primary_key
@@ -122,6 +123,18 @@ module ActiveRecord
122
123
  @quoted_primary_key = nil
123
124
  @attributes_builder = nil
124
125
  end
126
+
127
+ private
128
+
129
+ def suppress_composite_primary_key(pk)
130
+ return pk unless pk.is_a?(Array)
131
+
132
+ warn <<-WARNING.strip_heredoc
133
+ WARNING: Active Record does not support composite primary key.
134
+
135
+ #{table_name} has composite primary key. Composite primary key is ignored.
136
+ WARNING
137
+ end
125
138
  end
126
139
  end
127
140
  end