activerecord 4.2.11.1 → 5.2.4.5

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 (274) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +594 -1620
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +10 -11
  5. data/examples/performance.rb +32 -31
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record/aggregations.rb +263 -249
  8. data/lib/active_record/association_relation.rb +11 -6
  9. data/lib/active_record/associations/alias_tracker.rb +29 -35
  10. data/lib/active_record/associations/association.rb +77 -43
  11. data/lib/active_record/associations/association_scope.rb +106 -133
  12. data/lib/active_record/associations/belongs_to_association.rb +52 -41
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
  14. data/lib/active_record/associations/builder/association.rb +29 -38
  15. data/lib/active_record/associations/builder/belongs_to.rb +77 -30
  16. data/lib/active_record/associations/builder/collection_association.rb +9 -22
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +42 -35
  18. data/lib/active_record/associations/builder/has_many.rb +6 -4
  19. data/lib/active_record/associations/builder/has_one.rb +13 -6
  20. data/lib/active_record/associations/builder/singular_association.rb +15 -11
  21. data/lib/active_record/associations/collection_association.rb +139 -280
  22. data/lib/active_record/associations/collection_proxy.rb +231 -133
  23. data/lib/active_record/associations/foreign_association.rb +3 -1
  24. data/lib/active_record/associations/has_many_association.rb +34 -89
  25. data/lib/active_record/associations/has_many_through_association.rb +49 -76
  26. data/lib/active_record/associations/has_one_association.rb +38 -24
  27. data/lib/active_record/associations/has_one_through_association.rb +18 -9
  28. data/lib/active_record/associations/join_dependency/join_association.rb +40 -87
  29. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
  31. data/lib/active_record/associations/join_dependency.rb +133 -159
  32. data/lib/active_record/associations/preloader/association.rb +85 -120
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -74
  34. data/lib/active_record/associations/preloader.rb +81 -91
  35. data/lib/active_record/associations/singular_association.rb +27 -34
  36. data/lib/active_record/associations/through_association.rb +38 -18
  37. data/lib/active_record/associations.rb +1732 -1597
  38. data/lib/active_record/attribute_assignment.rb +58 -182
  39. data/lib/active_record/attribute_decorators.rb +39 -15
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +10 -8
  41. data/lib/active_record/attribute_methods/dirty.rb +94 -135
  42. data/lib/active_record/attribute_methods/primary_key.rb +86 -71
  43. data/lib/active_record/attribute_methods/query.rb +4 -2
  44. data/lib/active_record/attribute_methods/read.rb +45 -63
  45. data/lib/active_record/attribute_methods/serialization.rb +40 -20
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +58 -36
  47. data/lib/active_record/attribute_methods/write.rb +30 -45
  48. data/lib/active_record/attribute_methods.rb +166 -109
  49. data/lib/active_record/attributes.rb +201 -82
  50. data/lib/active_record/autosave_association.rb +94 -36
  51. data/lib/active_record/base.rb +57 -44
  52. data/lib/active_record/callbacks.rb +97 -57
  53. data/lib/active_record/coders/json.rb +3 -1
  54. data/lib/active_record/coders/yaml_column.rb +24 -12
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -290
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +237 -90
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +71 -21
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +118 -52
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +318 -217
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +81 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +570 -228
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +138 -70
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +325 -202
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +542 -601
  69. data/lib/active_record/connection_adapters/column.rb +50 -41
  70. data/lib/active_record/connection_adapters/connection_specification.rb +147 -135
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +41 -180
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +45 -114
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -58
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +10 -6
  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 +4 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -22
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
  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 -11
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  99. data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +55 -53
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +107 -47
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +144 -90
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -284
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +432 -323
  117. data/lib/active_record/connection_adapters/schema_cache.rb +48 -24
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +269 -308
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +40 -27
  128. data/lib/active_record/core.rb +178 -198
  129. data/lib/active_record/counter_cache.rb +79 -36
  130. data/lib/active_record/define_callbacks.rb +22 -0
  131. data/lib/active_record/dynamic_matchers.rb +87 -105
  132. data/lib/active_record/enum.rb +135 -88
  133. data/lib/active_record/errors.rb +179 -52
  134. data/lib/active_record/explain.rb +23 -11
  135. data/lib/active_record/explain_registry.rb +4 -2
  136. data/lib/active_record/explain_subscriber.rb +10 -5
  137. data/lib/active_record/fixture_set/file.rb +35 -9
  138. data/lib/active_record/fixtures.rb +188 -132
  139. data/lib/active_record/gem_version.rb +5 -3
  140. data/lib/active_record/inheritance.rb +148 -112
  141. data/lib/active_record/integration.rb +70 -28
  142. data/lib/active_record/internal_metadata.rb +45 -0
  143. data/lib/active_record/legacy_yaml_adapter.rb +21 -3
  144. data/lib/active_record/locale/en.yml +3 -2
  145. data/lib/active_record/locking/optimistic.rb +88 -96
  146. data/lib/active_record/locking/pessimistic.rb +15 -3
  147. data/lib/active_record/log_subscriber.rb +95 -33
  148. data/lib/active_record/migration/command_recorder.rb +133 -90
  149. data/lib/active_record/migration/compatibility.rb +217 -0
  150. data/lib/active_record/migration/join_table.rb +8 -6
  151. data/lib/active_record/migration.rb +581 -282
  152. data/lib/active_record/model_schema.rb +290 -111
  153. data/lib/active_record/nested_attributes.rb +264 -222
  154. data/lib/active_record/no_touching.rb +7 -1
  155. data/lib/active_record/null_relation.rb +24 -37
  156. data/lib/active_record/persistence.rb +347 -119
  157. data/lib/active_record/query_cache.rb +13 -24
  158. data/lib/active_record/querying.rb +19 -17
  159. data/lib/active_record/railtie.rb +94 -32
  160. data/lib/active_record/railties/console_sandbox.rb +2 -0
  161. data/lib/active_record/railties/controller_runtime.rb +9 -3
  162. data/lib/active_record/railties/databases.rake +149 -156
  163. data/lib/active_record/readonly_attributes.rb +5 -4
  164. data/lib/active_record/reflection.rb +414 -267
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  166. data/lib/active_record/relation/batches.rb +204 -55
  167. data/lib/active_record/relation/calculations.rb +256 -248
  168. data/lib/active_record/relation/delegation.rb +67 -60
  169. data/lib/active_record/relation/finder_methods.rb +288 -239
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +86 -86
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -24
  173. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  174. data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  179. data/lib/active_record/relation/predicate_builder.rb +116 -119
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +448 -393
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +11 -13
  184. data/lib/active_record/relation/where_clause.rb +186 -0
  185. data/lib/active_record/relation/where_clause_factory.rb +34 -0
  186. data/lib/active_record/relation.rb +287 -340
  187. data/lib/active_record/result.rb +54 -36
  188. data/lib/active_record/runtime_registry.rb +6 -4
  189. data/lib/active_record/sanitization.rb +155 -124
  190. data/lib/active_record/schema.rb +30 -24
  191. data/lib/active_record/schema_dumper.rb +91 -87
  192. data/lib/active_record/schema_migration.rb +19 -16
  193. data/lib/active_record/scoping/default.rb +102 -85
  194. data/lib/active_record/scoping/named.rb +81 -32
  195. data/lib/active_record/scoping.rb +45 -26
  196. data/lib/active_record/secure_token.rb +40 -0
  197. data/lib/active_record/serialization.rb +5 -5
  198. data/lib/active_record/statement_cache.rb +45 -35
  199. data/lib/active_record/store.rb +42 -36
  200. data/lib/active_record/suppressor.rb +61 -0
  201. data/lib/active_record/table_metadata.rb +82 -0
  202. data/lib/active_record/tasks/database_tasks.rb +134 -96
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +56 -100
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +83 -41
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -16
  206. data/lib/active_record/timestamp.rb +70 -38
  207. data/lib/active_record/touch_later.rb +64 -0
  208. data/lib/active_record/transactions.rb +199 -124
  209. data/lib/active_record/translation.rb +2 -0
  210. data/lib/active_record/type/adapter_specific_registry.rb +136 -0
  211. data/lib/active_record/type/date.rb +4 -45
  212. data/lib/active_record/type/date_time.rb +4 -49
  213. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  214. data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
  215. data/lib/active_record/type/internal/timezone.rb +17 -0
  216. data/lib/active_record/type/json.rb +30 -0
  217. data/lib/active_record/type/serialized.rb +24 -15
  218. data/lib/active_record/type/text.rb +2 -2
  219. data/lib/active_record/type/time.rb +11 -16
  220. data/lib/active_record/type/type_map.rb +15 -17
  221. data/lib/active_record/type/unsigned_integer.rb +9 -7
  222. data/lib/active_record/type.rb +79 -23
  223. data/lib/active_record/type_caster/connection.rb +33 -0
  224. data/lib/active_record/type_caster/map.rb +23 -0
  225. data/lib/active_record/type_caster.rb +9 -0
  226. data/lib/active_record/validations/absence.rb +25 -0
  227. data/lib/active_record/validations/associated.rb +13 -4
  228. data/lib/active_record/validations/length.rb +26 -0
  229. data/lib/active_record/validations/presence.rb +14 -13
  230. data/lib/active_record/validations/uniqueness.rb +40 -41
  231. data/lib/active_record/validations.rb +38 -35
  232. data/lib/active_record/version.rb +3 -1
  233. data/lib/active_record.rb +34 -22
  234. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  235. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  236. data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -35
  237. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +8 -3
  238. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -1
  239. data/lib/rails/generators/active_record/migration.rb +18 -1
  240. data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
  241. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +3 -0
  242. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  243. data/lib/rails/generators/active_record.rb +7 -5
  244. metadata +72 -49
  245. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  246. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  247. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  248. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  249. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  250. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  251. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  252. data/lib/active_record/attribute.rb +0 -163
  253. data/lib/active_record/attribute_set/builder.rb +0 -106
  254. data/lib/active_record/attribute_set.rb +0 -81
  255. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  256. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  257. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  258. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  259. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  260. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  261. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  262. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  263. data/lib/active_record/type/big_integer.rb +0 -13
  264. data/lib/active_record/type/binary.rb +0 -50
  265. data/lib/active_record/type/boolean.rb +0 -31
  266. data/lib/active_record/type/decimal.rb +0 -64
  267. data/lib/active_record/type/decorator.rb +0 -14
  268. data/lib/active_record/type/float.rb +0 -19
  269. data/lib/active_record/type/integer.rb +0 -59
  270. data/lib/active_record/type/mutable.rb +0 -16
  271. data/lib/active_record/type/numeric.rb +0 -36
  272. data/lib/active_record/type/string.rb +0 -40
  273. data/lib/active_record/type/time_value.rb +0 -38
  274. data/lib/active_record/type/value.rb +0 -110
