activerecord 5.1.0 → 5.2.0.rc1

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 (260) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +410 -530
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  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 +23 -32
  10. data/lib/active_record/associations/association.rb +20 -21
  11. data/lib/active_record/associations/association_scope.rb +49 -49
  12. data/lib/active_record/associations/belongs_to_association.rb +12 -10
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +4 -7
  14. data/lib/active_record/associations/builder/association.rb +4 -7
  15. data/lib/active_record/associations/builder/belongs_to.rb +10 -6
  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 +50 -41
  22. data/lib/active_record/associations/collection_proxy.rb +22 -39
  23. data/lib/active_record/associations/foreign_association.rb +2 -0
  24. data/lib/active_record/associations/has_many_association.rb +4 -2
  25. data/lib/active_record/associations/has_many_through_association.rb +12 -18
  26. data/lib/active_record/associations/has_one_association.rb +5 -1
  27. data/lib/active_record/associations/has_one_through_association.rb +8 -7
  28. data/lib/active_record/associations/join_dependency/join_association.rb +17 -64
  29. data/lib/active_record/associations/join_dependency/join_base.rb +9 -8
  30. data/lib/active_record/associations/join_dependency/join_part.rb +2 -9
  31. data/lib/active_record/associations/join_dependency.rb +27 -44
  32. data/lib/active_record/associations/preloader/association.rb +53 -92
  33. data/lib/active_record/associations/preloader/through_association.rb +72 -73
  34. data/lib/active_record/associations/preloader.rb +17 -37
  35. data/lib/active_record/associations/singular_association.rb +14 -10
  36. data/lib/active_record/associations/through_association.rb +26 -11
  37. data/lib/active_record/associations.rb +68 -76
  38. data/lib/active_record/attribute_assignment.rb +2 -0
  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 +24 -214
  42. data/lib/active_record/attribute_methods/primary_key.rb +10 -13
  43. data/lib/active_record/attribute_methods/query.rb +2 -0
  44. data/lib/active_record/attribute_methods/read.rb +8 -2
  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 +22 -19
  48. data/lib/active_record/attribute_methods.rb +48 -12
  49. data/lib/active_record/attributes.rb +7 -6
  50. data/lib/active_record/autosave_association.rb +8 -11
  51. data/lib/active_record/base.rb +2 -0
  52. data/lib/active_record/callbacks.rb +8 -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 +14 -10
  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 +175 -33
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +8 -2
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +13 -24
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -6
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +58 -3
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +165 -85
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +45 -9
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +83 -97
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +118 -180
  69. data/lib/active_record/connection_adapters/column.rb +4 -2
  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 +11 -17
  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 -23
  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 +30 -1
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -32
  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 +2 -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_time.rb +2 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +2 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  96. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -1
  98. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +2 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +4 -2
  101. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +4 -2
  103. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +3 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -1
  107. data/lib/active_record/connection_adapters/postgresql/quoting.rb +22 -1
  108. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
  109. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +14 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +24 -11
  111. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +20 -13
  112. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +269 -126
  113. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
  114. data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -0
  115. data/lib/active_record/connection_adapters/postgresql_adapter.rb +64 -85
  116. data/lib/active_record/connection_adapters/schema_cache.rb +4 -2
  117. data/lib/active_record/connection_adapters/sql_type_metadata.rb +2 -0
  118. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +2 -0
  119. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +18 -0
  120. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +2 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -15
  122. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +3 -2
  123. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +71 -1
  124. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +92 -95
  125. data/lib/active_record/connection_adapters/statement_pool.rb +2 -0
  126. data/lib/active_record/connection_handling.rb +4 -2
  127. data/lib/active_record/core.rb +39 -60
  128. data/lib/active_record/counter_cache.rb +3 -2
  129. data/lib/active_record/define_callbacks.rb +5 -3
  130. data/lib/active_record/dynamic_matchers.rb +9 -9
  131. data/lib/active_record/enum.rb +17 -13
  132. data/lib/active_record/errors.rb +42 -3
  133. data/lib/active_record/explain.rb +3 -1
  134. data/lib/active_record/explain_registry.rb +2 -0
  135. data/lib/active_record/explain_subscriber.rb +2 -0
  136. data/lib/active_record/fixture_set/file.rb +2 -0
  137. data/lib/active_record/fixtures.rb +67 -60
  138. data/lib/active_record/gem_version.rb +4 -2
  139. data/lib/active_record/inheritance.rb +9 -9
  140. data/lib/active_record/integration.rb +58 -19
  141. data/lib/active_record/internal_metadata.rb +2 -0
  142. data/lib/active_record/legacy_yaml_adapter.rb +3 -1
  143. data/lib/active_record/locking/optimistic.rb +8 -6
  144. data/lib/active_record/locking/pessimistic.rb +9 -6
  145. data/lib/active_record/log_subscriber.rb +46 -4
  146. data/lib/active_record/migration/command_recorder.rb +11 -9
  147. data/lib/active_record/migration/compatibility.rb +74 -22
  148. data/lib/active_record/migration/join_table.rb +2 -0
  149. data/lib/active_record/migration.rb +181 -137
  150. data/lib/active_record/model_schema.rb +73 -58
  151. data/lib/active_record/nested_attributes.rb +18 -6
  152. data/lib/active_record/no_touching.rb +3 -1
  153. data/lib/active_record/null_relation.rb +2 -0
  154. data/lib/active_record/persistence.rb +153 -18
  155. data/lib/active_record/query_cache.rb +17 -12
  156. data/lib/active_record/querying.rb +4 -2
  157. data/lib/active_record/railtie.rb +61 -3
  158. data/lib/active_record/railties/console_sandbox.rb +2 -0
  159. data/lib/active_record/railties/controller_runtime.rb +2 -0
  160. data/lib/active_record/railties/databases.rake +47 -37
  161. data/lib/active_record/readonly_attributes.rb +3 -2
  162. data/lib/active_record/reflection.rb +131 -204
  163. data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
  164. data/lib/active_record/relation/batches.rb +32 -17
  165. data/lib/active_record/relation/calculations.rb +58 -20
  166. data/lib/active_record/relation/delegation.rb +10 -29
  167. data/lib/active_record/relation/finder_methods.rb +74 -85
  168. data/lib/active_record/relation/from_clause.rb +2 -8
  169. data/lib/active_record/relation/merger.rb +51 -20
  170. data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -7
  171. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  172. data/lib/active_record/relation/predicate_builder/base_handler.rb +2 -2
  173. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +12 -1
  174. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +54 -0
  175. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -6
  176. data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
  177. data/lib/active_record/relation/predicate_builder.rb +53 -78
  178. data/lib/active_record/relation/query_attribute.rb +9 -2
  179. data/lib/active_record/relation/query_methods.rb +101 -95
  180. data/lib/active_record/relation/record_fetch_warning.rb +2 -0
  181. data/lib/active_record/relation/spawn_methods.rb +3 -1
  182. data/lib/active_record/relation/where_clause.rb +65 -67
  183. data/lib/active_record/relation/where_clause_factory.rb +5 -48
  184. data/lib/active_record/relation.rb +99 -202
  185. data/lib/active_record/result.rb +2 -0
  186. data/lib/active_record/runtime_registry.rb +2 -0
  187. data/lib/active_record/sanitization.rb +129 -121
  188. data/lib/active_record/schema.rb +4 -2
  189. data/lib/active_record/schema_dumper.rb +36 -26
  190. data/lib/active_record/schema_migration.rb +2 -0
  191. data/lib/active_record/scoping/default.rb +10 -7
  192. data/lib/active_record/scoping/named.rb +38 -12
  193. data/lib/active_record/scoping.rb +12 -10
  194. data/lib/active_record/secure_token.rb +2 -0
  195. data/lib/active_record/serialization.rb +2 -0
  196. data/lib/active_record/statement_cache.rb +22 -12
  197. data/lib/active_record/store.rb +3 -1
  198. data/lib/active_record/suppressor.rb +2 -0
  199. data/lib/active_record/table_metadata.rb +12 -3
  200. data/lib/active_record/tasks/database_tasks.rb +37 -25
  201. data/lib/active_record/tasks/mysql_database_tasks.rb +11 -50
  202. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -3
  203. data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
  204. data/lib/active_record/timestamp.rb +5 -5
  205. data/lib/active_record/touch_later.rb +2 -0
  206. data/lib/active_record/transactions.rb +9 -7
  207. data/lib/active_record/translation.rb +2 -0
  208. data/lib/active_record/type/adapter_specific_registry.rb +2 -0
  209. data/lib/active_record/type/date.rb +2 -0
  210. data/lib/active_record/type/date_time.rb +2 -0
  211. data/lib/active_record/type/decimal_without_scale.rb +2 -0
  212. data/lib/active_record/type/hash_lookup_type_map.rb +2 -0
  213. data/lib/active_record/type/internal/timezone.rb +2 -0
  214. data/lib/active_record/type/json.rb +30 -0
  215. data/lib/active_record/type/serialized.rb +2 -0
  216. data/lib/active_record/type/text.rb +2 -0
  217. data/lib/active_record/type/time.rb +2 -0
  218. data/lib/active_record/type/type_map.rb +2 -0
  219. data/lib/active_record/type/unsigned_integer.rb +2 -0
  220. data/lib/active_record/type.rb +4 -1
  221. data/lib/active_record/type_caster/connection.rb +2 -0
  222. data/lib/active_record/type_caster/map.rb +3 -1
  223. data/lib/active_record/type_caster.rb +2 -0
  224. data/lib/active_record/validations/absence.rb +2 -0
  225. data/lib/active_record/validations/associated.rb +2 -0
  226. data/lib/active_record/validations/length.rb +2 -0
  227. data/lib/active_record/validations/presence.rb +2 -0
  228. data/lib/active_record/validations/uniqueness.rb +35 -5
  229. data/lib/active_record/validations.rb +2 -0
  230. data/lib/active_record/version.rb +2 -0
  231. data/lib/active_record.rb +11 -4
  232. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  233. data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  234. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -1
  235. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +0 -0
  236. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +0 -0
  237. data/lib/rails/generators/active_record/migration.rb +2 -0
  238. data/lib/rails/generators/active_record/model/model_generator.rb +2 -23
  239. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +0 -0
  240. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  241. data/lib/rails/generators/active_record.rb +3 -1
  242. metadata +25 -37
  243. data/lib/active_record/associations/preloader/belongs_to.rb +0 -15
  244. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  245. data/lib/active_record/associations/preloader/has_many.rb +0 -15
  246. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  247. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  248. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  249. data/lib/active_record/associations/preloader/singular_association.rb +0 -18
  250. data/lib/active_record/attribute/user_provided_default.rb +0 -30
  251. data/lib/active_record/attribute.rb +0 -240
  252. data/lib/active_record/attribute_mutation_tracker.rb +0 -113
  253. data/lib/active_record/attribute_set/builder.rb +0 -124
  254. data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
  255. data/lib/active_record/attribute_set.rb +0 -113
  256. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -10
  257. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  258. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  259. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -59
  260. data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -1,10 +1,10 @@
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
7
- module Dirty # :nodoc:
7
+ module Dirty
8
8
  extend ActiveSupport::Concern
