activerecord 5.2.8.1 → 6.1.6.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 (316) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1255 -596
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +7 -5
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +9 -8
  7. data/lib/active_record/association_relation.rb +30 -10
  8. data/lib/active_record/associations/alias_tracker.rb +19 -16
  9. data/lib/active_record/associations/association.rb +100 -41
  10. data/lib/active_record/associations/association_scope.rb +23 -21
  11. data/lib/active_record/associations/belongs_to_association.rb +55 -48
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -6
  13. data/lib/active_record/associations/builder/association.rb +45 -22
  14. data/lib/active_record/associations/builder/belongs_to.rb +29 -59
  15. data/lib/active_record/associations/builder/collection_association.rb +8 -17
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
  17. data/lib/active_record/associations/builder/has_many.rb +8 -2
  18. data/lib/active_record/associations/builder/has_one.rb +33 -2
  19. data/lib/active_record/associations/builder/singular_association.rb +3 -1
  20. data/lib/active_record/associations/collection_association.rb +44 -34
  21. data/lib/active_record/associations/collection_proxy.rb +25 -21
  22. data/lib/active_record/associations/foreign_association.rb +20 -0
  23. data/lib/active_record/associations/has_many_association.rb +26 -13
  24. data/lib/active_record/associations/has_many_through_association.rb +24 -18
  25. data/lib/active_record/associations/has_one_association.rb +43 -31
  26. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  27. data/lib/active_record/associations/join_dependency/join_association.rb +44 -22
  28. data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
  29. data/lib/active_record/associations/join_dependency.rb +91 -60
  30. data/lib/active_record/associations/preloader/association.rb +69 -43
  31. data/lib/active_record/associations/preloader/through_association.rb +49 -40
  32. data/lib/active_record/associations/preloader.rb +47 -34
  33. data/lib/active_record/associations/singular_association.rb +3 -17
  34. data/lib/active_record/associations/through_association.rb +1 -1
  35. data/lib/active_record/associations.rb +137 -25
  36. data/lib/active_record/attribute_assignment.rb +17 -19
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -7
  38. data/lib/active_record/attribute_methods/dirty.rb +101 -40
  39. data/lib/active_record/attribute_methods/primary_key.rb +20 -25
  40. data/lib/active_record/attribute_methods/query.rb +4 -8
  41. data/lib/active_record/attribute_methods/read.rb +14 -56
  42. data/lib/active_record/attribute_methods/serialization.rb +12 -7
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  44. data/lib/active_record/attribute_methods/write.rb +18 -34
  45. data/lib/active_record/attribute_methods.rb +81 -143
  46. data/lib/active_record/attributes.rb +46 -9
  47. data/lib/active_record/autosave_association.rb +57 -42
  48. data/lib/active_record/base.rb +4 -17
  49. data/lib/active_record/callbacks.rb +158 -43
  50. data/lib/active_record/coders/yaml_column.rb +1 -2
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +272 -130
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -36
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -146
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -14
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +98 -47
  56. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -110
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +211 -90
  59. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -4
  60. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +385 -144
  61. data/lib/active_record/connection_adapters/abstract/transaction.rb +167 -69
  62. data/lib/active_record/connection_adapters/abstract_adapter.rb +229 -99
  63. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +243 -275
  64. data/lib/active_record/connection_adapters/column.rb +30 -12
  65. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  66. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  67. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  68. data/lib/active_record/connection_adapters/mysql/database_statements.rb +88 -32
  69. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  70. data/lib/active_record/connection_adapters/mysql/quoting.rb +59 -7
  71. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +34 -10
  72. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +48 -32
  73. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +18 -7
  74. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +142 -19
  75. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
  76. data/lib/active_record/connection_adapters/mysql2_adapter.rb +53 -18
  77. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  78. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  79. data/lib/active_record/connection_adapters/postgresql/column.rb +37 -28
  80. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -54
  81. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  84. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +3 -4
  90. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +3 -4
  93. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
  94. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  96. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
  97. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  98. data/lib/active_record/connection_adapters/postgresql/quoting.rb +47 -10
  99. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  100. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +19 -4
  101. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  102. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  103. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +120 -100
  104. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +31 -26
  105. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  106. data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -120
  107. data/lib/active_record/connection_adapters/schema_cache.rb +159 -21
  108. data/lib/active_record/connection_adapters/sql_type_metadata.rb +17 -6
  109. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +146 -0
  110. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
  111. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  112. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +77 -13
  113. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +174 -186
  114. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  115. data/lib/active_record/connection_adapters.rb +52 -0
  116. data/lib/active_record/connection_handling.rb +293 -33
  117. data/lib/active_record/core.rb +333 -98
  118. data/lib/active_record/counter_cache.rb +8 -30
  119. data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -0
  120. data/lib/active_record/database_configurations/database_config.rb +80 -0
  121. data/lib/active_record/database_configurations/hash_config.rb +96 -0
  122. data/lib/active_record/database_configurations/url_config.rb +53 -0
  123. data/lib/active_record/database_configurations.rb +273 -0
  124. data/lib/active_record/delegated_type.rb +209 -0
  125. data/lib/active_record/destroy_association_async_job.rb +36 -0
  126. data/lib/active_record/dynamic_matchers.rb +3 -4
  127. data/lib/active_record/enum.rb +108 -36
  128. data/lib/active_record/errors.rb +62 -19
  129. data/lib/active_record/explain.rb +10 -6
  130. data/lib/active_record/explain_subscriber.rb +1 -1
  131. data/lib/active_record/fixture_set/file.rb +10 -17
  132. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  133. data/lib/active_record/fixture_set/render_context.rb +17 -0
  134. data/lib/active_record/fixture_set/table_row.rb +152 -0
  135. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  136. data/lib/active_record/fixtures.rb +200 -481
  137. data/lib/active_record/gem_version.rb +3 -3
  138. data/lib/active_record/inheritance.rb +53 -24
  139. data/lib/active_record/insert_all.rb +212 -0
  140. data/lib/active_record/integration.rb +67 -17
  141. data/lib/active_record/internal_metadata.rb +28 -9
  142. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  143. data/lib/active_record/locking/optimistic.rb +37 -23
  144. data/lib/active_record/locking/pessimistic.rb +9 -5
  145. data/lib/active_record/log_subscriber.rb +35 -35
  146. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  147. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  148. data/lib/active_record/middleware/database_selector.rb +77 -0
  149. data/lib/active_record/migration/command_recorder.rb +96 -44
  150. data/lib/active_record/migration/compatibility.rb +145 -64
  151. data/lib/active_record/migration/join_table.rb +0 -1
  152. data/lib/active_record/migration.rb +206 -157
  153. data/lib/active_record/model_schema.rb +148 -22
  154. data/lib/active_record/nested_attributes.rb +4 -7
  155. data/lib/active_record/no_touching.rb +8 -1
  156. data/lib/active_record/null_relation.rb +0 -1
  157. data/lib/active_record/persistence.rb +267 -59
  158. data/lib/active_record/query_cache.rb +21 -4
  159. data/lib/active_record/querying.rb +40 -23
  160. data/lib/active_record/railtie.rb +116 -59
  161. data/lib/active_record/railties/console_sandbox.rb +2 -4
  162. data/lib/active_record/railties/controller_runtime.rb +30 -35
  163. data/lib/active_record/railties/databases.rake +411 -80
  164. data/lib/active_record/readonly_attributes.rb +4 -0
  165. data/lib/active_record/reflection.rb +109 -93
  166. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  167. data/lib/active_record/relation/batches.rb +44 -35
  168. data/lib/active_record/relation/calculations.rb +157 -90
  169. data/lib/active_record/relation/delegation.rb +35 -50
  170. data/lib/active_record/relation/finder_methods.rb +64 -39
  171. data/lib/active_record/relation/from_clause.rb +5 -1
  172. data/lib/active_record/relation/merger.rb +32 -40
  173. data/lib/active_record/relation/predicate_builder/array_handler.rb +13 -13
  174. data/lib/active_record/relation/predicate_builder/association_query_value.rb +5 -9
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +11 -10
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  179. data/lib/active_record/relation/predicate_builder.rb +62 -45
  180. data/lib/active_record/relation/query_attribute.rb +13 -8
  181. data/lib/active_record/relation/query_methods.rb +476 -187
  182. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  183. data/lib/active_record/relation/spawn_methods.rb +9 -9
  184. data/lib/active_record/relation/where_clause.rb +115 -62
  185. data/lib/active_record/relation.rb +379 -115
  186. data/lib/active_record/result.rb +64 -38
  187. data/lib/active_record/runtime_registry.rb +2 -2
  188. data/lib/active_record/sanitization.rb +22 -41
  189. data/lib/active_record/schema.rb +2 -11
  190. data/lib/active_record/schema_dumper.rb +54 -9
  191. data/lib/active_record/schema_migration.rb +7 -9
  192. data/lib/active_record/scoping/default.rb +4 -8
  193. data/lib/active_record/scoping/named.rb +17 -24
  194. data/lib/active_record/scoping.rb +8 -9
  195. data/lib/active_record/secure_token.rb +16 -8
  196. data/lib/active_record/serialization.rb +5 -3
  197. data/lib/active_record/signed_id.rb +116 -0
  198. data/lib/active_record/statement_cache.rb +49 -6
  199. data/lib/active_record/store.rb +88 -9
  200. data/lib/active_record/suppressor.rb +2 -2
  201. data/lib/active_record/table_metadata.rb +42 -43
  202. data/lib/active_record/tasks/database_tasks.rb +277 -81
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +27 -32
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
  206. data/lib/active_record/test_databases.rb +24 -0
  207. data/lib/active_record/test_fixtures.rb +287 -0
  208. data/lib/active_record/timestamp.rb +43 -32
  209. data/lib/active_record/touch_later.rb +23 -22
  210. data/lib/active_record/transactions.rb +62 -118
  211. data/lib/active_record/translation.rb +1 -1
  212. data/lib/active_record/type/adapter_specific_registry.rb +3 -13
  213. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  214. data/lib/active_record/type/serialized.rb +6 -3
  215. data/lib/active_record/type/time.rb +10 -0
  216. data/lib/active_record/type/type_map.rb +0 -1
  217. data/lib/active_record/type/unsigned_integer.rb +0 -1
  218. data/lib/active_record/type.rb +10 -5
  219. data/lib/active_record/type_caster/connection.rb +15 -15
  220. data/lib/active_record/type_caster/map.rb +8 -8
  221. data/lib/active_record/validations/associated.rb +1 -2
  222. data/lib/active_record/validations/numericality.rb +35 -0
  223. data/lib/active_record/validations/uniqueness.rb +38 -30
  224. data/lib/active_record/validations.rb +4 -3
  225. data/lib/active_record.rb +13 -12
  226. data/lib/arel/alias_predication.rb +9 -0
  227. data/lib/arel/attributes/attribute.rb +41 -0
  228. data/lib/arel/collectors/bind.rb +29 -0
  229. data/lib/arel/collectors/composite.rb +39 -0
  230. data/lib/arel/collectors/plain_string.rb +20 -0
  231. data/lib/arel/collectors/sql_string.rb +27 -0
  232. data/lib/arel/collectors/substitute_binds.rb +35 -0
  233. data/lib/arel/crud.rb +42 -0
  234. data/lib/arel/delete_manager.rb +18 -0
  235. data/lib/arel/errors.rb +9 -0
  236. data/lib/arel/expressions.rb +29 -0
  237. data/lib/arel/factory_methods.rb +49 -0
  238. data/lib/arel/insert_manager.rb +49 -0
  239. data/lib/arel/math.rb +45 -0
  240. data/lib/arel/nodes/and.rb +32 -0
  241. data/lib/arel/nodes/ascending.rb +23 -0
  242. data/lib/arel/nodes/binary.rb +126 -0
  243. data/lib/arel/nodes/bind_param.rb +44 -0
  244. data/lib/arel/nodes/case.rb +55 -0
  245. data/lib/arel/nodes/casted.rb +62 -0
  246. data/lib/arel/nodes/comment.rb +29 -0
  247. data/lib/arel/nodes/count.rb +12 -0
  248. data/lib/arel/nodes/delete_statement.rb +45 -0
  249. data/lib/arel/nodes/descending.rb +23 -0
  250. data/lib/arel/nodes/equality.rb +15 -0
  251. data/lib/arel/nodes/extract.rb +24 -0
  252. data/lib/arel/nodes/false.rb +16 -0
  253. data/lib/arel/nodes/full_outer_join.rb +8 -0
  254. data/lib/arel/nodes/function.rb +44 -0
  255. data/lib/arel/nodes/grouping.rb +11 -0
  256. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  257. data/lib/arel/nodes/in.rb +15 -0
  258. data/lib/arel/nodes/infix_operation.rb +92 -0
  259. data/lib/arel/nodes/inner_join.rb +8 -0
  260. data/lib/arel/nodes/insert_statement.rb +37 -0
  261. data/lib/arel/nodes/join_source.rb +20 -0
  262. data/lib/arel/nodes/matches.rb +18 -0
  263. data/lib/arel/nodes/named_function.rb +23 -0
  264. data/lib/arel/nodes/node.rb +51 -0
  265. data/lib/arel/nodes/node_expression.rb +13 -0
  266. data/lib/arel/nodes/ordering.rb +27 -0
  267. data/lib/arel/nodes/outer_join.rb +8 -0
  268. data/lib/arel/nodes/over.rb +15 -0
  269. data/lib/arel/nodes/regexp.rb +16 -0
  270. data/lib/arel/nodes/right_outer_join.rb +8 -0
  271. data/lib/arel/nodes/select_core.rb +67 -0
  272. data/lib/arel/nodes/select_statement.rb +41 -0
  273. data/lib/arel/nodes/sql_literal.rb +19 -0
  274. data/lib/arel/nodes/string_join.rb +11 -0
  275. data/lib/arel/nodes/table_alias.rb +31 -0
  276. data/lib/arel/nodes/terminal.rb +16 -0
  277. data/lib/arel/nodes/true.rb +16 -0
  278. data/lib/arel/nodes/unary.rb +44 -0
  279. data/lib/arel/nodes/unary_operation.rb +20 -0
  280. data/lib/arel/nodes/unqualified_column.rb +22 -0
  281. data/lib/arel/nodes/update_statement.rb +41 -0
  282. data/lib/arel/nodes/values_list.rb +9 -0
  283. data/lib/arel/nodes/window.rb +126 -0
  284. data/lib/arel/nodes/with.rb +11 -0
  285. data/lib/arel/nodes.rb +70 -0
  286. data/lib/arel/order_predications.rb +13 -0
  287. data/lib/arel/predications.rb +250 -0
  288. data/lib/arel/select_manager.rb +270 -0
  289. data/lib/arel/table.rb +118 -0
  290. data/lib/arel/tree_manager.rb +72 -0
  291. data/lib/arel/update_manager.rb +34 -0
  292. data/lib/arel/visitors/dot.rb +308 -0
  293. data/lib/arel/visitors/mysql.rb +93 -0
  294. data/lib/arel/visitors/postgresql.rb +120 -0
  295. data/lib/arel/visitors/sqlite.rb +38 -0
  296. data/lib/arel/visitors/to_sql.rb +899 -0
  297. data/lib/arel/visitors/visitor.rb +45 -0
  298. data/lib/arel/visitors.rb +13 -0
  299. data/lib/arel/window_predications.rb +9 -0
  300. data/lib/arel.rb +54 -0
  301. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  302. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -5
  303. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +3 -1
  304. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +7 -5
  305. data/lib/rails/generators/active_record/migration.rb +19 -2
  306. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  307. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  308. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  309. metadata +116 -30
  310. data/lib/active_record/attribute_decorators.rb +0 -90
  311. data/lib/active_record/collection_cache_key.rb +0 -53
  312. data/lib/active_record/connection_adapters/connection_specification.rb +0 -287
  313. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -33
  314. data/lib/active_record/define_callbacks.rb +0 -22
  315. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -19
  316. data/lib/active_record/relation/where_clause_factory.rb +0 -34