@@ -1,8 +1,10 @@
1
- require 'active_support/core_ext/module/attribute_accessors'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/module/attribute_accessors"
2
4
 
3
5
  module ActiveRecord
4
6
  module AttributeMethods
5
- module Dirty # :nodoc:
7
+ module Dirty
6
8
  extend ActiveSupport::Concern
7
9
 
8
10
  include ActiveModel::Dirty
@@ -12,180 +14,137 @@ module ActiveRecord
12
14
  raise "You cannot include Dirty after Timestamp"
13
15
  end
14
16
 
15
- class_attribute :partial_writes, instance_writer: false
16
- self.partial_writes = true
17
- end
17
+ class_attribute :partial_writes, instance_writer: false, default: true
18
18
 
19
- # Attempts to +save+ the record and clears changed attributes if successful.
20
- def save(*)
21
- if status = super
22
- changes_applied
23
- end
24
- status
25
- end
19
+ # Attribute methods for "changed in last call to save?"
20
+ attribute_method_affix(prefix: "saved_change_to_", suffix: "?")
21
+ attribute_method_prefix("saved_change_to_")
22
+ attribute_method_suffix("_before_last_save")
26
23
 
27
- # Attempts to <tt>save!</tt> the record and clears changed attributes if successful.
28
- def save!(*)
29
- super.tap do
30
- changes_applied
31
- end
24
+ # Attribute methods for "will change if I call save?"
25
+ attribute_method_affix(prefix: "will_save_change_to_", suffix: "?")
26
+ attribute_method_suffix("_change_to_be_saved", "_in_database")
32
27
  end