9
9
 
10
10
  include ActiveModel::Dirty
@@ -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,106 +29,16 @@ 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 = mutation_tracker
67
- forget_attribute_assignments
68
- @mutations_from_database = AttributeMutationTracker.new(@attributes)
69
- end
70
-
71
- def changes_applied
72
- @previous_mutation_tracker = mutation_tracker
73
- @changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
74
- clear_mutation_trackers
75
- end
76
-
77
- def clear_changes_information
78
- @previous_mutation_tracker = nil
79
- @changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
80
- forget_attribute_assignments
81
- clear_mutation_trackers
82
- end
83
-
84
- def raw_write_attribute(attr_name, *)
85
- result = super
86
- clear_attribute_change(attr_name)
87
- result
88
- end
89
-
90
- def clear_attribute_changes(attr_names)
91
- super
92
- attr_names.each do |attr_name|
93
- clear_attribute_change(attr_name)
94
- end
95
- end
96
-
97
- def changed_attributes
98
- # This should only be set by methods which will call changed_attributes
99
- # multiple times when it is known that the computed value cannot change.
100
- if defined?(@cached_changed_attributes)
101
- @cached_changed_attributes
102
- else
103
- emit_warning_if_needed("changed_attributes", "saved_changes.transform_values(&:first)")
104
- super.reverse_merge(mutation_tracker.changed_values).freeze
105
- end
106
- end
107
-
108
- def changes
109
- cache_changed_attributes do
110
- emit_warning_if_needed("changes", "saved_changes")
111
- super
112
- end
113
- end
114
-
115
- def previous_changes
116
- unless previous_mutation_tracker.equal?(mutations_before_last_save)
117
- ActiveSupport::Deprecation.warn(<<-EOW.strip_heredoc)
118
- The behavior of `previous_changes` inside of after callbacks is
119
- deprecated without replacement. In the next release of Rails,
120
- this method inside of `after_save` will return the changes that
121
- were just saved.
122
- EOW
35
+ @mutations_before_last_save = nil
36
+ @mutations_from_database = nil
123
37
  end