@@ -4,26 +4,26 @@ require "active_model/forbidden_attributes_protection"
4
4
 
5
5
  module ActiveRecord
6
6
  module AttributeAssignment
7
- extend ActiveSupport::Concern
8
7
  include ActiveModel::AttributeAssignment
9
8
 
10
9
  private
11
-
12
10
  def _assign_attributes(attributes)
13
- multi_parameter_attributes = {}
14
- nested_parameter_attributes = {}
11
+ multi_parameter_attributes = nested_parameter_attributes = nil
15
12
 
16
13
  attributes.each do |k, v|
17
- if k.include?("(")
18
- multi_parameter_attributes[k] = attributes.delete(k)
14
+ key = k.to_s
15
+
16
+ if key.include?("(")
17
+ (multi_parameter_attributes ||= {})[key] = v
19
18
  elsif v.is_a?(Hash)
20
- nested_parameter_attributes[k] = attributes.delete(k)
19
+ (nested_parameter_attributes ||= {})[key] = v
20
+ else
21
+ _assign_attribute(key, v)
21
22
  end
22
23
  end
23
- super(attributes)
24
24
 
25
- assign_nested_parameter_attributes(nested_parameter_attributes) unless nested_parameter_attributes.empty?
26
- assign_multiparameter_attributes(multi_parameter_attributes) unless multi_parameter_attributes.empty?
25
+ assign_nested_parameter_attributes(nested_parameter_attributes) if nested_parameter_attributes
26
+ assign_multiparameter_attributes(multi_parameter_attributes) if multi_parameter_attributes
27
27
  end
