activerecord 5.1.5 → 5.2.1

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 (261) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +450 -699
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +5 -5
  5. data/examples/performance.rb +2 -0
  6. data/examples/simple.rb +2 -0
  7. data/lib/active_record/aggregations.rb +6 -5
  8. data/lib/active_record/association_relation.rb +4 -2
  9. data/lib/active_record/associations/alias_tracker.rb +19 -27
  10. data/lib/active_record/associations/association.rb +33 -37
  11. data/lib/active_record/associations/association_scope.rb +38 -50
  12. data/lib/active_record/associations/belongs_to_association.rb +28 -9
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
  14. data/lib/active_record/associations/builder/association.rb +4 -7
  15. data/lib/active_record/associations/builder/belongs_to.rb +14 -5
  16. data/lib/active_record/associations/builder/collection_association.rb +1 -1
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -1
  18. data/lib/active_record/associations/builder/has_many.rb +2 -0
  19. data/lib/active_record/associations/builder/has_one.rb +2 -0
  20. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  21. data/lib/active_record/associations/collection_association.rb +52 -41
  22. data/lib/active_record/associations/collection_proxy.rb +12 -15
  23. data/lib/active_record/associations/foreign_association.rb +2 -0
  24. data/lib/active_record/associations/has_many_association.rb +3 -1
  25. data/lib/active_record/associations/has_many_through_association.rb +8 -19
  26. data/lib/active_record/associations/has_one_association.rb +12 -1
  27. data/lib/active_record/associations/has_one_through_association.rb +13 -8
  28. data/lib/active_record/associations/join_dependency/join_association.rb +22 -67
  29. data/lib/active_record/associations/join_dependency/join_base.rb +9 -8
  30. data/lib/active_record/associations/join_dependency/join_part.rb +9 -9
  31. data/lib/active_record/associations/join_dependency.rb +48 -93
  32. data/lib/active_record/associations/preloader/association.rb +45 -61
  33. data/lib/active_record/associations/preloader/through_association.rb +71 -79
  34. data/lib/active_record/associations/preloader.rb +17 -37
  35. data/lib/active_record/associations/singular_association.rb +14 -16
  36. data/lib/active_record/associations/through_association.rb +26 -11
  37. data/lib/active_record/associations.rb +40 -63
  38. data/lib/active_record/attribute_assignment.rb +2 -5
  39. data/lib/active_record/attribute_decorators.rb +3 -2
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -0
  41. data/lib/active_record/attribute_methods/dirty.rb +25 -214
  42. data/lib/active_record/attribute_methods/primary_key.rb +7 -6
  43. data/lib/active_record/attribute_methods/query.rb +2 -0
  44. data/lib/active_record/attribute_methods/read.rb +9 -3
  45. data/lib/active_record/attribute_methods/serialization.rb +23 -0
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +6 -8
  47. data/lib/active_record/attribute_methods/write.rb +21 -9
  48. data/lib/active_record/attribute_methods.rb +65 -24
  49. data/lib/active_record/attributes.rb +7 -6
  50. data/lib/active_record/autosave_association.rb +16 -14
  51. data/lib/active_record/base.rb +2 -0
  52. data/lib/active_record/callbacks.rb +12 -6
  53. data/lib/active_record/coders/json.rb +2 -0
  54. data/lib/active_record/coders/yaml_column.rb +2 -0
  55. data/lib/active_record/collection_cache_key.rb +11 -7
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +110 -35
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -0
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +157 -29
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +7 -2
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -32
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +14 -5
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +64 -6
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +149 -78
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -21
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +81 -96
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +92 -165
  69. data/lib/active_record/connection_adapters/column.rb +3 -1
  70. data/lib/active_record/connection_adapters/connection_specification.rb +17 -3
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +2 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +2 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +47 -2
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +2 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +9 -10
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +5 -3
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +7 -10
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +30 -30
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +106 -1
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +2 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -2
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +2 -0
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -0
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +2 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +6 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +2 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +2 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -1
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -1
  99. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +2 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +8 -2
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +4 -2
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +3 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +3 -1
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +18 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +14 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +24 -11
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +20 -13
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +248 -112
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +57 -73
  117. data/lib/active_record/connection_adapters/schema_cache.rb +4 -2
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +2 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +2 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +20 -1
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +2 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -15
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +3 -2
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +75 -1
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +79 -92
  126. data/lib/active_record/connection_adapters/statement_pool.rb +2 -0
  127. data/lib/active_record/connection_handling.rb +4 -2
  128. data/lib/active_record/core.rb +39 -60
  129. data/lib/active_record/counter_cache.rb +20 -15
  130. data/lib/active_record/define_callbacks.rb +5 -3
  131. data/lib/active_record/dynamic_matchers.rb +9 -9
  132. data/lib/active_record/enum.rb +17 -13
  133. data/lib/active_record/errors.rb +42 -3
  134. data/lib/active_record/explain.rb +3 -1
  135. data/lib/active_record/explain_registry.rb +2 -0
  136. data/lib/active_record/explain_subscriber.rb +2 -0
  137. data/lib/active_record/fixture_set/file.rb +2 -0
  138. data/lib/active_record/fixtures.rb +67 -60
  139. data/lib/active_record/gem_version.rb +4 -2
  140. data/lib/active_record/inheritance.rb +49 -19
  141. data/lib/active_record/integration.rb +58 -19
  142. data/lib/active_record/internal_metadata.rb +2 -0
  143. data/lib/active_record/legacy_yaml_adapter.rb +3 -1
  144. data/lib/active_record/locking/optimistic.rb +30 -42
  145. data/lib/active_record/locking/pessimistic.rb +9 -6
  146. data/lib/active_record/log_subscriber.rb +43 -0
  147. data/lib/active_record/migration/command_recorder.rb +11 -9
  148. data/lib/active_record/migration/compatibility.rb +40 -2
  149. data/lib/active_record/migration/join_table.rb +2 -0
  150. data/lib/active_record/migration.rb +189 -139
  151. data/lib/active_record/model_schema.rb +19 -24
  152. data/lib/active_record/nested_attributes.rb +18 -6
  153. data/lib/active_record/no_touching.rb +3 -1
  154. data/lib/active_record/null_relation.rb +2 -0
  155. data/lib/active_record/persistence.rb +196 -48
  156. data/lib/active_record/query_cache.rb +12 -14
  157. data/lib/active_record/querying.rb +3 -1
  158. data/lib/active_record/railtie.rb +61 -3
  159. data/lib/active_record/railties/console_sandbox.rb +2 -0
  160. data/lib/active_record/railties/controller_runtime.rb +2 -0
  161. data/lib/active_record/railties/databases.rake +46 -36
  162. data/lib/active_record/readonly_attributes.rb +3 -2
  163. data/lib/active_record/reflection.rb +110 -192
  164. data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
  165. data/lib/active_record/relation/batches.rb +20 -5
  166. data/lib/active_record/relation/calculations.rb +31 -9
  167. data/lib/active_record/relation/delegation.rb +15 -27
  168. data/lib/active_record/relation/finder_methods.rb +71 -76
  169. data/lib/active_record/relation/from_clause.rb +2 -8
  170. data/lib/active_record/relation/merger.rb +47 -20
  171. data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -7
  172. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  173. data/lib/active_record/relation/predicate_builder/base_handler.rb +2 -2
  174. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +12 -1
  175. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  176. data/lib/active_record/relation/predicate_builder/range_handler.rb +26 -9
  177. data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
  178. data/lib/active_record/relation/predicate_builder.rb +55 -79
  179. data/lib/active_record/relation/query_attribute.rb +26 -2
  180. data/lib/active_record/relation/query_methods.rb +95 -91
  181. data/lib/active_record/relation/record_fetch_warning.rb +2 -0
  182. data/lib/active_record/relation/spawn_methods.rb +3 -1
  183. data/lib/active_record/relation/where_clause.rb +65 -68
  184. data/lib/active_record/relation/where_clause_factory.rb +5 -48
  185. data/lib/active_record/relation.rb +106 -219
  186. data/lib/active_record/result.rb +2 -0
  187. data/lib/active_record/runtime_registry.rb +2 -0
  188. data/lib/active_record/sanitization.rb +129 -121
  189. data/lib/active_record/schema.rb +4 -2
  190. data/lib/active_record/schema_dumper.rb +36 -26
  191. data/lib/active_record/schema_migration.rb +2 -0
  192. data/lib/active_record/scoping/default.rb +6 -7
  193. data/lib/active_record/scoping/named.rb +21 -7
  194. data/lib/active_record/scoping.rb +9 -8
  195. data/lib/active_record/secure_token.rb +2 -0
  196. data/lib/active_record/serialization.rb +2 -0
  197. data/lib/active_record/statement_cache.rb +22 -12
  198. data/lib/active_record/store.rb +3 -1
  199. data/lib/active_record/suppressor.rb +2 -0
  200. data/lib/active_record/table_metadata.rb +12 -3
  201. data/lib/active_record/tasks/database_tasks.rb +25 -14
  202. data/lib/active_record/tasks/mysql_database_tasks.rb +9 -48
  203. data/lib/active_record/tasks/postgresql_database_tasks.rb +10 -2
  204. data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
  205. data/lib/active_record/timestamp.rb +13 -6
  206. data/lib/active_record/touch_later.rb +2 -0
  207. data/lib/active_record/transactions.rb +32 -27
  208. data/lib/active_record/translation.rb +2 -0
  209. data/lib/active_record/type/adapter_specific_registry.rb +2 -0
  210. data/lib/active_record/type/date.rb +2 -0
  211. data/lib/active_record/type/date_time.rb +2 -0
  212. data/lib/active_record/type/decimal_without_scale.rb +2 -0
  213. data/lib/active_record/type/hash_lookup_type_map.rb +2 -0
  214. data/lib/active_record/type/internal/timezone.rb +2 -0
  215. data/lib/active_record/type/json.rb +30 -0
  216. data/lib/active_record/type/serialized.rb +6 -0
  217. data/lib/active_record/type/text.rb +2 -0
  218. data/lib/active_record/type/time.rb +2 -0
  219. data/lib/active_record/type/type_map.rb +2 -0
  220. data/lib/active_record/type/unsigned_integer.rb +2 -0
  221. data/lib/active_record/type.rb +4 -1
  222. data/lib/active_record/type_caster/connection.rb +2 -0
  223. data/lib/active_record/type_caster/map.rb +3 -1
  224. data/lib/active_record/type_caster.rb +2 -0
  225. data/lib/active_record/validations/absence.rb +2 -0
  226. data/lib/active_record/validations/associated.rb +2 -0
  227. data/lib/active_record/validations/length.rb +2 -0
  228. data/lib/active_record/validations/presence.rb +2 -0
  229. data/lib/active_record/validations/uniqueness.rb +36 -6
  230. data/lib/active_record/validations.rb +2 -0
  231. data/lib/active_record/version.rb +2 -0
  232. data/lib/active_record.rb +11 -4
  233. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  234. data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  235. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -1
  236. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +0 -0
  237. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +0 -0
  238. data/lib/rails/generators/active_record/migration.rb +2 -0
  239. data/lib/rails/generators/active_record/model/model_generator.rb +2 -23
  240. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +0 -0
  241. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  242. data/lib/rails/generators/active_record.rb +3 -1
  243. metadata +23 -36
  244. data/lib/active_record/associations/preloader/belongs_to.rb +0 -15
  245. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  246. data/lib/active_record/associations/preloader/has_many.rb +0 -15
  247. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  248. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  249. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  250. data/lib/active_record/associations/preloader/singular_association.rb +0 -18
  251. data/lib/active_record/attribute/user_provided_default.rb +0 -30
  252. data/lib/active_record/attribute.rb +0 -240
  253. data/lib/active_record/attribute_mutation_tracker.rb +0 -114
  254. data/lib/active_record/attribute_set/builder.rb +0 -124
  255. data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
  256. data/lib/active_record/attribute_set.rb +0 -113
  257. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -10
  258. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  259. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  260. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -59
  261. data/lib/active_record/type/internal/abstract_json.rb +0 -37
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require "active_support/core_ext/module/attribute_accessors"
3
- require "active_record/attribute_mutation_tracker"
4
4
 