124
- previous_mutation_tracker.changes
125
- end
126
-
127
- def attribute_changed_in_place?(attr_name)
128
- mutation_tracker.changed_in_place?(attr_name)
129
38
  end
130
39
 
131
40
  # Did this attribute change when we last saved? This method can be invoked
132
- # as `saved_change_to_name?` instead of `saved_change_to_attribute?("name")`.
41
+ # as +saved_change_to_name?+ instead of <tt>saved_change_to_attribute?("name")</tt>.
133
42
  # Behaves similarly to +attribute_changed?+. This method is useful in
134
43
  # after callbacks to determine if the call to save changed a certain
135
44
  # attribute.
@@ -152,8 +61,8 @@ module ActiveRecord
152
61
  # Behaves similarly to +attribute_change+. This method is useful in after
153
62
  # callbacks, to see the change in an attribute that just occurred
154
63
  #
155
- # This method can be invoked as `saved_change_to_name` in instead of
156
- # `saved_change_to_attribute("name")`
64
+ # This method can be invoked as +saved_change_to_name+ in instead of
65
+ # <tt>saved_change_to_attribute("name")</tt>
157
66
  def saved_change_to_attribute(attr_name)
158
67
  mutations_before_last_save.change_to_attribute(attr_name)
159
68
  end