28
28
 
29
29
  # Assign any deferred nested attributes after the base attributes have been set.
@@ -46,16 +46,14 @@ module ActiveRecord
46
46
  def execute_callstack_for_multiparameter_attributes(callstack)
47
47
  errors = []
48
48
  callstack.each do |name, values_with_empty_parameters|
49
- begin
50
- if values_with_empty_parameters.each_value.all?(&:nil?)
51
- values = nil
52
- else
53
- values = values_with_empty_parameters
54
- end
55
- send("#{name}=", values)
56
- rescue => ex
57
- errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name)
49
+ if values_with_empty_parameters.each_value.all?(&:nil?)
50
+ values = nil
51
+ else
52
+ values = values_with_empty_parameters
58
53
  end
54
+ send("#{name}=", values)
55
+ rescue => ex
56
+ errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name)
59
57
  end
60
58
  unless errors.empty?
61
59
  error_descriptions = errors.map(&:message).join(",")
@@ -29,7 +29,7 @@ module ActiveRecord
29
29
  extend ActiveSupport::Concern
30
30
 
31
31
  included do
32
- attribute_method_suffix "_before_type_cast"
32
+ attribute_method_suffix "_before_type_cast", "_for_database"
33
33
  attribute_method_suffix "_came_from_user?"
34
34
  end