33
28
 
34
29
  # <tt>reload</tt> the record and clears changed attributes.
35
30
  def reload(*)
36
31
  super.tap do
37
- clear_changes_information
32
+ @previously_changed = ActiveSupport::HashWithIndifferentAccess.new
33
+ @mutations_before_last_save = nil
34
+ @attributes_changed_by_setter = ActiveSupport::HashWithIndifferentAccess.new
35
+ @mutations_from_database = nil
38
36
  end
39
37
  end
40
38
 
41
- def initialize_dup(other) # :nodoc:
42
- super
43
- @original_raw_attributes = nil
44
- calculate_changes_from_defaults
39
+ # Did this attribute change when we last saved? This method can be invoked
40
+ # as +saved_change_to_name?+ instead of <tt>saved_change_to_attribute?("name")</tt>.
41
+ # Behaves similarly to +attribute_changed?+. This method is useful in
42
+ # after callbacks to determine if the call to save changed a certain
43
+ # attribute.
44
+ #
45
+ # ==== Options
46
+ #
47
+ # +from+ When passed, this method will return false unless the original
48
+ # value is equal to the given option
49
+ #
50
+ # +to+ When passed, this method will return false unless the value was
51
+ # changed to the given value
52
+ def saved_change_to_attribute?(attr_name, **options)
53
+ mutations_before_last_save.changed?(attr_name, **options)
45
54
  end