5
5
  module ActiveRecord
6
6
  module AttributeMethods
@@ -14,11 +14,10 @@ module ActiveRecord
14
14
  raise "You cannot include Dirty after Timestamp"
15
15
  end
16
16
 
17
- class_attribute :partial_writes, instance_writer: false
18
- self.partial_writes = true
17
+ class_attribute :partial_writes, instance_writer: false, default: true
19
18
 
20
- after_create { changes_internally_applied }
21
- after_update { changes_internally_applied }
19
+ after_create { changes_applied }
20
+ after_update { changes_applied }
22
21
 
23
22
  # Attribute methods for "changed in last call to save?"
24
23
  attribute_method_affix(prefix: "saved_change_to_", suffix: "?")
@@ -30,107 +29,18 @@ module ActiveRecord
30
29
  attribute_method_suffix("_change_to_be_saved", "_in_database")
31
30
  end
32
31
 
33
- # Attempts to +save+ the record and clears changed attributes if successful.
34
- def save(*)
35
- if status = super
36
- changes_applied
37
- end
38
- status
39
- end
40
-
41
- # Attempts to <tt>save!</tt> the record and clears changed attributes if successful.
42
- def save!(*)
43
- super.tap do
44
- changes_applied
45
- end
46
- end
47
-
48
32
  # <tt>reload</tt> the record and clears changed attributes.