35
35
 
@@ -46,7 +46,10 @@ module ActiveRecord
46
46
  # task.read_attribute_before_type_cast('completed_on') # => "2012-10-21"
47
47
  # task.read_attribute_before_type_cast(:completed_on) # => "2012-10-21"
48
48
  def read_attribute_before_type_cast(attr_name)
49
- @attributes[attr_name.to_s].value_before_type_cast
49
+ name = attr_name.to_s
50
+ name = self.class.attribute_aliases[name] || name
51
+
52
+ attribute_before_type_cast(name)
50
53
  end
51
54
 
52
55
  # Returns a hash of attributes before typecasting and deserialization.
@@ -64,14 +67,17 @@ module ActiveRecord
64
67
  end
65
68
 
66
69
  private
70
+ # Dispatch target for <tt>*_before_type_cast</tt> attribute methods.
71
+ def attribute_before_type_cast(attr_name)
72
+ @attributes[attr_name].value_before_type_cast
73
+ end
67
74
 
68
- # Handle *_before_type_cast for method_missing.
69
- def attribute_before_type_cast(attribute_name)
70
- read_attribute_before_type_cast(attribute_name)
75
+ def attribute_for_database(attr_name)
76
+ @attributes[attr_name].value_for_database
71
77
  end
72
78
 