46
55
 
47
- def changes_applied
48
- super
49
- store_original_raw_attributes
56
+ # Returns the change to an attribute during the last save. If the
57
+ # attribute was changed, the result will be an array containing the
58
+ # original value and the saved value.
59
+ #
60
+ # Behaves similarly to +attribute_change+. This method is useful in after
61
+ # callbacks, to see the change in an attribute that just occurred
62
+ #
63
+ # This method can be invoked as +saved_change_to_name+ in instead of
64
+ # <tt>saved_change_to_attribute("name")</tt>
65
+ def saved_change_to_attribute(attr_name)
66
+ mutations_before_last_save.change_to_attribute(attr_name)
50
67
  end
51
68
 
52
- def clear_changes_information
53
- super
54
- original_raw_attributes.clear
69
+ # Returns the original value of an attribute before the last save.
70
+ # Behaves similarly to +attribute_was+. This method is useful in after
71
+ # callbacks to get the original value of an attribute before the save that
72
+ # just occurred
73
+ def attribute_before_last_save(attr_name)
74
+ mutations_before_last_save.original_value(attr_name)
55
75
  end
56
76
 
57
- def changed_attributes
58
- # This should only be set by methods which will call changed_attributes
59
- # multiple times when it is known that the computed value cannot change.
60
- if defined?(@cached_changed_attributes)
61
- @cached_changed_attributes
62
- else
63
- super.reverse_merge(attributes_changed_in_place).freeze
64
- end
77
+ # Did the last call to +save+ have any changes to change?
78
+ def saved_changes?
79
+ mutations_before_last_save.any_changes?
65
80
  end
66
81
 
67
- def changes
68
- cache_changed_attributes do
69
- super
70
- end
82
+ # Returns a hash containing all the changes that were just saved.
83
+ def saved_changes
84
+ mutations_before_last_save.changes
71
85
  end
72
86
 
73
- 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)
87
+ # Alias for +attribute_changed?+
88
+ def will_save_change_to_attribute?(attr_name, **options)
89
+ mutations_from_database.changed?(attr_name, **options)
76
90
  end