49
33
  def reload(*)
50
34
  super.tap do
51
- @previous_mutation_tracker = nil
52
- clear_mutation_trackers
53
- @changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
54
- end
55
- end
56
-
57
- def initialize_dup(other) # :nodoc:
58
- super
59
- @attributes = self.class._default_attributes.map do |attr|
60
- attr.with_value_from_user(@attributes.fetch_value(attr.name))
61
- end
62
- clear_mutation_trackers
63
- end
64
-
65
- def changes_internally_applied # :nodoc:
66
- @mutations_before_last_save = mutations_from_database
67
- forget_attribute_assignments
68
- @mutations_from_database = AttributeMutationTracker.new(@attributes)
69
- end
70
-
71
- def changes_applied # :nodoc:
72
- @previous_mutation_tracker = mutation_tracker
73
- @changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
74
- @mutation_tracker = nil
75
- @mutations_from_database = nil
76
- end
77
-
78
- def clear_changes_information # :nodoc:
79
- @previous_mutation_tracker = nil
80
- @changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
81
- forget_attribute_assignments
82
- clear_mutation_trackers
83
- end
84
-
85
- def raw_write_attribute(attr_name, *) # :nodoc:
86
- result = super
87
- clear_attribute_change(attr_name)
88
- result
89
- end
90
-
91
- def clear_attribute_changes(attr_names) # :nodoc:
92
- super
93
- attr_names.each do |attr_name|
94
- clear_attribute_change(attr_name)
95
- end
96
- end
97
-
98
- def changed_attributes # :nodoc:
99
- # This should only be set by methods which will call changed_attributes
100
- # multiple times when it is known that the computed value cannot change.
101
- if defined?(@cached_changed_attributes)
102
- @cached_changed_attributes
103
- else
104
- emit_warning_if_needed("changed_attributes", "saved_changes.transform_values(&:first)")
105
- super.reverse_merge(mutation_tracker.changed_values).freeze
106
- end
107
- end
108
-
109
- def changes # :nodoc:
110
- cache_changed_attributes do
111
- emit_warning_if_needed("changes", "saved_changes")
112
- super
113
- end
114
- end
115
-
116
- def previous_changes # :nodoc:
117
- unless previous_mutation_tracker.equal?(mutations_before_last_save)
118
- ActiveSupport::Deprecation.warn(<<-EOW.strip_heredoc)
119
- The behavior of `previous_changes` inside of after callbacks is
120
- deprecated without replacement. In the next release of Rails,
121
- this method inside of `after_save` will return the changes that
122
- were just saved.
123
- EOW
35
+ @previously_changed = ActiveSupport::HashWithIndifferentAccess.new
36
+ @mutations_before_last_save = nil
37
+ @attributes_changed_by_setter = ActiveSupport::HashWithIndifferentAccess.new
38
+ @mutations_from_database = nil
124
39
  end