73
- def attribute_came_from_user?(attribute_name)
74
- @attributes[attribute_name].came_from_user?
79
+ def attribute_came_from_user?(attr_name)
80
+ @attributes[attr_name].came_from_user?
75
81
  end
76
82
  end
77
83
  end
@@ -29,18 +29,17 @@ module ActiveRecord
29
29
  # <tt>reload</tt> the record and clears changed attributes.
30
30
  def reload(*)
31
31
  super.tap do
32
- @previously_changed = ActiveSupport::HashWithIndifferentAccess.new
33
32
  @mutations_before_last_save = nil
34
- @attributes_changed_by_setter = ActiveSupport::HashWithIndifferentAccess.new
35
33
  @mutations_from_database = nil
36
34
  end
37
35
  end
38
36
 
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.
37
+ # Did this attribute change when we last saved?
38
+ #
39
+ # This method is useful in after callbacks to determine if an attribute
40
+ # was changed during the save that triggered the callbacks to run. It can
41
+ # be invoked as +saved_change_to_name?+ instead of
42
+ # <tt>saved_change_to_attribute?("name")</tt>.
44
43
  #
45
44
  # ==== Options
46
45
  #
@@ -50,28 +49,29 @@ module ActiveRecord
50
49
  # +to+ When passed, this method will return false unless the value was
51
50
  # changed to the given value
52
51
  def saved_change_to_attribute?(attr_name, **options)
53
- mutations_before_last_save.changed?(attr_name, **options)
52
+ mutations_before_last_save.changed?(attr_name.to_s, **options)
54
53
  end
55
54
 
56
55
  # Returns the change to an attribute during the last save. If the
57
56
  # attribute was changed, the result will be an array containing the
58
57
  # original value and the saved value.
59
58
  #
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>
59
+ # This method is useful in after callbacks, to see the change in an
60
+ # attribute during the save that triggered the callbacks to run. It can be
61
+ # invoked as +saved_change_to_name+ instead of
62
+ # <tt>saved_change_to_attribute("name")</tt>.
65
63
  def saved_change_to_attribute(attr_name)
66
- mutations_before_last_save.change_to_attribute(attr_name)
64
+ mutations_before_last_save.change_to_attribute(attr_name.to_s)
67
65
  end
68
66
 
69
67
  # 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
68
+ #
69
+ # This method is useful in after callbacks to get the original value of an
70
+ # attribute before the save that triggered the callbacks to run. It can be
71
+ # invoked as +name_before_last_save+ instead of
72
+ # <tt>attribute_before_last_save("name")</tt>.
73
73
  def attribute_before_last_save(attr_name)
74
- mutations_before_last_save.original_value(attr_name)
74
+ mutations_before_last_save.original_value(attr_name.to_s)
75
75
  end
76
76
 
77
77
  # Did the last call to +save+ have any changes to change?
@@ -84,66 +84,127 @@ module ActiveRecord
84
84
  mutations_before_last_save.changes
85
85
  end
86
86
 
87
- # Alias for +attribute_changed?+
87
+ # Will this attribute change the next time we save?
88
+ #
89
+ # This method is useful in validations and before callbacks to determine
90
+ # if the next call to +save+ will change a particular attribute. It can be
91
+ # invoked as +will_save_change_to_name?+ instead of
92
+ # <tt>will_save_change_to_attribute?("name")</tt>.
93
+ #
94
+ # ==== Options
95
+ #
96
+ # +from+ When passed, this method will return false unless the original
97
+ # value is equal to the given option
98
+ #
99
+ # +to+ When passed, this method will return false unless the value will be
100
+ # changed to the given value
88
101
  def will_save_change_to_attribute?(attr_name, **options)
89
- mutations_from_database.changed?(attr_name, **options)
102
+ mutations_from_database.changed?(attr_name.to_s, **options)
90
103
  end
91
104
 
92
- # Alias for +attribute_change+
105
+ # Returns the change to an attribute that will be persisted during the
106
+ # next save.
107
+ #
108
+ # This method is useful in validations and before callbacks, to see the
109
+ # change to an attribute that will occur when the record is saved. It can
110
+ # be invoked as +name_change_to_be_saved+ instead of
111
+ # <tt>attribute_change_to_be_saved("name")</tt>.
112
+ #
113
+ # If the attribute will change, the result will be an array containing the
114
+ # original value and the new value about to be saved.
93
115
  def attribute_change_to_be_saved(attr_name)
94
- mutations_from_database.change_to_attribute(attr_name)
116
+ mutations_from_database.change_to_attribute(attr_name.to_s)
95
117
  end