77
91
 
78
- private
79
-
80
- def changes_include?(attr_name)
81
- super || attribute_changed_in_place?(attr_name)
92
+ # Alias for +attribute_change+
93
+ def attribute_change_to_be_saved(attr_name)
94
+ mutations_from_database.change_to_attribute(attr_name)
82
95
  end
83
96
 
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)
88
- end
89
- end
90
-
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
97
+ # Alias for +attribute_was+
98
+ def attribute_in_database(attr_name)
99
+ mutations_from_database.original_value(attr_name)
101
100
  end
102
101
 
103
- def raw_write_attribute(attr, value)
104
- attr = attr.to_s
105
-
106
- result = super
107
- original_raw_attributes[attr] = value
108
- result
102
+ # Alias for +changed?+
103
+ def has_changes_to_save?
104
+ mutations_from_database.any_changes?
109
105
  end
110
106
 
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
107
+ # Alias for +changes+
108
+ def changes_to_save
109
+ mutations_from_database.changes
118
110
  end
119
111
 
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
112
+ # Alias for +changed+
113
+ def changed_attribute_names_to_save
114
+ mutations_from_database.changed_attribute_names
126
115
  end
127
116
 
128
- def _update_record(*)
129
- partial_writes? ? super(keys_for_partial_write) : super
117
+ # Alias for +changed_attributes+
118
+ def attributes_in_database
119
+ mutations_from_database.changed_values
130
120
  end
131
121
 
132
- def _create_record(*)
133
- partial_writes? ? super(keys_for_partial_write) : super
134
- end
135
-
136
- # Serialized attributes should always be written in case they've been
137
- # changed in place.
138
- 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
122
+ private
123
+ def write_attribute_without_type_cast(attr_name, value)
124
+ name = attr_name.to_s
125
+ if self.class.attribute_alias?(name)
126
+ name = self.class.attribute_alias(name)
127
+ end
128
+ result = super(name, value)
129
+ clear_attribute_change(name)
130
+ result
150
131
  end
151
- end
152
132
 
153
- def changed_in_place
154
- self.class.attribute_names.select do |attr_name|
155
- attribute_changed_in_place?(attr_name)
133
+ def _update_record(*)
134
+ affected_rows = partial_writes? ? super(keys_for_partial_write) : super
135
+ changes_applied
136
+ affected_rows
156
137
  end
157
- end
158
138
 
159
- def original_raw_attribute(attr_name)
160
- original_raw_attributes.fetch(attr_name) do
161
- read_attribute_before_type_cast(attr_name)
139
+ def _create_record(*)
140
+ id = partial_writes? ? super(keys_for_partial_write) : super
141
+ changes_applied
142
+ id
162
143
  end
163
- end
164
-
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
144
 
173
- def store_original_raw_attributes
174
- attribute_names.each do |attr|
175
- store_original_raw_attribute(attr)
145
+ def keys_for_partial_write
146
+ changed_attribute_names_to_save & self.class.column_names
176
147
  end
177
- end
178
-
179
- def cache_changed_attributes
180
- @cached_changed_attributes = changed_attributes
181
- yield
182
- ensure
183
- clear_changed_attributes_cache
184
- end
185
-
186
- def clear_changed_attributes_cache
187
- remove_instance_variable(:@cached_changed_attributes) if defined?(@cached_changed_attributes)
188
- end
189
148
  end
190
149
  end
191
150
  end
@@ -1,30 +1,31 @@
1
- require 'set'
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
2
4
 
3
5
  module ActiveRecord
4
6
  module AttributeMethods
5
7
  module PrimaryKey
6
8
  extend ActiveSupport::Concern
7
9
 
8
- # Returns this record's primary key value wrapped in an Array if one is
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
- key = self.id
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.
@@ -45,84 +46,98 @@ module ActiveRecord
45
46
  attribute_was(self.class.primary_key)
46
47
  end
47
48
 