@@ -166,7 +75,7 @@ module ActiveRecord
166
75
  mutations_before_last_save.original_value(attr_name)
167
76
  end
168
77
 
169
- # Did the last call to `save` have any changes to change?
78
+ # Did the last call to +save+ have any changes to change?
170
79
  def saved_changes?
171
80
  mutations_before_last_save.any_changes?
172
81
  end
@@ -176,116 +85,46 @@ module ActiveRecord
176
85
  mutations_before_last_save.changes
177
86
  end
178
87
 
179
- # Alias for `attribute_changed?`
88
+ # Alias for +attribute_changed?+
180
89
  def will_save_change_to_attribute?(attr_name, **options)
181
90
  mutations_from_database.changed?(attr_name, **options)
182
91
  end
183
92
 
184
- # Alias for `attribute_change`
93
+ # Alias for +attribute_change+
185
94
  def attribute_change_to_be_saved(attr_name)
186
95
  mutations_from_database.change_to_attribute(attr_name)
187
96
  end
188
97
 
189
- # Alias for `attribute_was`
98
+ # Alias for +attribute_was+
190
99
  def attribute_in_database(attr_name)
191
100
  mutations_from_database.original_value(attr_name)
192
101
  end
193
102
 
194
- # Alias for `changed?`
103
+ # Alias for +changed?+
195
104
  def has_changes_to_save?
196
105
  mutations_from_database.any_changes?
197
106
  end
198
107
 
199
- # Alias for `changes`
108
+ # Alias for +changes+
200
109
  def changes_to_save
201
110
  mutations_from_database.changes
202
111
  end
203
112
 
204
- # Alias for `changed`
113
+ # Alias for +changed+
205
114
  def changed_attribute_names_to_save
206
- changes_to_save.keys
115
+ mutations_from_database.changed_attribute_names
207
116
  end
208
117
 
209
- # Alias for `changed_attributes`
118
+ # Alias for +changed_attributes+
210
119
  def attributes_in_database
211
- changes_to_save.transform_values(&:first)
212
- end
213
-
214
- def attribute_was(*)
215
- emit_warning_if_needed("attribute_was", "attribute_before_last_save")
216
- super
217
- end
218
-
219
- def attribute_change(*)
220
- emit_warning_if_needed("attribute_change", "saved_change_to_attribute")
221
- super
222
- end
223
-
224
- def attribute_changed?(*)
225
- emit_warning_if_needed("attribute_changed?", "saved_change_to_attribute?")
226
- super
227
- end
228
-
229
- def changed?(*)
230
- emit_warning_if_needed("changed?", "saved_changes?")
231
- super
232
- end
233
-
234
- def changed(*)
235
- emit_warning_if_needed("changed", "saved_changes.keys")
236
- super
120
+ mutations_from_database.changed_values
237
121
  end