96
118
 
97
- # Alias for +attribute_was+
119
+ # Returns the value of an attribute in the database, as opposed to the
120
+ # in-memory value that will be persisted the next time the record is
121
+ # saved.
122
+ #
123
+ # This method is useful in validations and before callbacks, to see the
124
+ # original value of an attribute prior to any changes about to be
125
+ # saved. It can be invoked as +name_in_database+ instead of
126
+ # <tt>attribute_in_database("name")</tt>.
98
127
  def attribute_in_database(attr_name)
99
- mutations_from_database.original_value(attr_name)
128
+ mutations_from_database.original_value(attr_name.to_s)
100
129
  end
101
130
 
102
- # Alias for +changed?+
131
+ # Will the next call to +save+ have any changes to persist?
103
132
  def has_changes_to_save?
104
133
  mutations_from_database.any_changes?
105
134
  end
106
135
 
107
- # Alias for +changes+
136
+ # Returns a hash containing all the changes that will be persisted during
137
+ # the next save.
108
138
  def changes_to_save
109
139
  mutations_from_database.changes
110
140
  end
111
141
 
112
- # Alias for +changed+
142
+ # Returns an array of the names of any attributes that will change when
143
+ # the record is next saved.
113
144
  def changed_attribute_names_to_save
114
145
  mutations_from_database.changed_attribute_names
115
146
  end
116
147
 
117
- # Alias for +changed_attributes+
148
+ # Returns a hash of the attributes that will change when the record is
149
+ # next saved.
150
+ #
151
+ # The hash keys are the attribute names, and the hash values are the
152
+ # original attribute values in the database (as opposed to the in-memory
153
+ # values about to be saved).
118
154
  def attributes_in_database
119
155
  mutations_from_database.changed_values
120
156
  end
121
157
 
122
158
  private
123
159
  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)
160
+ result = super
161
+ clear_attribute_change(attr_name)
130
162
  result
131
163
  end
132
164
 
133
- def _update_record(*)
134
- affected_rows = partial_writes? ? super(keys_for_partial_write) : super
165
+ def _touch_row(attribute_names, time)
166
+ @_touch_attr_names = Set.new(attribute_names)
167
+
168
+ affected_rows = super
169
+
170
+ if @_skip_dirty_tracking ||= false
171
+ clear_attribute_changes(@_touch_attr_names)
172
+ return affected_rows
173
+ end
174
+
175
+ changes = {}
176
+ @attributes.keys.each do |attr_name|
177
+ next if @_touch_attr_names.include?(attr_name)
178
+
179
+ if attribute_changed?(attr_name)
180
+ changes[attr_name] = _read_attribute(attr_name)
181
+ _write_attribute(attr_name, attribute_was(attr_name))
182
+ clear_attribute_change(attr_name)
183
+ end
184
+ end
185
+
186
+ changes_applied
187
+ changes.each { |attr_name, value| _write_attribute(attr_name, value) }
188
+
189
+ affected_rows
190
+ ensure
191
+ @_touch_attr_names, @_skip_dirty_tracking = nil, nil
192
+ end
193
+
194
+ def _update_record(attribute_names = attribute_names_for_partial_writes)
195
+ affected_rows = super
135
196
  changes_applied
136
197
  affected_rows
137
198
  end
138
199
 
139
- def _create_record(*)
140
- id = partial_writes? ? super(keys_for_partial_write) : super
200
+ def _create_record(attribute_names = attribute_names_for_partial_writes)
201
+ id = super
141
202
  changes_applied
142
203
  id
143
204
  end
144
205
 
145
- def keys_for_partial_write
146
- changed_attribute_names_to_save & self.class.column_names
206
+ def attribute_names_for_partial_writes
207
+ partial_writes? ? changed_attribute_names_to_save : attribute_names
147
208
  end
148
209
  end
149
210
  end
@@ -14,51 +14,47 @@ module ActiveRecord
14
14
  [key] if key
15
15
  end
16
16
 
17
- # Returns the primary key value.
17
+ # Returns the primary key column's value.
18
18
  def id
19
- sync_with_transaction_state
20
- primary_key = self.class.primary_key
21
- _read_attribute(primary_key) if primary_key
19
+ _read_attribute(@primary_key)
22
20
  end
23
21
 
24
- # Sets the primary key value.
22
+ # Sets the primary key column's value.
25
23
  def id=(value)
26
- sync_with_transaction_state
27
- primary_key = self.class.primary_key
28
- _write_attribute(primary_key, value) if primary_key
24
+ _write_attribute(@primary_key, value)
29
25
  end
30
26
 
31
- # Queries the primary key value.
27
+ # Queries the primary key column's value.
32
28
  def id?
33
- sync_with_transaction_state
34
- query_attribute(self.class.primary_key)
29
+ query_attribute(@primary_key)
35
30
  end
36
31
 
37
- # Returns the primary key value before type cast.
32
+ # Returns the primary key column's value before type cast.
38
33
  def id_before_type_cast