48
- protected
49
-
50
- def attribute_method?(attr_name)
51
- attr_name == 'id' || super
49
+ def id_in_database
50
+ sync_with_transaction_state
51
+ attribute_in_database(self.class.primary_key)
52
52
  end
53
53
 
54
- module ClassMethods
55
- def define_method_attribute(attr_name)
56
- super
54
+ private
57
55
 
58
- if attr_name == primary_key && attr_name != 'id'
59
- generated_attribute_methods.send(:alias_method, :id, primary_key)
60
- end
56
+ def attribute_method?(attr_name)
57
+ attr_name == "id" || super
61
58
  end
62
59
 
63
- ID_ATTRIBUTE_METHODS = %w(id id= id? id_before_type_cast id_was).to_set
60
+ module ClassMethods
61
+ ID_ATTRIBUTE_METHODS = %w(id id= id? id_before_type_cast id_was id_in_database).to_set
64
62
 
65
- def dangerous_attribute_method?(method_name)
66
- super && !ID_ATTRIBUTE_METHODS.include?(method_name)
67
- end
63
+ def instance_method_already_implemented?(method_name)
64
+ super || primary_key && ID_ATTRIBUTE_METHODS.include?(method_name)
65
+ end
68
66
 
69
- # Defines the primary key field -- can be overridden in subclasses.
70
- # Overwriting will negate any effect of the +primary_key_prefix_type+
71
- # setting, though.
72
- def primary_key
73
- @primary_key = reset_primary_key unless defined? @primary_key
74
- @primary_key
75
- end
67
+ def dangerous_attribute_method?(method_name)
68
+ super && !ID_ATTRIBUTE_METHODS.include?(method_name)
69
+ end
76
70
 
77
- # Returns a quoted version of the primary key name, used to construct
78
- # SQL statements.
79
- def quoted_primary_key
80
- @quoted_primary_key ||= connection.quote_column_name(primary_key)
81
- end
71
+ # Defines the primary key field -- can be overridden in subclasses.
72
+ # Overwriting will negate any effect of the +primary_key_prefix_type+
73
+ # setting, though.
74
+ def primary_key
75
+ @primary_key = reset_primary_key unless defined? @primary_key
76
+ @primary_key
77
+ end
82
78
 
83
- def reset_primary_key #:nodoc:
84
- if self == base_class
85
- self.primary_key = get_primary_key(base_class.name)
86
- else
87
- self.primary_key = base_class.primary_key
79
+ # Returns a quoted version of the primary key name, used to construct
80
+ # SQL statements.
81
+ def quoted_primary_key
82
+ @quoted_primary_key ||= connection.quote_column_name(primary_key)
88
83
  end
89
- end
90
84
 
91
- def get_primary_key(base_name) #:nodoc:
92
- if base_name && primary_key_prefix_type == :table_name
93
- base_name.foreign_key(false)
94
- elsif base_name && primary_key_prefix_type == :table_name_with_underscore
95
- base_name.foreign_key
96
- else
97
- if ActiveRecord::Base != self && table_exists?
98
- connection.schema_cache.primary_keys(table_name)
85
+ def reset_primary_key #:nodoc:
86
+ if self == base_class
87
+ self.primary_key = get_primary_key(base_class.name)
99
88
  else
100
- 'id'
89
+ self.primary_key = base_class.primary_key
101
90
  end
102
91
  end
103
- end
104
92
 