238
122
 
239
123
  private
240
-
241
- def mutation_tracker
242
- unless defined?(@mutation_tracker)
243
- @mutation_tracker = nil
244
- end
245
- @mutation_tracker ||= AttributeMutationTracker.new(@attributes)
246
- end
247
-
248
- def emit_warning_if_needed(method_name, new_method_name)
249
- unless mutation_tracker.equal?(mutations_from_database)
250
- ActiveSupport::Deprecation.warn(<<-EOW.squish)
251
- The behavior of `#{method_name}` inside of after callbacks will
252
- be changing in the next version of Rails. The new return value will reflect the
253
- behavior of calling the method after `save` returned (e.g. the opposite of what
254
- it returns now). To maintain the current behavior, use `#{new_method_name}`
255
- instead.
256
- EOW
257
- end
258
- end
259
-
260
- def mutations_from_database
261
- unless defined?(@mutations_from_database)
262
- @mutations_from_database = nil
263
- end
264
- @mutations_from_database ||= mutation_tracker
265
- end
266
-
267
- def changes_include?(attr_name)
268
- super || mutation_tracker.changed?(attr_name)
269
- end
270
-
271
- def clear_attribute_change(attr_name)
272
- mutation_tracker.forget_change(attr_name)
273
- mutations_from_database.forget_change(attr_name)
274
- end
275
-
276
- def attribute_will_change!(attr_name)
277
- super
278
- if self.class.has_attribute?(attr_name)
279
- mutations_from_database.force_change(attr_name)
280
- else
281
- ActiveSupport::Deprecation.warn(<<-EOW.squish)
282
- #{attr_name} is not an attribute known to Active Record.
283
- This behavior is deprecated and will be removed in the next
284
- version of Rails. If you'd like #{attr_name} to be managed
285
- by Active Record, add `attribute :#{attr_name} to your class.
286
- EOW
287
- mutations_from_database.deprecated_force_change(attr_name)
288
- end
124
+ def write_attribute_without_type_cast(attr_name, _)
125
+ result = super
126
+ clear_attribute_change(attr_name)
127
+ result
289
128
  end
290
129
 
291
130
  def _update_record(*)
@@ -299,35 +138,6 @@ module ActiveRecord
299
138
  def keys_for_partial_write
300
139
  changed_attribute_names_to_save & self.class.column_names
301
140
  end
302
-
303
- def forget_attribute_assignments
304
- @attributes = @attributes.map(&:forgetting_assignment)
305
- end
306
-
307
- def clear_mutation_trackers
308
- @mutation_tracker = nil
309
- @mutations_from_database = nil
310
- @mutations_before_last_save = nil
311
- end
312
-
313
- def previous_mutation_tracker
314
- @previous_mutation_tracker ||= NullMutationTracker.instance
315
- end
316
-
317
- def mutations_before_last_save
318
- @mutations_before_last_save ||= previous_mutation_tracker
319
- end
320
-
321
- def cache_changed_attributes
322
- @cached_changed_attributes = changed_attributes
323
- yield
324
- ensure
325
- clear_changed_attributes_cache
326
- end
327
-
328
- def clear_changed_attributes_cache
329
- remove_instance_variable(:@cached_changed_attributes) if defined?(@cached_changed_attributes)
330
- end
331
141
  end
332
142
  end
333
143
  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.
@@ -57,16 +58,12 @@ module ActiveRecord
57
58
  end
58
59
 
59
60
  module ClassMethods
60
- def define_method_attribute(attr_name)
61
- super
61
+ ID_ATTRIBUTE_METHODS = %w(id id= id? id_before_type_cast id_was id_in_database).to_set
62
62
 
63
- if attr_name == primary_key && attr_name != "id"
64
- generated_attribute_methods.send(:alias_method, :id, primary_key)
65
- end
63
+ def instance_method_already_implemented?(method_name)
64
+ super || primary_key && ID_ATTRIBUTE_METHODS.include?(method_name)
66
65
  end
67
66
 
68
- ID_ATTRIBUTE_METHODS = %w(id id= id? id_before_type_cast id_was id_in_database).to_set
69
-
70
67
  def dangerous_attribute_method?(method_name)
71
68
  super && !ID_ATTRIBUTE_METHODS.include?(method_name)
72
69
  end
@@ -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
 
@@ -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,30 +39,29 @@ module ActiveRecord
35
39
  attr_name.to_s
36
40
  end
37
41
 
38
- write_attribute_with_type_cast(name, value, true)
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)
39
46
  end
40
47
 
41
- def raw_write_attribute(attr_name, value) # :nodoc:
42
- write_attribute_with_type_cast(attr_name, value, false)
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)
52
+ value
43
53
  end
44
54
 
45
55
  private
46
- # Handle *= for method_missing.
47
- def attribute=(attribute_name, value)
48
- write_attribute(attribute_name, value)
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
49
60
  end
50
61
 
51
- def write_attribute_with_type_cast(attr_name, value, should_type_cast)
52
- attr_name = attr_name.to_s
53
- attr_name = self.class.primary_key if attr_name == "id" && self.class.primary_key
54
-
55
- if should_type_cast
56
- @attributes.write_from_user(attr_name, value)
57
- else
58
- @attributes.write_cast_value(attr_name, value)
59
- end
60
-
61
- value
62
+ # Handle *= for method_missing.
63
+ def attribute=(attribute_name, value)
64
+ _write_attribute(attribute_name, value)
62
65
  end
63
66
  end
64
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,46 @@ 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
+ # "#{column_name}"
179
+ # "#{column_name} #{direction}"
180
+ COLUMN_NAME_ORDER_WHITELIST = /\A(?:\w+\.)?\w+(?:\s+asc|\s+desc)?\z/i
181
+
182
+ def enforce_raw_sql_whitelist(args, whitelist: COLUMN_NAME_WHITELIST) # :nodoc:
183
+ unexpected = args.reject do |arg|
184
+ arg.kind_of?(Arel::Node) ||
185
+ arg.is_a?(Arel::Nodes::SqlLiteral) ||
186
+ arg.is_a?(Arel::Attributes::Attribute) ||
187
+ arg.to_s.split(/\s*,\s*/).all? { |part| whitelist.match?(part) }
188
+ end
189
+
190
+ return if unexpected.none?
191
+
192
+ if allow_unsafe_raw_sql == :deprecated
193
+ ActiveSupport::Deprecation.warn(
194
+ "Dangerous query method (method whose arguments are used as raw " \
195
+ "SQL) called with non-attribute argument(s): " \
196
+ "#{unexpected.map(&:inspect).join(", ")}. Non-attribute " \
197
+ "arguments will be disallowed in Rails 6.0. This method should " \
198
+ "not be called with user-provided values, such as request " \
199
+ "parameters or model attributes. Known-safe values can be passed " \
200
+ "by wrapping them in Arel.sql()."
201
+ )
202
+ else
203
+ raise(ActiveRecord::UnknownAttributeReference,
204
+ "Query method called with non-attribute argument(s): " +
205
+ unexpected.map(&:inspect).join(", ")
206
+ )
207
+ end
208
+ end
209
+
170
210
  # Returns true if the given attribute exists, otherwise false.
171
211
  #
172
212
  # class Person < ActiveRecord::Base
@@ -236,7 +276,7 @@ module ActiveRecord
236
276
  return has_attribute?(name)
237
277
  end
238
278
 
239
- return true
279
+ true
240
280
  end
241
281
 
242
282
  # Returns +true+ if the given attribute is in the attributes hash, otherwise +false+.
@@ -416,7 +456,7 @@ module ActiveRecord
416
456
  arel_table = self.class.arel_table
417
457
 
418
458
  attribute_names.each do |name|
419
- attrs[arel_table[name]] = typecasted_attribute_value(name)
459
+ attrs[arel_table[name]] = _read_attribute(name)
420
460
  end
421
461
  attrs
422
462
  end
@@ -443,9 +483,5 @@ module ActiveRecord
443
483
  def pk_attribute?(name)
444
484
  name == self.class.primary_key
445
485
  end
446
-
447
- def typecasted_attribute_value(name)
448
- _read_attribute(name)
449
- end
450
486
  end
451
487
  end