125
- previous_mutation_tracker.changes
126
- end
127
-
128
- def attribute_changed_in_place?(attr_name) # :nodoc:
129
- mutation_tracker.changed_in_place?(attr_name)
130
40
  end
131
41
 
132
42
  # Did this attribute change when we last saved? This method can be invoked
133
- # as `saved_change_to_name?` instead of `saved_change_to_attribute?("name")`.
43
+ # as +saved_change_to_name?+ instead of <tt>saved_change_to_attribute?("name")</tt>.
134
44
  # Behaves similarly to +attribute_changed?+. This method is useful in
135
45
  # after callbacks to determine if the call to save changed a certain
136
46
  # attribute.
@@ -153,8 +63,8 @@ module ActiveRecord
153
63
  # Behaves similarly to +attribute_change+. This method is useful in after
154
64
  # callbacks, to see the change in an attribute that just occurred
155
65
  #
156
- # This method can be invoked as `saved_change_to_name` in instead of
157
- # `saved_change_to_attribute("name")`
66
+ # This method can be invoked as +saved_change_to_name+ in instead of
67
+ # <tt>saved_change_to_attribute("name")</tt>
158
68
  def saved_change_to_attribute(attr_name)
159
69
  mutations_before_last_save.change_to_attribute(attr_name)
160
70
  end
@@ -167,7 +77,7 @@ module ActiveRecord
167
77
  mutations_before_last_save.original_value(attr_name)
168
78
  end
169
79
 
170
- # Did the last call to `save` have any changes to change?
80
+ # Did the last call to +save+ have any changes to change?
171
81
  def saved_changes?
172
82
  mutations_before_last_save.any_changes?
173
83
  end
@@ -177,116 +87,46 @@ module ActiveRecord
177
87
  mutations_before_last_save.changes
178
88
  end
179
89
 
180
- # Alias for `attribute_changed?`
90
+ # Alias for +attribute_changed?+
181
91
  def will_save_change_to_attribute?(attr_name, **options)
182
92
  mutations_from_database.changed?(attr_name, **options)
183
93
  end
184
94
 
185
- # Alias for `attribute_change`
95
+ # Alias for +attribute_change+
186
96
  def attribute_change_to_be_saved(attr_name)
187
97
  mutations_from_database.change_to_attribute(attr_name)
188
98
  end
189
99
 