105
- # Sets the name of the primary key column.
106
- #
107
- # class Project < ActiveRecord::Base
108
- # self.primary_key = 'sysid'
109
- # end
110
- #
111
- # You can also define the +primary_key+ method yourself:
112
- #
113
- # class Project < ActiveRecord::Base
114
- # def self.primary_key
115
- # 'foo_' + super
116
- # end
117
- # end
118
- #
119
- # Project.primary_key # => "foo_id"
120
- def primary_key=(value)
121
- @primary_key = value && value.to_s
122
- @quoted_primary_key = nil
123
- @attributes_builder = nil
93
+ def get_primary_key(base_name) #:nodoc:
94
+ if base_name && primary_key_prefix_type == :table_name
95
+ base_name.foreign_key(false)
96
+ elsif base_name && primary_key_prefix_type == :table_name_with_underscore
97
+ base_name.foreign_key
98
+ else
99
+ if ActiveRecord::Base != self && table_exists?
100
+ pk = connection.schema_cache.primary_keys(table_name)
101
+ suppress_composite_primary_key(pk)
102
+ else
103
+ "id"
104
+ end
105
+ end
106
+ end
107
+
108
+ # Sets the name of the primary key column.
109
+ #
110
+ # class Project < ActiveRecord::Base
111
+ # self.primary_key = 'sysid'
112
+ # end
113
+ #
114
+ # You can also define the #primary_key method yourself:
115
+ #
116
+ # class Project < ActiveRecord::Base
117
+ # def self.primary_key
118
+ # 'foo_' + super
119
+ # end
120
+ # end
121
+ #
122
+ # Project.primary_key # => "foo_id"
123
+ def primary_key=(value)
124
+ @primary_key = value && value.to_s
125
+ @quoted_primary_key = nil
126
+ @attributes_builder = nil
127
+ end
128
+
129
+ private
130
+
131
+ def suppress_composite_primary_key(pk)
132
+ return pk unless pk.is_a?(Array)
133
+
134
+ warn <<-WARNING.strip_heredoc
135
+ WARNING: Active Record does not support composite primary key.
136
+
137
+ #{table_name} has composite primary key. Composite primary key is ignored.
138
+ WARNING
139
+ end
124
140
  end
125
- end
126
141
  end
127
142
  end
128
143
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module AttributeMethods
3
5
  module Query
@@ -19,10 +21,10 @@ module ActiveRecord
19
21
  if Numeric === value || value !~ /[^0-9]/
20
22
  !value.to_i.zero?
21
23
  else
22
- return false if ActiveRecord::ConnectionAdapters::Column::FALSE_VALUES.include?(value)
24
+ return false if ActiveModel::Type::Boolean::FALSE_VALUES.include?(value)
23
25
  !value.blank?
24
26
  end
25
- elsif column.number?
27
+ elsif value.respond_to?(:zero?)
26
28
  !value.zero?
27
29
  else
28
30
  !value.blank?
@@ -1,68 +1,41 @@
1
- require 'active_support/core_ext/module/method_transplanting'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord
4
4
  module AttributeMethods
5
5
  module Read
6
- ReaderMethodCache = Class.new(AttributeMethodCache) {
7
- private
8
- # We want to generate the methods via module_eval rather than
9
- # define_method, because define_method is slower on dispatch.
10
- # Evaluating many similar methods may use more memory as the instruction
11
- # sequences are duplicated and cached (in MRI). define_method may
12
- # be slower on dispatch, but if you're careful about the closure
13
- # created, then define_method will consume much less memory.
14
- #
15
- # But sometimes the database might return columns with
16
- # characters that are not allowed in normal method names (like
17
- # 'my_column(omg)'. So to work around this we first define with
18
- # the __temp__ identifier, and then use alias method to rename
19
- # it to what we want.
20
- #
21
- # We are also defining a constant to hold the frozen string of
22
- # the attribute name. Using a constant means that we do not have
23
- # to allocate an object on each call to the attribute method.
24
- # Making it frozen means that it doesn't get duped when used to
25
- # key the @attributes in read_attribute.
26
- def method_body(method_name, const_name)
27
- <<-EOMETHOD
28
- def #{method_name}
29
- name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{const_name}
30
- _read_attribute(name) { |n| missing_attribute(n, caller) }
31
- end
32
- EOMETHOD
33
- end
34
- }.new
35
-
36
6
  extend ActiveSupport::Concern
37
7
 