39
- sync_with_transaction_state
40
- read_attribute_before_type_cast(self.class.primary_key)
34
+ attribute_before_type_cast(@primary_key)
41
35
  end
42
36
 
43
- # Returns the primary key previous value.
37
+ # Returns the primary key column's previous value.
44
38
  def id_was
45
- sync_with_transaction_state
46
- attribute_was(self.class.primary_key)
39
+ attribute_was(@primary_key)
47
40
  end
48
41
 
42
+ # Returns the primary key column's value from the database.
49
43
  def id_in_database
50
- sync_with_transaction_state
51
- attribute_in_database(self.class.primary_key)
44
+ attribute_in_database(@primary_key)
52
45
  end
53
46
 
54
- private
47
+ def id_for_database # :nodoc:
48
+ @attributes[@primary_key].value_for_database
49
+ end
55
50
 
51
+ private
56
52
  def attribute_method?(attr_name)
57
53
  attr_name == "id" || super
58
54
  end
59
55
 
60
56
  module ClassMethods
61
- ID_ATTRIBUTE_METHODS = %w(id id= id? id_before_type_cast id_was id_in_database).to_set
57
+ ID_ATTRIBUTE_METHODS = %w(id id= id? id_before_type_cast id_was id_in_database id_for_database).to_set
62
58
 
63
59
  def instance_method_already_implemented?(method_name)
64
60
  super || primary_key && ID_ATTRIBUTE_METHODS.include?(method_name)
@@ -83,7 +79,7 @@ module ActiveRecord
83
79
  end
84
80
 
85
81
  def reset_primary_key #:nodoc:
86
- if self == base_class
82
+ if base_class?
87
83
  self.primary_key = get_primary_key(base_class.name)
88
84
  else
89
85
  self.primary_key = base_class.primary_key
@@ -121,17 +117,16 @@ module ActiveRecord
121
117
  #
122
118
  # Project.primary_key # => "foo_id"
123
119
  def primary_key=(value)
124
- @primary_key = value && value.to_s
120
+ @primary_key = value && -value.to_s
125
121
  @quoted_primary_key = nil
126
122
  @attributes_builder = nil
127
123
  end
128
124
 
129
125
  private
130
-
131
126
  def suppress_composite_primary_key(pk)
132
127
  return pk unless pk.is_a?(Array)
133
128
 
134
- warn <<-WARNING.strip_heredoc
129
+ warn <<~WARNING
135
130
  WARNING: Active Record does not support composite primary key.
136
131
 
137
132
  #{table_name} has composite primary key. Composite primary key is ignored.
@@ -16,9 +16,8 @@ module ActiveRecord
16
16
  when true then true
17
17
  when false, nil then false
18
18
  else
19
- column = self.class.columns_hash[attr_name]
20
- if column.nil?
21
- if Numeric === value || value !~ /[^0-9]/
19
+ if !type_for_attribute(attr_name) { false }
20
+ if Numeric === value || !value.match?(/[^0-9]/)
22
21
  !value.to_i.zero?
23
22
  else
24
23
  return false if ActiveModel::Type::Boolean::FALSE_VALUES.include?(value)
@@ -32,11 +31,8 @@ module ActiveRecord
32
31
  end
33
32
  end
34
33
 
35
- private
36
- # Handle *? for method_missing.
37
- def attribute?(attribute_name)
38
- query_attribute(attribute_name)
39
- end
34
+ alias :attribute? :query_attribute
35
+ private :attribute?
40
36
  end
41
37
  end
42
38
  end
@@ -7,43 +7,14 @@ module ActiveRecord
7
7
 
8
8
  module ClassMethods # :nodoc:
9
9
  private
10
-
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.
29
- def define_method_attribute(name)
30
- safe_name = name.unpack("h*".freeze).first
31
- temp_method = "__temp__#{safe_name}"
32
-
33
- ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
34
- sync_with_transaction_state = "sync_with_transaction_state" if name == primary_key
35
-
36
- generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
37
- def #{temp_method}
38
- #{sync_with_transaction_state}
39
- name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
40
- _read_attribute(name) { |n| missing_attribute(n, caller) }
41
- end
42
- STR
43
-
44
- generated_attribute_methods.module_eval do
45
- alias_method name, temp_method
46
- undef_method temp_method
10
+ def define_method_attribute(name, owner:)
11
+ ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
12
+ owner, name
13
+ ) do |temp_method_name, attr_name_expr|
14
+ owner <<
15
+ "def #{temp_method_name}" <<
16
+ " _read_attribute(#{attr_name_expr}) { |n| missing_attribute(n, caller) }" <<
17
+ "end"
47
18
  end
48
19
  end
49
20
  end
@@ -52,30 +23,17 @@ module ActiveRecord
52
23
  # it has been typecast (for example, "2004-12-12" in a date column is cast
53
24
  # to a date object, like Date.new(2004, 12, 12)).