190
- # Alias for `attribute_was`
100
+ # Alias for +attribute_was+
191
101
  def attribute_in_database(attr_name)
192
102
  mutations_from_database.original_value(attr_name)
193
103
  end
194
104
 
195
- # Alias for `changed?`
105
+ # Alias for +changed?+
196
106
  def has_changes_to_save?
197
107
  mutations_from_database.any_changes?
198
108
  end
199
109
 
200
- # Alias for `changes`
110
+ # Alias for +changes+
201
111
  def changes_to_save
202
112
  mutations_from_database.changes
203
113
  end
204
114
 
205
- # Alias for `changed`
115
+ # Alias for +changed+
206
116
  def changed_attribute_names_to_save
207
- changes_to_save.keys
117
+ mutations_from_database.changed_attribute_names
208
118
  end
209
119
 
210
- # Alias for `changed_attributes`
120
+ # Alias for +changed_attributes+
211
121
  def attributes_in_database
212
- changes_to_save.transform_values(&:first)
213
- end
214
-
215
- def attribute_was(*)
216
- emit_warning_if_needed("attribute_was", "attribute_before_last_save")
217
- super
218
- end
219
-
220
- def attribute_change(*)
221
- emit_warning_if_needed("attribute_change", "saved_change_to_attribute")
222
- super
223
- end
224
-
225
- def attribute_changed?(*)
226
- emit_warning_if_needed("attribute_changed?", "saved_change_to_attribute?")
227
- super
228
- end
229
-
230
- def changed?(*)
231
- emit_warning_if_needed("changed?", "saved_changes?")
232
- super
233
- end
234
-
235
- def changed(*)
236
- emit_warning_if_needed("changed", "saved_changes.keys")
237
- super
122
+ mutations_from_database.changed_values
238
123
  end
239
124
 
240
125
  private