38
- module ClassMethods
39
- [:cache_attributes, :cached_attributes, :cache_attribute?].each do |method_name|
40
- define_method method_name do |*|
41
- cached_attributes_deprecation_warning(method_name)
42
- true
43
- end
44
- end
45
-
46
- protected
47
-
48
- def cached_attributes_deprecation_warning(method_name)
49
- ActiveSupport::Deprecation.warn "Calling `#{method_name}` is no longer necessary. All attributes are cached."
50
- end
8
+ module ClassMethods # :nodoc:
9
+ private
51
10
 
52
- if Module.methods_transplantable?
53
- def define_method_attribute(name)
54
- method = ReaderMethodCache[name]
55
- generated_attribute_methods.module_eval { define_method name, method }
56
- end
57
- else
11
+ # We want to generate the methods via module_eval rather than
12
+ # define_method, because define_method is slower on dispatch.
13
+ # Evaluating many similar methods may use more memory as the instruction
14
+ # sequences are duplicated and cached (in MRI). define_method may
15
+ # be slower on dispatch, but if you're careful about the closure
16
+ # created, then define_method will consume much less memory.
17
+ #
18
+ # But sometimes the database might return columns with
19
+ # characters that are not allowed in normal method names (like
20
+ # 'my_column(omg)'. So to work around this we first define with
21
+ # the __temp__ identifier, and then use alias method to rename
22
+ # it to what we want.
23
+ #
24
+ # We are also defining a constant to hold the frozen string of
25
+ # the attribute name. Using a constant means that we do not have
26
+ # to allocate an object on each call to the attribute method.
27
+ # Making it frozen means that it doesn't get duped when used to
28
+ # key the @attributes in read_attribute.
58
29
  def define_method_attribute(name)
59
- safe_name = name.unpack('h*').first
30
+ safe_name = name.unpack("h*".freeze).first
60
31
  temp_method = "__temp__#{safe_name}"
61
32
 
62
33
  ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
34
+ sync_with_transaction_state = "sync_with_transaction_state" if name == primary_key
63
35
 
64
36
  generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
65
37
  def #{temp_method}
38
+ #{sync_with_transaction_state}
66
39
  name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
67
40
  _read_attribute(name) { |n| missing_attribute(n, caller) }
68
41
  end
@@ -73,31 +46,40 @@ module ActiveRecord
73
46
  undef_method temp_method
74
47
  end
75
48
  end
76
- end
77
49
  end
78
50
 
79
- ID = 'id'.freeze
80
-
81
51
  # Returns the value of the attribute identified by <tt>attr_name</tt> after
82
52
  # it has been typecast (for example, "2004-12-12" in a date column is cast
83
53
  # to a date object, like Date.new(2004, 12, 12)).
84
54
  def read_attribute(attr_name, &block)
85
- name = attr_name.to_s
86
- name = self.class.primary_key if name == ID
55
+ name = if self.class.attribute_alias?(attr_name)
56
+ self.class.attribute_alias(attr_name).to_s
57
+ else
58
+ attr_name.to_s
59
+ end
60
+
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
87
64
  _read_attribute(name, &block)
88
65
  end
89
66
 
90
67
  # This method exists to avoid the expensive primary_key check internally, without
91
68
  # breaking compatibility with the read_attribute API
92
- def _read_attribute(attr_name) # :nodoc:
93
- @attributes.fetch_value(attr_name.to_s) { |n| yield n if block_given? }
69
+ if defined?(JRUBY_VERSION)
70
+ # This form is significantly faster on JRuby, and this is one of our biggest hotspots.
71
+ # https://github.com/jruby/jruby/pull/2562
72
+ def _read_attribute(attr_name, &block) # :nodoc:
73
+ @attributes.fetch_value(attr_name.to_s, &block)
74
+ end
75
+ else
76
+ def _read_attribute(attr_name) # :nodoc:
77
+ @attributes.fetch_value(attr_name.to_s) { |n| yield n if block_given? }
78
+ end
94
79
  end
95
80
 
96
- private
97
-
98
- def attribute(attribute_name)
99
- _read_attribute(attribute_name)
100
- end
81
+ alias :attribute :_read_attribute
82
+ private :attribute
101
83
  end
102
84
  end
103
85
  end