54
25
  def read_attribute(attr_name, &block)
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
26
+ name = attr_name.to_s
27
+ name = self.class.attribute_aliases[name] || name
60
28
 
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
64
- _read_attribute(name, &block)
29
+ name = @primary_key if name == "id" && @primary_key
30
+ @attributes.fetch_value(name, &block)
65
31
  end
66
32
 
67
33
  # This method exists to avoid the expensive primary_key check internally, without
68
34
  # breaking compatibility with the read_attribute API
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
35
+ def _read_attribute(attr_name, &block) # :nodoc
36
+ @attributes.fetch_value(attr_name, &block)
79
37
  end
80
38
 
81
39
  alias :attribute :_read_attribute
@@ -7,7 +7,7 @@ module ActiveRecord
7
7
 
8
8
  class ColumnNotSerializableError < StandardError
9
9
  def initialize(name, type)
10
- super <<-EOS.strip_heredoc
10
+ super <<~EOS
11
11
  Column `#{name}` of type #{type.class} does not support `serialize` feature.
12
12
  Usually it means that you are trying to use `serialize`
13
13
  on a column that already implements serialization natively.
@@ -41,6 +41,12 @@ module ActiveRecord
41
41
  # * +class_name_or_coder+ - Optional, a coder object, which responds to +.load+ and +.dump+
42
42
  # or a class name that the object type should be equal to.
43
43
  #
44
+ # ==== Options
45
+ #
46
+ # +default+ The default value to use when no value is provided. If this option
47
+ # is not passed, the previous default value (if any) will be used.
48
+ # Otherwise, the default will be +nil+.
49
+ #
44
50
  # ==== Example
45
51
  #
46
52
  # # Serialize a preferences attribute.
@@ -57,7 +63,7 @@ module ActiveRecord
57
63
  # class User < ActiveRecord::Base
58
64
  # serialize :preferences, Hash
59
65
  # end
60
- def serialize(attr_name, class_name_or_coder = Object)
66
+ def serialize(attr_name, class_name_or_coder = Object, **options)
61
67
  # When ::JSON is used, force it to go through the Active Support JSON encoder
62
68
  # to ensure special objects (e.g. Active Record models) are dumped correctly
63
69
  # using the #as_json hook.
@@ -69,17 +75,16 @@ module ActiveRecord
69
75
  Coders::YAMLColumn.new(attr_name, class_name_or_coder)
70
76
  end
71
77
 
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)
78
+ decorate_attribute_type(attr_name.to_s, **options) do |cast_type|
79
+ if type_incompatible_with_serialize?(cast_type, class_name_or_coder)
80
+ raise ColumnNotSerializableError.new(attr_name, cast_type)
75
81
  end
76
82
 
77
- Type::Serialized.new(type, coder)
83
+ Type::Serialized.new(cast_type, coder)
78
84
  end
79
85
  end
80
86
 
81
87
  private
82
-
83
88
  def type_incompatible_with_serialize?(type, class_name)
84
89
  type.is_a?(ActiveRecord::Type::Json) && class_name == ::JSON ||
85
90
  type.respond_to?(:type_cast_array, true) && class_name == ::Array
@@ -1,9 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/object/try"
4
+
3
5
  module ActiveRecord
4
6
  module AttributeMethods
5
7
  module TimeZoneConversion
6
8
  class TimeZoneConverter < DelegateClass(Type::Value) # :nodoc:
9
+ def self.new(subtype)
10
+ self === subtype ? subtype : super
11
+ end
12
+
7
13
  def deserialize(value)
8
14
  convert_time_to_time_zone(super)
9
15
  end
@@ -25,7 +31,6 @@ module ActiveRecord
25
31
  end
26
32
 
27
33
  private
28
-
29
34
  def convert_time_to_time_zone(value)
30
35
  return if value.nil?
31
36
 
@@ -63,22 +68,14 @@ module ActiveRecord
63
68
  end
64
69
 
65
70
  module ClassMethods # :nodoc:
66
- private
67
-
68
- def inherited(subclass)
69
- super
70
- # We need to apply this decorator here, rather than on module inclusion. The closure
71
- # created by the matcher would otherwise evaluate for `ActiveRecord::Base`, not the
72
- # sub class being decorated. As such, changes to `time_zone_aware_attributes`, or
73
- # `skip_time_zone_conversion_for_attributes` would not be picked up.
74
- subclass.class_eval do
75
- matcher = ->(name, type) { create_time_zone_conversion_attribute?(name, type) }
76
- decorate_matching_attribute_types(matcher, :_time_zone_conversion) do |type|
77
- TimeZoneConverter.new(type)
78
- end
79
- end
71
+ def define_attribute(name, cast_type, **)
72
+ if create_time_zone_conversion_attribute?(name, cast_type)
73
+ cast_type = TimeZoneConverter.new(cast_type)
80
74
  end
75
+ super
76
+ end
81
77
 
78
+ private
82
79
  def create_time_zone_conversion_attribute?(name, cast_type)
83
80
  enabled_for_column = time_zone_aware_attributes &&
84
81
  !skip_time_zone_conversion_for_attributes.include?(name.to_sym)