241
-
242
- def mutation_tracker
243
- unless defined?(@mutation_tracker)
244
- @mutation_tracker = nil
245
- end
246
- @mutation_tracker ||= AttributeMutationTracker.new(@attributes)
247
- end
248
-
249
- def emit_warning_if_needed(method_name, new_method_name)
250
- unless mutation_tracker.equal?(mutations_from_database)
251
- ActiveSupport::Deprecation.warn(<<-EOW.squish)
252
- The behavior of `#{method_name}` inside of after callbacks will
253
- be changing in the next version of Rails. The new return value will reflect the
254
- behavior of calling the method after `save` returned (e.g. the opposite of what
255
- it returns now). To maintain the current behavior, use `#{new_method_name}`
256
- instead.
257
- EOW
258
- end
259
- end
260
-
261
- def mutations_from_database
262
- unless defined?(@mutations_from_database)
263
- @mutations_from_database = nil
264
- end
265
- @mutations_from_database ||= mutation_tracker
266
- end
267
-
268
- def changes_include?(attr_name)
269
- super || mutation_tracker.changed?(attr_name)
270
- end
271
-
272
- def clear_attribute_change(attr_name)
273
- mutation_tracker.forget_change(attr_name)
274
- mutations_from_database.forget_change(attr_name)
275
- end
276
-
277
- def attribute_will_change!(attr_name)
278
- super
279
- if self.class.has_attribute?(attr_name)
280
- mutations_from_database.force_change(attr_name)
281
- else
282
- ActiveSupport::Deprecation.warn(<<-EOW.squish)
283
- #{attr_name} is not an attribute known to Active Record.
284
- This behavior is deprecated and will be removed in the next
285
- version of Rails. If you'd like #{attr_name} to be managed
286
- by Active Record, add `attribute :#{attr_name} to your class.
287
- EOW
288
- mutations_from_database.deprecated_force_change(attr_name)
289
- end
126
+ def write_attribute_without_type_cast(attr_name, _)
127
+ result = super
128
+ clear_attribute_change(attr_name)
129
+ result
290
130
  end
291
131
 
292
132
  def _update_record(*)
@@ -300,35 +140,6 @@ module ActiveRecord
300
140
  def keys_for_partial_write
301
141
  changed_attribute_names_to_save & self.class.column_names
302
142
  end
303
-
304
- def forget_attribute_assignments
305
- @attributes = @attributes.map(&:forgetting_assignment)
306
- end
307
-
308
- def clear_mutation_trackers
309
- @mutation_tracker = nil
310
- @mutations_from_database = nil
311
- @mutations_before_last_save = nil
312
- end
313
-
314
- def previous_mutation_tracker
315
- @previous_mutation_tracker ||= NullMutationTracker.instance
316
- end
317
-
318
- def mutations_before_last_save
319
- @mutations_before_last_save ||= previous_mutation_tracker
320
- end
321
-
322
- def cache_changed_attributes
323
- @cached_changed_attributes = changed_attributes
324
- yield
325
- ensure
326
- clear_changed_attributes_cache
327
- end
328
-
329
- def clear_changed_attributes_cache
330
- remove_instance_variable(:@cached_changed_attributes) if defined?(@cached_changed_attributes)
331
- end
332
143
  end
333
144
  end
334
145
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "set"
2
4
 
3
5
  module ActiveRecord
@@ -8,23 +10,22 @@ module ActiveRecord
8
10
  # Returns this record's primary key value wrapped in an array if one is
9
11
  # available.
10
12
  def to_key
11
- sync_with_transaction_state
12
13
  key = id
13
14
  [key] if key
14
15
  end
15
16
 
16
17
  # Returns the primary key value.
17
18
  def id
18
- if pk = self.class.primary_key
19
- sync_with_transaction_state
20
- _read_attribute(pk)
21
- end
19
+ sync_with_transaction_state
20
+ primary_key = self.class.primary_key
21
+ _read_attribute(primary_key) if primary_key
22
22
  end
23
23
 
24
24
  # Sets the primary key value.
25
25
  def id=(value)
26
26
  sync_with_transaction_state
27
- write_attribute(self.class.primary_key, value) if self.class.primary_key
27
+ primary_key = self.class.primary_key
28
+ _write_attribute(primary_key, value) if primary_key
28
29
  end
29
30
 
30
31
  # Queries the primary key value.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module AttributeMethods
3
5
  module Query
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module AttributeMethods
3
5
  module Read
4
6
  extend ActiveSupport::Concern
5
7
 
6
- module ClassMethods
8
+ module ClassMethods # :nodoc:
7
9
  private
8
10
 
9
11
  # We want to generate the methods via module_eval rather than
@@ -29,9 +31,11 @@ module ActiveRecord
29
31
  temp_method = "__temp__#{safe_name}"
30
32
 
31
33
  ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
34
+ sync_with_transaction_state = "sync_with_transaction_state" if name == primary_key
32
35
 
33
36
  generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
34
37
  def #{temp_method}
38
+ #{sync_with_transaction_state}
35
39
  name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
36
40
  _read_attribute(name) { |n| missing_attribute(n, caller) }
37
41
  end
@@ -54,7 +58,9 @@ module ActiveRecord
54
58
  attr_name.to_s
55
59
  end
56
60
 
57
- name = self.class.primary_key if name == "id".freeze && self.class.primary_key
61
+ primary_key = self.class.primary_key
62
+ name = primary_key if name == "id".freeze && primary_key
63
+ sync_with_transaction_state if name == primary_key
58
64
  _read_attribute(name, &block)
59
65
  end
60
66
 
@@ -63,7 +69,7 @@ module ActiveRecord
63
69
  if defined?(JRUBY_VERSION)
64
70
  # This form is significantly faster on JRuby, and this is one of our biggest hotspots.
65
71
  # https://github.com/jruby/jruby/pull/2562
66
- def _read_attribute(attr_name, &block) # :nodoc
72
+ def _read_attribute(attr_name, &block) # :nodoc:
67
73
  @attributes.fetch_value(attr_name.to_s, &block)
68
74
  end
69
75
  else
@@ -1,8 +1,20 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module AttributeMethods
3
5
  module Serialization
4
6
  extend ActiveSupport::Concern
5
7
 
8
+ class ColumnNotSerializableError < StandardError
9
+ def initialize(name, type)
10
+ super <<-EOS.strip_heredoc
11
+ Column `#{name}` of type #{type.class} does not support `serialize` feature.
12
+ Usually it means that you are trying to use `serialize`
13
+ on a column that already implements serialization natively.
14
+ EOS
15
+ end
16
+ end
17
+
6
18
  module ClassMethods
7
19
  # If you have an attribute that needs to be saved to the database as an
8
20
  # object, and retrieved as the same object, then specify the name of that
@@ -58,9 +70,20 @@ module ActiveRecord
58
70
  end
59
71
 
60
72
  decorate_attribute_type(attr_name, :serialize) do |type|
73
+ if type_incompatible_with_serialize?(type, class_name_or_coder)
74
+ raise ColumnNotSerializableError.new(attr_name, type)
75
+ end
76
+
61
77
  Type::Serialized.new(type, coder)
62
78
  end
63
79
  end
80
+
81
+ private
82
+
83
+ def type_incompatible_with_serialize?(type, class_name)
84
+ type.is_a?(ActiveRecord::Type::Json) && class_name == ::JSON ||
85
+ type.respond_to?(:type_cast_array, true) && class_name == ::Array
86
+ end
64
87
  end
65
88
  end
66
89
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module AttributeMethods
3
5
  module TimeZoneConversion
@@ -54,17 +56,13 @@ module ActiveRecord
54
56
  extend ActiveSupport::Concern
55
57
 
56
58
  included do
57
- mattr_accessor :time_zone_aware_attributes, instance_writer: false
58
- self.time_zone_aware_attributes = false
59
-
60
- class_attribute :skip_time_zone_conversion_for_attributes, instance_writer: false
61
- self.skip_time_zone_conversion_for_attributes = []
59
+ mattr_accessor :time_zone_aware_attributes, instance_writer: false, default: false
62
60
 
63
- class_attribute :time_zone_aware_types, instance_writer: false
64
- self.time_zone_aware_types = [:datetime, :time]
61
+ class_attribute :skip_time_zone_conversion_for_attributes, instance_writer: false, default: []
62
+ class_attribute :time_zone_aware_types, instance_writer: false, default: [ :datetime, :time ]
65
63
  end
66
64
 
67
- module ClassMethods
65
+ module ClassMethods # :nodoc:
68
66
  private
69
67
 
70
68
  def inherited(subclass)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module AttributeMethods
3
5
  module Write
@@ -7,17 +9,19 @@ module ActiveRecord
7
9
  attribute_method_suffix "="
8
10
  end
9
11
 
10
- module ClassMethods
12
+ module ClassMethods # :nodoc:
11
13
  private
12
14
 
13
15
  def define_method_attribute=(name)
14
16
  safe_name = name.unpack("h*".freeze).first
15
17
  ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
18
+ sync_with_transaction_state = "sync_with_transaction_state" if name == primary_key
16
19
 
17
20
  generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
18
21
  def __temp__#{safe_name}=(value)
19
22
  name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
20
- write_attribute(name, value)
23
+ #{sync_with_transaction_state}
24
+ _write_attribute(name, value)
21
25
  end
22
26
  alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
23
27
  undef_method :__temp__#{safe_name}=
@@ -35,21 +39,29 @@ module ActiveRecord
35
39
  attr_name.to_s
36
40
  end
37
41
 
38
- name = self.class.primary_key if name == "id".freeze && self.class.primary_key
39
- @attributes.write_from_user(name, value)
40
- value
42
+ primary_key = self.class.primary_key
43
+ name = primary_key if name == "id".freeze && primary_key
44
+ sync_with_transaction_state if name == primary_key
45
+ _write_attribute(name, value)
41
46
  end
42
47
 
43
- def raw_write_attribute(attr_name, value) # :nodoc:
44
- name = attr_name.to_s
45
- @attributes.write_cast_value(name, value)
48
+ # This method exists to avoid the expensive primary_key check internally, without
49
+ # breaking compatibility with the write_attribute API
50
+ def _write_attribute(attr_name, value) # :nodoc:
51
+ @attributes.write_from_user(attr_name.to_s, value)
46
52
  value
47
53
  end
48
54
 
49
55
  private
56
+ def write_attribute_without_type_cast(attr_name, value)
57
+ name = attr_name.to_s
58
+ @attributes.write_cast_value(name, value)
59
+ value
60
+ end
61
+
50
62
  # Handle *= for method_missing.
51
63
  def attribute=(attribute_name, value)
52
- write_attribute(attribute_name, value)
64
+ _write_attribute(attribute_name, value)
53
65
  end
54
66
  end
55
67
  end
@@ -1,7 +1,6 @@
1
- require "active_support/core_ext/enumerable"
2
- require "active_support/core_ext/string/filters"
1
+ # frozen_string_literal: true
2
+
3
3
  require "mutex_m"
4
- require "concurrent/map"
5
4
 
6
5
  module ActiveRecord
7
6
  # = Active Record Attribute Methods
@@ -34,7 +33,9 @@ module ActiveRecord
34
33
 
35
34
  BLACKLISTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
36
35
 
37
- class GeneratedAttributeMethods < Module; end # :nodoc:
36
+ class GeneratedAttributeMethods < Module #:nodoc:
37
+ include Mutex_m
38
+ end
38
39
 
39
40
  module ClassMethods
40
41
  def inherited(child_class) #:nodoc:
@@ -43,7 +44,7 @@ module ActiveRecord
43
44
  end
44
45
 
45
46
  def initialize_generated_modules # :nodoc:
46
- @generated_attribute_methods = GeneratedAttributeMethods.new { extend Mutex_m }
47
+ @generated_attribute_methods = GeneratedAttributeMethods.new
47
48
  @attribute_methods_generated = false
48
49
  include @generated_attribute_methods
49
50
 
@@ -62,7 +63,6 @@ module ActiveRecord
62
63
  super(attribute_names)
63
64
  @attribute_methods_generated = true
64
65
  end
65
- true
66
66
  end
67
67
 
68
68
  def undefine_attribute_methods # :nodoc:
@@ -167,6 +167,57 @@ module ActiveRecord
167
167
  end
168
168
  end
169
169
 
170
+ # Regexp whitelist. Matches the following:
171
+ # "#{table_name}.#{column_name}"
172
+ # "#{column_name}"
173
+ COLUMN_NAME_WHITELIST = /\A(?:\w+\.)?\w+\z/i
174
+
175
+ # Regexp whitelist. Matches the following:
176
+ # "#{table_name}.#{column_name}"
177
+ # "#{table_name}.#{column_name} #{direction}"
178
+ # "#{table_name}.#{column_name} #{direction} NULLS FIRST"
179
+ # "#{table_name}.#{column_name} NULLS LAST"
180
+ # "#{column_name}"
181
+ # "#{column_name} #{direction}"
182
+ # "#{column_name} #{direction} NULLS FIRST"
183
+ # "#{column_name} NULLS LAST"
184
+ COLUMN_NAME_ORDER_WHITELIST = /
185
+ \A
186
+ (?:\w+\.)?
187
+ \w+
188
+ (?:\s+asc|\s+desc)?
189
+ (?:\s+nulls\s+(?:first|last))?
190
+ \z
191
+ /ix
192
+
193
+ def enforce_raw_sql_whitelist(args, whitelist: COLUMN_NAME_WHITELIST) # :nodoc:
194
+ unexpected = args.reject do |arg|
195
+ arg.kind_of?(Arel::Node) ||
196
+ arg.is_a?(Arel::Nodes::SqlLiteral) ||
197
+ arg.is_a?(Arel::Attributes::Attribute) ||
198
+ arg.to_s.split(/\s*,\s*/).all? { |part| whitelist.match?(part) }
199
+ end
200
+
201
+ return if unexpected.none?
202
+
203
+ if allow_unsafe_raw_sql == :deprecated
204
+ ActiveSupport::Deprecation.warn(
205
+ "Dangerous query method (method whose arguments are used as raw " \
206
+ "SQL) called with non-attribute argument(s): " \
207
+ "#{unexpected.map(&:inspect).join(", ")}. Non-attribute " \
208
+ "arguments will be disallowed in Rails 6.0. This method should " \
209
+ "not be called with user-provided values, such as request " \
210
+ "parameters or model attributes. Known-safe values can be passed " \
211
+ "by wrapping them in Arel.sql()."
212
+ )
213
+ else
214
+ raise(ActiveRecord::UnknownAttributeReference,
215
+ "Query method called with non-attribute argument(s): " +
216
+ unexpected.map(&:inspect).join(", ")
217
+ )
218
+ end
219
+ end
220
+
170
221
  # Returns true if the given attribute exists, otherwise false.
171
222
  #
172
223
  # class Person < ActiveRecord::Base
@@ -236,7 +287,7 @@ module ActiveRecord
236
287
  return has_attribute?(name)
237
288
  end
238
289
 
239
- return true
290
+ true
240
291
  end
241
292
 
242
293
  # Returns +true+ if the given attribute is in the attributes hash, otherwise +false+.
@@ -401,24 +452,18 @@ module ActiveRecord
401
452
 
402
453
  private
403
454
 
404
- def arel_attributes_with_values_for_create(attribute_names)
405
- arel_attributes_with_values(attributes_for_create(attribute_names))
455
+ def attributes_with_values_for_create(attribute_names)
456
+ attributes_with_values(attributes_for_create(attribute_names))
406
457
  end
407
458
 
408
- def arel_attributes_with_values_for_update(attribute_names)
409
- arel_attributes_with_values(attributes_for_update(attribute_names))
459
+ def attributes_with_values_for_update(attribute_names)
460
+ attributes_with_values(attributes_for_update(attribute_names))
410
461
  end
411
462
 
412
- # Returns a Hash of the Arel::Attributes and attribute values that have been
413
- # typecasted for use in an Arel insert/update method.
414
- def arel_attributes_with_values(attribute_names)
415
- attrs = {}
416
- arel_table = self.class.arel_table
417
-
418
- attribute_names.each do |name|
419
- attrs[arel_table[name]] = typecasted_attribute_value(name)
463
+ def attributes_with_values(attribute_names)
464
+ attribute_names.each_with_object({}) do |name, attrs|
465
+ attrs[name] = _read_attribute(name)
420
466
  end
421
- attrs
422
467
  end
423
468
 
424
469
  # Filters the primary keys and readonly attributes from the attribute names.
@@ -443,9 +488,5 @@ module ActiveRecord
443
488
  def pk_attribute?(name)
444
489
  name == self.class.primary_key
445
490
  end
446
-
447
- def typecasted_attribute_value(name)
448
- _read_attribute(name)
449
- end
450
491
  end
451
492
  end