activerecord 5.2.8 → 7.0.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (364) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1393 -587
  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 +10 -9
  7. data/lib/active_record/association_relation.rb +22 -12
  8. data/lib/active_record/associations/alias_tracker.rb +19 -16
  9. data/lib/active_record/associations/association.rb +122 -47
  10. data/lib/active_record/associations/association_scope.rb +24 -24
  11. data/lib/active_record/associations/belongs_to_association.rb +67 -49
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +16 -7
  13. data/lib/active_record/associations/builder/association.rb +52 -23
  14. data/lib/active_record/associations/builder/belongs_to.rb +44 -61
  15. data/lib/active_record/associations/builder/collection_association.rb +17 -19
  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 +10 -3
  18. data/lib/active_record/associations/builder/has_one.rb +35 -3
  19. data/lib/active_record/associations/builder/singular_association.rb +5 -3
  20. data/lib/active_record/associations/collection_association.rb +59 -50
  21. data/lib/active_record/associations/collection_proxy.rb +32 -23
  22. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  23. data/lib/active_record/associations/foreign_association.rb +20 -0
  24. data/lib/active_record/associations/has_many_association.rb +27 -14
  25. data/lib/active_record/associations/has_many_through_association.rb +26 -19
  26. data/lib/active_record/associations/has_one_association.rb +52 -37
  27. data/lib/active_record/associations/has_one_through_association.rb +6 -6
  28. data/lib/active_record/associations/join_dependency/join_association.rb +44 -22
  29. data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
  30. data/lib/active_record/associations/join_dependency.rb +97 -62
  31. data/lib/active_record/associations/preloader/association.rb +220 -60
  32. data/lib/active_record/associations/preloader/batch.rb +48 -0
  33. data/lib/active_record/associations/preloader/branch.rb +147 -0
  34. data/lib/active_record/associations/preloader/through_association.rb +85 -40
  35. data/lib/active_record/associations/preloader.rb +44 -105
  36. data/lib/active_record/associations/singular_association.rb +9 -17
  37. data/lib/active_record/associations/through_association.rb +4 -4
  38. data/lib/active_record/associations.rb +207 -66
  39. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  40. data/lib/active_record/attribute_assignment.rb +17 -19
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +19 -8
  42. data/lib/active_record/attribute_methods/dirty.rb +141 -47
  43. data/lib/active_record/attribute_methods/primary_key.rb +22 -27
  44. data/lib/active_record/attribute_methods/query.rb +6 -10
  45. data/lib/active_record/attribute_methods/read.rb +15 -55
  46. data/lib/active_record/attribute_methods/serialization.rb +77 -18
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +16 -18
  48. data/lib/active_record/attribute_methods/write.rb +18 -37
  49. data/lib/active_record/attribute_methods.rb +90 -153
  50. data/lib/active_record/attributes.rb +38 -12
  51. data/lib/active_record/autosave_association.rb +50 -50
  52. data/lib/active_record/base.rb +23 -18
  53. data/lib/active_record/callbacks.rb +159 -44
  54. data/lib/active_record/coders/yaml_column.rb +12 -3
  55. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
  57. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
  58. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +92 -464
  59. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -51
  60. data/lib/active_record/connection_adapters/abstract/database_statements.rb +209 -164
  61. data/lib/active_record/connection_adapters/abstract/query_cache.rb +38 -22
  62. data/lib/active_record/connection_adapters/abstract/quoting.rb +103 -82
  63. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  64. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +140 -110
  65. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -94
  66. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +16 -5
  67. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +456 -159
  68. data/lib/active_record/connection_adapters/abstract/transaction.rb +169 -78
  69. data/lib/active_record/connection_adapters/abstract_adapter.rb +367 -162
  70. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +311 -327
  71. data/lib/active_record/connection_adapters/column.rb +33 -11
  72. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  73. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  74. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  75. data/lib/active_record/connection_adapters/mysql/database_statements.rb +113 -45
  76. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  77. data/lib/active_record/connection_adapters/mysql/quoting.rb +71 -5
  78. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +34 -10
  79. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +48 -32
  80. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +25 -8
  81. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +143 -19
  82. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
  83. data/lib/active_record/connection_adapters/mysql2_adapter.rb +63 -22
  84. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  85. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  86. data/lib/active_record/connection_adapters/postgresql/column.rb +53 -28
  87. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +56 -63
  88. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +10 -2
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +15 -2
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +54 -16
  95. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +3 -4
  97. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +3 -4
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
  101. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  102. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +26 -12
  105. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
  106. data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
  107. data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -52
  108. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -2
  109. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +39 -4
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +128 -91
  111. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -1
  112. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +149 -113
  113. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +31 -26
  114. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  115. data/lib/active_record/connection_adapters/postgresql_adapter.rb +386 -182
  116. data/lib/active_record/connection_adapters/schema_cache.rb +161 -22
  117. data/lib/active_record/connection_adapters/sql_type_metadata.rb +17 -6
  118. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +152 -0
  119. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +65 -18
  120. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  121. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +92 -26
  122. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +251 -204
  123. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  124. data/lib/active_record/connection_adapters.rb +53 -0
  125. data/lib/active_record/connection_handling.rb +292 -38
  126. data/lib/active_record/core.rb +385 -158
  127. data/lib/active_record/counter_cache.rb +8 -30
  128. data/lib/active_record/database_configurations/connection_url_resolver.rb +100 -0
  129. data/lib/active_record/database_configurations/database_config.rb +83 -0
  130. data/lib/active_record/database_configurations/hash_config.rb +154 -0
  131. data/lib/active_record/database_configurations/url_config.rb +53 -0
  132. data/lib/active_record/database_configurations.rb +256 -0
  133. data/lib/active_record/delegated_type.rb +250 -0
  134. data/lib/active_record/destroy_association_async_job.rb +36 -0
  135. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  136. data/lib/active_record/dynamic_matchers.rb +4 -5
  137. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  138. data/lib/active_record/encryption/cipher.rb +53 -0
  139. data/lib/active_record/encryption/config.rb +44 -0
  140. data/lib/active_record/encryption/configurable.rb +61 -0
  141. data/lib/active_record/encryption/context.rb +35 -0
  142. data/lib/active_record/encryption/contexts.rb +72 -0
  143. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  144. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  145. data/lib/active_record/encryption/encryptable_record.rb +208 -0
  146. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  147. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  148. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  149. data/lib/active_record/encryption/encryptor.rb +155 -0
  150. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  151. data/lib/active_record/encryption/errors.rb +15 -0
  152. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  153. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  154. data/lib/active_record/encryption/key.rb +28 -0
  155. data/lib/active_record/encryption/key_generator.rb +42 -0
  156. data/lib/active_record/encryption/key_provider.rb +46 -0
  157. data/lib/active_record/encryption/message.rb +33 -0
  158. data/lib/active_record/encryption/message_serializer.rb +90 -0
  159. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  160. data/lib/active_record/encryption/properties.rb +76 -0
  161. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  162. data/lib/active_record/encryption/scheme.rb +99 -0
  163. data/lib/active_record/encryption.rb +55 -0
  164. data/lib/active_record/enum.rb +130 -51
  165. data/lib/active_record/errors.rb +129 -23
  166. data/lib/active_record/explain.rb +10 -6
  167. data/lib/active_record/explain_registry.rb +11 -6
  168. data/lib/active_record/explain_subscriber.rb +1 -1
  169. data/lib/active_record/fixture_set/file.rb +22 -15
  170. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  171. data/lib/active_record/fixture_set/render_context.rb +17 -0
  172. data/lib/active_record/fixture_set/table_row.rb +187 -0
  173. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  174. data/lib/active_record/fixtures.rb +206 -490
  175. data/lib/active_record/future_result.rb +139 -0
  176. data/lib/active_record/gem_version.rb +3 -3
  177. data/lib/active_record/inheritance.rb +104 -37
  178. data/lib/active_record/insert_all.rb +278 -0
  179. data/lib/active_record/integration.rb +69 -18
  180. data/lib/active_record/internal_metadata.rb +24 -9
  181. data/lib/active_record/legacy_yaml_adapter.rb +3 -36
  182. data/lib/active_record/locking/optimistic.rb +41 -26
  183. data/lib/active_record/locking/pessimistic.rb +18 -8
  184. data/lib/active_record/log_subscriber.rb +46 -35
  185. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  186. data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
  187. data/lib/active_record/middleware/database_selector.rb +82 -0
  188. data/lib/active_record/middleware/shard_selector.rb +60 -0
  189. data/lib/active_record/migration/command_recorder.rb +96 -44
  190. data/lib/active_record/migration/compatibility.rb +246 -64
  191. data/lib/active_record/migration/join_table.rb +1 -2
  192. data/lib/active_record/migration.rb +266 -187
  193. data/lib/active_record/model_schema.rb +165 -52
  194. data/lib/active_record/nested_attributes.rb +17 -19
  195. data/lib/active_record/no_touching.rb +11 -4
  196. data/lib/active_record/null_relation.rb +2 -7
  197. data/lib/active_record/persistence.rb +467 -92
  198. data/lib/active_record/query_cache.rb +21 -4
  199. data/lib/active_record/query_logs.rb +138 -0
  200. data/lib/active_record/querying.rb +51 -24
  201. data/lib/active_record/railtie.rb +224 -57
  202. data/lib/active_record/railties/console_sandbox.rb +2 -4
  203. data/lib/active_record/railties/controller_runtime.rb +31 -36
  204. data/lib/active_record/railties/databases.rake +369 -101
  205. data/lib/active_record/readonly_attributes.rb +15 -0
  206. data/lib/active_record/reflection.rb +170 -137
  207. data/lib/active_record/relation/batches/batch_enumerator.rb +44 -14
  208. data/lib/active_record/relation/batches.rb +46 -37
  209. data/lib/active_record/relation/calculations.rb +168 -96
  210. data/lib/active_record/relation/delegation.rb +37 -52
  211. data/lib/active_record/relation/finder_methods.rb +79 -58
  212. data/lib/active_record/relation/from_clause.rb +5 -1
  213. data/lib/active_record/relation/merger.rb +50 -51
  214. data/lib/active_record/relation/predicate_builder/array_handler.rb +13 -13
  215. data/lib/active_record/relation/predicate_builder/association_query_value.rb +5 -9
  216. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  217. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +11 -10
  218. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  219. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  220. data/lib/active_record/relation/predicate_builder.rb +58 -46
  221. data/lib/active_record/relation/query_attribute.rb +9 -10
  222. data/lib/active_record/relation/query_methods.rb +685 -208
  223. data/lib/active_record/relation/record_fetch_warning.rb +9 -11
  224. data/lib/active_record/relation/spawn_methods.rb +10 -10
  225. data/lib/active_record/relation/where_clause.rb +108 -64
  226. data/lib/active_record/relation.rb +515 -151
  227. data/lib/active_record/result.rb +78 -42
  228. data/lib/active_record/runtime_registry.rb +9 -13
  229. data/lib/active_record/sanitization.rb +29 -44
  230. data/lib/active_record/schema.rb +37 -31
  231. data/lib/active_record/schema_dumper.rb +74 -23
  232. data/lib/active_record/schema_migration.rb +7 -9
  233. data/lib/active_record/scoping/default.rb +62 -17
  234. data/lib/active_record/scoping/named.rb +17 -32
  235. data/lib/active_record/scoping.rb +70 -41
  236. data/lib/active_record/secure_token.rb +16 -8
  237. data/lib/active_record/serialization.rb +6 -4
  238. data/lib/active_record/signed_id.rb +116 -0
  239. data/lib/active_record/statement_cache.rb +49 -6
  240. data/lib/active_record/store.rb +88 -9
  241. data/lib/active_record/suppressor.rb +13 -17
  242. data/lib/active_record/table_metadata.rb +42 -43
  243. data/lib/active_record/tasks/database_tasks.rb +352 -94
  244. data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
  245. data/lib/active_record/tasks/postgresql_database_tasks.rb +41 -39
  246. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
  247. data/lib/active_record/test_databases.rb +24 -0
  248. data/lib/active_record/test_fixtures.rb +287 -0
  249. data/lib/active_record/timestamp.rb +44 -34
  250. data/lib/active_record/touch_later.rb +23 -22
  251. data/lib/active_record/transactions.rb +67 -128
  252. data/lib/active_record/translation.rb +3 -3
  253. data/lib/active_record/type/adapter_specific_registry.rb +34 -19
  254. data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
  255. data/lib/active_record/type/internal/timezone.rb +2 -2
  256. data/lib/active_record/type/serialized.rb +7 -4
  257. data/lib/active_record/type/time.rb +10 -0
  258. data/lib/active_record/type/type_map.rb +17 -21
  259. data/lib/active_record/type/unsigned_integer.rb +0 -1
  260. data/lib/active_record/type.rb +9 -5
  261. data/lib/active_record/type_caster/connection.rb +15 -15
  262. data/lib/active_record/type_caster/map.rb +8 -8
  263. data/lib/active_record/validations/associated.rb +2 -3
  264. data/lib/active_record/validations/numericality.rb +35 -0
  265. data/lib/active_record/validations/uniqueness.rb +39 -31
  266. data/lib/active_record/validations.rb +4 -3
  267. data/lib/active_record.rb +209 -32
  268. data/lib/arel/alias_predication.rb +9 -0
  269. data/lib/arel/attributes/attribute.rb +33 -0
  270. data/lib/arel/collectors/bind.rb +29 -0
  271. data/lib/arel/collectors/composite.rb +39 -0
  272. data/lib/arel/collectors/plain_string.rb +20 -0
  273. data/lib/arel/collectors/sql_string.rb +27 -0
  274. data/lib/arel/collectors/substitute_binds.rb +35 -0
  275. data/lib/arel/crud.rb +48 -0
  276. data/lib/arel/delete_manager.rb +32 -0
  277. data/lib/arel/errors.rb +9 -0
  278. data/lib/arel/expressions.rb +29 -0
  279. data/lib/arel/factory_methods.rb +49 -0
  280. data/lib/arel/filter_predications.rb +9 -0
  281. data/lib/arel/insert_manager.rb +48 -0
  282. data/lib/arel/math.rb +45 -0
  283. data/lib/arel/nodes/and.rb +32 -0
  284. data/lib/arel/nodes/ascending.rb +23 -0
  285. data/lib/arel/nodes/binary.rb +126 -0
  286. data/lib/arel/nodes/bind_param.rb +44 -0
  287. data/lib/arel/nodes/case.rb +55 -0
  288. data/lib/arel/nodes/casted.rb +62 -0
  289. data/lib/arel/nodes/comment.rb +29 -0
  290. data/lib/arel/nodes/count.rb +12 -0
  291. data/lib/arel/nodes/delete_statement.rb +44 -0
  292. data/lib/arel/nodes/descending.rb +23 -0
  293. data/lib/arel/nodes/equality.rb +15 -0
  294. data/lib/arel/nodes/extract.rb +24 -0
  295. data/lib/arel/nodes/false.rb +16 -0
  296. data/lib/arel/nodes/filter.rb +10 -0
  297. data/lib/arel/nodes/full_outer_join.rb +8 -0
  298. data/lib/arel/nodes/function.rb +45 -0
  299. data/lib/arel/nodes/grouping.rb +11 -0
  300. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  301. data/lib/arel/nodes/in.rb +15 -0
  302. data/lib/arel/nodes/infix_operation.rb +92 -0
  303. data/lib/arel/nodes/inner_join.rb +8 -0
  304. data/lib/arel/nodes/insert_statement.rb +37 -0
  305. data/lib/arel/nodes/join_source.rb +20 -0
  306. data/lib/arel/nodes/matches.rb +18 -0
  307. data/lib/arel/nodes/named_function.rb +23 -0
  308. data/lib/arel/nodes/node.rb +51 -0
  309. data/lib/arel/nodes/node_expression.rb +13 -0
  310. data/lib/arel/nodes/ordering.rb +27 -0
  311. data/lib/arel/nodes/outer_join.rb +8 -0
  312. data/lib/arel/nodes/over.rb +15 -0
  313. data/lib/arel/nodes/regexp.rb +16 -0
  314. data/lib/arel/nodes/right_outer_join.rb +8 -0
  315. data/lib/arel/nodes/select_core.rb +67 -0
  316. data/lib/arel/nodes/select_statement.rb +41 -0
  317. data/lib/arel/nodes/sql_literal.rb +19 -0
  318. data/lib/arel/nodes/string_join.rb +11 -0
  319. data/lib/arel/nodes/table_alias.rb +31 -0
  320. data/lib/arel/nodes/terminal.rb +16 -0
  321. data/lib/arel/nodes/true.rb +16 -0
  322. data/lib/arel/nodes/unary.rb +44 -0
  323. data/lib/arel/nodes/unary_operation.rb +20 -0
  324. data/lib/arel/nodes/unqualified_column.rb +22 -0
  325. data/lib/arel/nodes/update_statement.rb +46 -0
  326. data/lib/arel/nodes/values_list.rb +9 -0
  327. data/lib/arel/nodes/window.rb +126 -0
  328. data/lib/arel/nodes/with.rb +11 -0
  329. data/lib/arel/nodes.rb +71 -0
  330. data/lib/arel/order_predications.rb +13 -0
  331. data/lib/arel/predications.rb +258 -0
  332. data/lib/arel/select_manager.rb +276 -0
  333. data/lib/arel/table.rb +117 -0
  334. data/lib/arel/tree_manager.rb +60 -0
  335. data/lib/arel/update_manager.rb +48 -0
  336. data/lib/arel/visitors/dot.rb +298 -0
  337. data/lib/arel/visitors/mysql.rb +99 -0
  338. data/lib/arel/visitors/postgresql.rb +110 -0
  339. data/lib/arel/visitors/sqlite.rb +38 -0
  340. data/lib/arel/visitors/to_sql.rb +955 -0
  341. data/lib/arel/visitors/visitor.rb +45 -0
  342. data/lib/arel/visitors.rb +13 -0
  343. data/lib/arel/window_predications.rb +9 -0
  344. data/lib/arel.rb +55 -0
  345. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  346. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  347. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -5
  348. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +3 -1
  349. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +7 -5
  350. data/lib/rails/generators/active_record/migration.rb +19 -2
  351. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  352. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  353. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  354. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  355. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  356. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  357. metadata +162 -32
  358. data/lib/active_record/attribute_decorators.rb +0 -90
  359. data/lib/active_record/collection_cache_key.rb +0 -53
  360. data/lib/active_record/connection_adapters/connection_specification.rb +0 -287
  361. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -33
  362. data/lib/active_record/define_callbacks.rb +0 -22
  363. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -19
  364. data/lib/active_record/relation/where_clause_factory.rb +0 -34
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/enumerable"
3
4
  require "active_support/core_ext/hash/indifferent_access"
4
5
  require "active_support/core_ext/string/filters"
6
+ require "active_support/parameter_filter"
5
7
  require "concurrent/map"
6
8
 
7
9
  module ActiveRecord
@@ -15,18 +17,17 @@ module ActiveRecord
15
17
  # Accepts a logger conforming to the interface of Log4r which is then
16
18
  # passed on to any new database connections made and which can be
17
19
  # retrieved on both a class and instance level by calling +logger+.
18
- mattr_accessor :logger, instance_writer: false
20
+ class_attribute :logger, instance_writer: false
19
21
 
20
22
  ##
21
23
  # :singleton-method:
22
24
  #
23
- # Specifies if the methods calling database queries should be logged below
24
- # their relevant queries. Defaults to false.
25
- mattr_accessor :verbose_query_logs, instance_writer: false, default: false
25
+ # Specifies the job used to destroy associations in the background
26
+ class_attribute :destroy_association_async_job, instance_writer: false, instance_predicate: false, default: false
26
27
 
27
28
  ##
28
29
  # Contains the database configuration - as is typically stored in config/database.yml -
29
- # as a Hash.
30
+ # as an ActiveRecord::DatabaseConfigurations object.
30
31
  #
31
32
  # For example, the following database.yml...
32
33
  #
@@ -40,110 +41,213 @@ module ActiveRecord
40
41
  #
41
42
  # ...would result in ActiveRecord::Base.configurations to look like this:
42
43
  #
43
- # {
44
- # 'development' => {
45
- # 'adapter' => 'sqlite3',
46
- # 'database' => 'db/development.sqlite3'
47
- # },
48
- # 'production' => {
49
- # 'adapter' => 'sqlite3',
50
- # 'database' => 'db/production.sqlite3'
51
- # }
52
- # }
44
+ # #<ActiveRecord::DatabaseConfigurations:0x00007fd1acbdf800 @configurations=[
45
+ # #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10 @env_name="development",
46
+ # @name="primary", @config={adapter: "sqlite3", database: "db/development.sqlite3"}>,
47
+ # #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbdea90 @env_name="production",
48
+ # @name="primary", @config={adapter: "sqlite3", database: "db/production.sqlite3"}>
49
+ # ]>
53
50
  def self.configurations=(config)
54
- @@configurations = ActiveRecord::ConnectionHandling::MergeAndResolveDefaultUrlConfig.new(config).resolve
51
+ @@configurations = ActiveRecord::DatabaseConfigurations.new(config)
55
52
  end
56
53
  self.configurations = {}
57
54
 
58
- # Returns fully resolved configurations hash
55
+ # Returns fully resolved ActiveRecord::DatabaseConfigurations object
59
56
  def self.configurations
60
57
  @@configurations
61
58
  end
62
59
 
63
60
  ##
64
61
  # :singleton-method:
65
- # Determines whether to use Time.utc (using :utc) or Time.local (using :local) when pulling
66
- # dates and times from the database. This is set to :utc by default.
67
- mattr_accessor :default_timezone, instance_writer: false, default: :utc
62
+ # Force enumeration of all columns in SELECT statements.
63
+ # e.g. `SELECT first_name, last_name FROM ...` instead of `SELECT * FROM ...`
64
+ # This avoids +PreparedStatementCacheExpired+ errors when a column is added
65
+ # to the database while the app is running.
66
+ class_attribute :enumerate_columns_in_select_statements, instance_accessor: false, default: false
68
67
 
69
- ##
70
- # :singleton-method:
71
- # Specifies the format to use when dumping the database schema with Rails'
72
- # Rakefile. If :sql, the schema is dumped as (potentially database-
73
- # specific) SQL statements. If :ruby, the schema is dumped as an
74
- # ActiveRecord::Schema file which can be loaded into any database that
75
- # supports migrations. Use :ruby if you want to have different database
76
- # adapters for, e.g., your development and test environments.
77
- mattr_accessor :schema_format, instance_writer: false, default: :ruby
78
-
79
- ##
80
- # :singleton-method:
81
- # Specifies if an error should be raised if the query has an order being
82
- # ignored when doing batch queries. Useful in applications where the
83
- # scope being ignored is error-worthy, rather than a warning.
84
- mattr_accessor :error_on_ignored_order, instance_writer: false, default: false
68
+ class_attribute :belongs_to_required_by_default, instance_accessor: false
85
69
 
86
- # :singleton-method:
87
- # Specify the behavior for unsafe raw query methods. Values are as follows
88
- # deprecated - Warnings are logged when unsafe raw SQL is passed to
89
- # query methods.
90
- # disabled - Unsafe raw SQL passed to query methods results in
91
- # UnknownAttributeReference exception.
92
- mattr_accessor :allow_unsafe_raw_sql, instance_writer: false, default: :deprecated
70
+ class_attribute :strict_loading_by_default, instance_accessor: false, default: false
93
71
 
94
- ##
95
- # :singleton-method:
96
- # Specify whether or not to use timestamps for migration versions
97
- mattr_accessor :timestamped_migrations, instance_writer: false, default: true
72
+ class_attribute :has_many_inversing, instance_accessor: false, default: false
98
73
 
99
- ##
100
- # :singleton-method:
101
- # Specify whether schema dump should happen at the end of the
102
- # db:migrate rake task. This is true by default, which is useful for the
103
- # development environment. This should ideally be false in the production
104
- # environment where dumping schema is rarely needed.
105
- mattr_accessor :dump_schema_after_migration, instance_writer: false, default: true
74
+ class_attribute :default_connection_handler, instance_writer: false
106
75
 
107
- ##
108
- # :singleton-method:
109
- # Specifies which database schemas to dump when calling db:structure:dump.
110
- # If the value is :schema_search_path (the default), any schemas listed in
111
- # schema_search_path are dumped. Use :all to dump all schemas regardless
112
- # of schema_search_path, or a string of comma separated schemas for a
113
- # custom list.
114
- mattr_accessor :dump_schemas, instance_writer: false, default: :schema_search_path
76
+ class_attribute :default_role, instance_writer: false
115
77
 
116
- ##
117
- # :singleton-method:
118
- # Specify a threshold for the size of query result sets. If the number of
119
- # records in the set exceeds the threshold, a warning is logged. This can
120
- # be used to identify queries which load thousands of records and
121
- # potentially cause memory bloat.
122
- mattr_accessor :warn_on_records_fetched_greater_than, instance_writer: false
78
+ class_attribute :default_shard, instance_writer: false
123
79
 
124
- mattr_accessor :maintain_test_schema, instance_accessor: false
80
+ class_attribute :shard_selector, instance_accessor: false, default: nil
125
81
 
126
- mattr_accessor :belongs_to_required_by_default, instance_accessor: false
82
+ def self.application_record_class? # :nodoc:
83
+ if ActiveRecord.application_record_class
84
+ self == ActiveRecord.application_record_class
85
+ else
86
+ if defined?(ApplicationRecord) && self == ApplicationRecord
87
+ true
88
+ end
89
+ end
90
+ end
127
91
 
128
- class_attribute :default_connection_handler, instance_writer: false
92
+ self.filter_attributes = []
129
93
 
130
94
  def self.connection_handler
131
- ActiveRecord::RuntimeRegistry.connection_handler || default_connection_handler
95
+ ActiveSupport::IsolatedExecutionState[:active_record_connection_handler] || default_connection_handler
132
96
  end
133
97
 
134
98
  def self.connection_handler=(handler)
135
- ActiveRecord::RuntimeRegistry.connection_handler = handler
99
+ ActiveSupport::IsolatedExecutionState[:active_record_connection_handler] = handler
136
100
  end
137
101
 
138
- self.default_connection_handler = ConnectionAdapters::ConnectionHandler.new
139
- end
102
+ def self.connection_handlers
103
+ if ActiveRecord.legacy_connection_handling
104
+ else
105
+ raise NotImplementedError, "The new connection handling does not support accessing multiple connection handlers."
106
+ end
140
107
 
141
- module ClassMethods # :nodoc:
142
- def allocate
143
- define_attribute_methods
144
- super
108
+ @@connection_handlers ||= {}
109
+ end
110
+
111
+ def self.connection_handlers=(handlers)
112
+ if ActiveRecord.legacy_connection_handling
113
+ ActiveSupport::Deprecation.warn(<<~MSG)
114
+ Using legacy connection handling is deprecated. Please set
115
+ `legacy_connection_handling` to `false` in your application.
116
+
117
+ The new connection handling does not support `connection_handlers`
118
+ getter and setter.
119
+
120
+ Read more about how to migrate at: https://guides.rubyonrails.org/active_record_multiple_databases.html#migrate-to-the-new-connection-handling
121
+ MSG
122
+ else
123
+ raise NotImplementedError, "The new connection handling does not support multiple connection handlers."
124
+ end
125
+
126
+ @@connection_handlers = handlers
127
+ end
128
+
129
+ def self.asynchronous_queries_session # :nodoc:
130
+ asynchronous_queries_tracker.current_session
131
+ end
132
+
133
+ def self.asynchronous_queries_tracker # :nodoc:
134
+ ActiveSupport::IsolatedExecutionState[:active_record_asynchronous_queries_tracker] ||= \
135
+ AsynchronousQueriesTracker.new
136
+ end
137
+
138
+ # Returns the symbol representing the current connected role.
139
+ #
140
+ # ActiveRecord::Base.connected_to(role: :writing) do
141
+ # ActiveRecord::Base.current_role #=> :writing
142
+ # end
143
+ #
144
+ # ActiveRecord::Base.connected_to(role: :reading) do
145
+ # ActiveRecord::Base.current_role #=> :reading
146
+ # end
147
+ def self.current_role
148
+ if ActiveRecord.legacy_connection_handling
149
+ connection_handlers.key(connection_handler) || default_role
150
+ else
151
+ connected_to_stack.reverse_each do |hash|
152
+ return hash[:role] if hash[:role] && hash[:klasses].include?(Base)
153
+ return hash[:role] if hash[:role] && hash[:klasses].include?(connection_class_for_self)
154
+ end
155
+
156
+ default_role
157
+ end
158
+ end
159
+
160
+ # Returns the symbol representing the current connected shard.
161
+ #
162
+ # ActiveRecord::Base.connected_to(role: :reading) do
163
+ # ActiveRecord::Base.current_shard #=> :default
164
+ # end
165
+ #
166
+ # ActiveRecord::Base.connected_to(role: :writing, shard: :one) do
167
+ # ActiveRecord::Base.current_shard #=> :one
168
+ # end
169
+ def self.current_shard
170
+ connected_to_stack.reverse_each do |hash|
171
+ return hash[:shard] if hash[:shard] && hash[:klasses].include?(Base)
172
+ return hash[:shard] if hash[:shard] && hash[:klasses].include?(connection_class_for_self)
173
+ end
174
+
175
+ default_shard
176
+ end
177
+
178
+ # Returns the symbol representing the current setting for
179
+ # preventing writes.
180
+ #
181
+ # ActiveRecord::Base.connected_to(role: :reading) do
182
+ # ActiveRecord::Base.current_preventing_writes #=> true
183
+ # end
184
+ #
185
+ # ActiveRecord::Base.connected_to(role: :writing) do
186
+ # ActiveRecord::Base.current_preventing_writes #=> false
187
+ # end
188
+ def self.current_preventing_writes
189
+ if ActiveRecord.legacy_connection_handling
190
+ connection_handler.prevent_writes
191
+ else
192
+ connected_to_stack.reverse_each do |hash|
193
+ return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(Base)
194
+ return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(connection_class_for_self)
195
+ end
196
+
197
+ false
198
+ end
199
+ end
200
+
201
+ def self.connected_to_stack # :nodoc:
202
+ if connected_to_stack = ActiveSupport::IsolatedExecutionState[:active_record_connected_to_stack]
203
+ connected_to_stack
204
+ else
205
+ connected_to_stack = Concurrent::Array.new
206
+ ActiveSupport::IsolatedExecutionState[:active_record_connected_to_stack] = connected_to_stack
207
+ connected_to_stack
208
+ end
209
+ end
210
+
211
+ def self.connection_class=(b) # :nodoc:
212
+ @connection_class = b
213
+ end
214
+
215
+ def self.connection_class # :nodoc:
216
+ @connection_class ||= false
217
+ end
218
+
219
+ def self.connection_class? # :nodoc:
220
+ self.connection_class
221
+ end
222
+
223
+ def self.connection_class_for_self # :nodoc:
224
+ klass = self
225
+
226
+ until klass == Base
227
+ break if klass.connection_class?
228
+ klass = klass.superclass
229
+ end
230
+
231
+ klass
232
+ end
233
+
234
+ self.default_connection_handler = ConnectionAdapters::ConnectionHandler.new
235
+ self.default_role = ActiveRecord.writing_role
236
+ self.default_shard = :default
237
+
238
+ def self.strict_loading_violation!(owner:, reflection:) # :nodoc:
239
+ case ActiveRecord.action_on_strict_loading_violation
240
+ when :raise
241
+ message = "`#{owner}` is marked for strict_loading. The `#{reflection.klass}` association named `:#{reflection.name}` cannot be lazily loaded."
242
+ raise ActiveRecord::StrictLoadingViolationError.new(message)
243
+ when :log
244
+ name = "strict_loading_violation.active_record"
245
+ ActiveSupport::Notifications.instrument(name, owner: owner, reflection: reflection)
246
+ end
145
247
  end
248
+ end
146
249
 
250
+ module ClassMethods
147
251
  def initialize_find_by_cache # :nodoc:
148
252
  @find_by_statement_cache = { true => Concurrent::Map.new, false => Concurrent::Map.new }
149
253
  end
@@ -151,16 +255,20 @@ module ActiveRecord
151
255
  def inherited(child_class) # :nodoc:
152
256
  # initialize cache at class definition for thread safety
153
257
  child_class.initialize_find_by_cache
258
+ unless child_class.base_class?
259
+ klass = self
260
+ until klass.base_class?
261
+ klass.initialize_find_by_cache
262
+ klass = klass.superclass
263
+ end
264
+ end
154
265
  super
155
266
  end
156
267
 
157
268
  def find(*ids) # :nodoc:
158
269
  # We don't have cache keys for this stuff yet
159
270
  return super unless ids.length == 1
160
- return super if block_given? ||
161
- primary_key.nil? ||
162
- scope_attributes? ||
163
- columns_hash.include?(inheritance_column)
271
+ return super if block_given? || primary_key.nil? || scope_attributes?
164
272
 
165
273
  id = ids.first
166
274
 
@@ -172,56 +280,86 @@ module ActiveRecord
172
280
  where(key => params.bind).limit(1)
173
281
  }
174
282
 
175
- record = statement.execute([id], connection).first
176
- unless record
177
- raise RecordNotFound.new("Couldn't find #{name} with '#{primary_key}'=#{id}",
178
- name, primary_key, id)
179
- end
180
- record
181
- rescue ::RangeError
182
- raise RecordNotFound.new("Couldn't find #{name} with an out of range value for '#{primary_key}'",
183
- name, primary_key)
283
+ statement.execute([id], connection).first ||
284
+ raise(RecordNotFound.new("Couldn't find #{name} with '#{key}'=#{id}", name, key, id))
184
285
  end
185
286
 
186
287
  def find_by(*args) # :nodoc:
187
- return super if scope_attributes? || reflect_on_all_aggregations.any? ||
188
- columns_hash.key?(inheritance_column) && base_class != self
288
+ return super if scope_attributes?
189
289
 
190
290
  hash = args.first
291
+ return super unless Hash === hash
191
292
 
192
- return super if !(Hash === hash) || hash.values.any? { |v|
193
- StatementCache.unsupported_value?(v)
194
- }
293
+ hash = hash.each_with_object({}) do |(key, value), h|
294
+ key = key.to_s
295
+ key = attribute_aliases[key] || key
195
296
 
196
- # We can't cache Post.find_by(author: david) ...yet
197
- return super unless hash.keys.all? { |k| columns_hash.has_key?(k.to_s) }
297
+ return super if reflect_on_aggregation(key)
198
298
 
199
- keys = hash.keys
299
+ reflection = _reflect_on_association(key)
300
+
301
+ if !reflection
302
+ value = value.id if value.respond_to?(:id)
303
+ elsif reflection.belongs_to? && !reflection.polymorphic?
304
+ key = reflection.join_foreign_key
305
+ pkey = reflection.join_primary_key
306
+ value = value.public_send(pkey) if value.respond_to?(pkey)
307
+ end
308
+
309
+ if !columns_hash.key?(key) || StatementCache.unsupported_value?(value)
310
+ return super
311
+ end
312
+
313
+ h[key] = value
314
+ end
200
315
 
316
+ keys = hash.keys
201
317
  statement = cached_find_by_statement(keys) { |params|
202
- wheres = keys.each_with_object({}) { |param, o|
203
- o[param] = params.bind
204
- }
318
+ wheres = keys.index_with { params.bind }
205
319
  where(wheres).limit(1)
206
320
  }
321
+
207
322
  begin
208
323
  statement.execute(hash.values, connection).first
209
324
  rescue TypeError
210
325
  raise ActiveRecord::StatementInvalid
211
- rescue ::RangeError
212
- nil
213
326
  end
214
327
  end
215
328
 
216
329
  def find_by!(*args) # :nodoc:
217
- find_by(*args) || raise(RecordNotFound.new("Couldn't find #{name}", name))
330
+ find_by(*args) || where(*args).raise_record_not_found_exception!
331
+ end
332
+
333
+ %w(
334
+ reading_role writing_role legacy_connection_handling default_timezone index_nested_attribute_errors
335
+ verbose_query_logs queues warn_on_records_fetched_greater_than maintain_test_schema
336
+ application_record_class action_on_strict_loading_violation schema_format error_on_ignored_order
337
+ timestamped_migrations dump_schema_after_migration dump_schemas suppress_multiple_database_warning
338
+ ).each do |attr|
339
+ module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
340
+ def #{attr}
341
+ ActiveSupport::Deprecation.warn(<<~MSG)
342
+ ActiveRecord::Base.#{attr} is deprecated and will be removed in Rails 7.1.
343
+ Use `ActiveRecord.#{attr}` instead.
344
+ MSG
345
+ ActiveRecord.#{attr}
346
+ end
347
+
348
+ def #{attr}=(value)
349
+ ActiveSupport::Deprecation.warn(<<~MSG)
350
+ ActiveRecord::Base.#{attr}= is deprecated and will be removed in Rails 7.1.
351
+ Use `ActiveRecord.#{attr}=` instead.
352
+ MSG
353
+ ActiveRecord.#{attr} = value
354
+ end
355
+ RUBY
218
356
  end
219
357
 
220
358
  def initialize_generated_modules # :nodoc:
221
359
  generated_association_methods
222
360
  end
223
361
 
224
- def generated_association_methods
362
+ def generated_association_methods # :nodoc:
225
363
  @generated_association_methods ||= begin
226
364
  mod = const_set(:GeneratedAssociationMethods, Module.new)
227
365
  private_constant :GeneratedAssociationMethods
@@ -231,8 +369,34 @@ module ActiveRecord
231
369
  end
232
370
  end
233
371
 
372
+ # Returns columns which shouldn't be exposed while calling +#inspect+.
373
+ def filter_attributes
374
+ if defined?(@filter_attributes)
375
+ @filter_attributes
376
+ else
377
+ superclass.filter_attributes
378
+ end
379
+ end
380
+
381
+ # Specifies columns which shouldn't be exposed while calling +#inspect+.
382
+ def filter_attributes=(filter_attributes)
383
+ @inspection_filter = nil
384
+ @filter_attributes = filter_attributes
385
+ end
386
+
387
+ def inspection_filter # :nodoc:
388
+ if defined?(@filter_attributes)
389
+ @inspection_filter ||= begin
390
+ mask = InspectionMask.new(ActiveSupport::ParameterFilter::FILTERED)
391
+ ActiveSupport::ParameterFilter.new(@filter_attributes, mask: mask)
392
+ end
393
+ else
394
+ superclass.inspection_filter
395
+ end
396
+ end
397
+
234
398
  # Returns a string like 'Post(id:integer, title:string, body:text)'
235
- def inspect
399
+ def inspect # :nodoc:
236
400
  if self == Base
237
401
  super
238
402
  elsif abstract_class?
@@ -248,22 +412,13 @@ module ActiveRecord
248
412
  end
249
413
 
250
414
  # Overwrite the default class equality method to provide support for decorated models.
251
- def ===(object)
415
+ def ===(object) # :nodoc:
252
416
  object.is_a?(self)
253
417
  end
254
418
 
255
419
  # Returns an instance of <tt>Arel::Table</tt> loaded with the current table name.
256
- #
257
- # class Post < ActiveRecord::Base
258
- # scope :published_and_commented, -> { published.and(arel_table[:comments_count].gt(0)) }
259
- # end
260
420
  def arel_table # :nodoc:
261
- @arel_table ||= Arel::Table.new(table_name, type_caster: type_caster)
262
- end
263
-
264
- def arel_attribute(name, table = arel_table) # :nodoc:
265
- name = attribute_alias(name) if attribute_alias?(name)
266
- table[name]
421
+ @arel_table ||= Arel::Table.new(table_name, klass: self)
267
422
  end
268
423
 
269
424
  def predicate_builder # :nodoc:
@@ -274,19 +429,17 @@ module ActiveRecord
274
429
  TypeCaster::Map.new(self)
275
430
  end
276
431
 
277
- private
278
-
279
- def cached_find_by_statement(key, &block)
280
- cache = @find_by_statement_cache[connection.prepared_statements]
281
- cache.compute_if_absent(key) { StatementCache.create(connection, &block) }
282
- end
432
+ def cached_find_by_statement(key, &block) # :nodoc:
433
+ cache = @find_by_statement_cache[connection.prepared_statements]
434
+ cache.compute_if_absent(key) { StatementCache.create(connection, &block) }
435
+ end
283
436
 
437
+ private
284
438
  def relation
285
439
  relation = Relation.create(self)
286
440
 
287
441
  if finder_needs_type_condition? && !ignore_default_scope?
288
442
  relation.where!(type_condition)
289
- relation.create_with!(inheritance_column.to_s => sti_name)
290
443
  else
291
444
  relation
292
445
  end
@@ -306,7 +459,7 @@ module ActiveRecord
306
459
  # # Instantiates a single new object
307
460
  # User.new(first_name: 'Jamie')
308
461
  def initialize(attributes = nil)
309
- self.class.define_attribute_methods
462
+ @new_record = true
310
463
  @attributes = self.class._default_attributes.deep_dup
311
464
 
312
465
  init_internals
@@ -332,15 +485,21 @@ module ActiveRecord
332
485
  # post = Post.allocate
333
486
  # post.init_with(coder)
334
487
  # post.title # => 'hello world'
335
- def init_with(coder)
336
- coder = LegacyYamlAdapter.convert(self.class, coder)
337
- @attributes = self.class.yaml_encoder.decode(coder)
338
-
339
- init_internals
488
+ def init_with(coder, &block)
489
+ coder = LegacyYamlAdapter.convert(coder)
490
+ attributes = self.class.yaml_encoder.decode(coder)
491
+ init_with_attributes(attributes, coder["new_record"], &block)
492
+ end
340
493
 
341
- @new_record = coder["new_record"]
494
+ ##
495
+ # Initialize an empty model object from +attributes+.
496
+ # +attributes+ should be an attributes object, and unlike the
497
+ # `initialize` method, no assignment calls are made per attribute.
498
+ def init_with_attributes(attributes, new_record = false) # :nodoc:
499
+ @new_record = new_record
500
+ @attributes = attributes
342
501
 
343
- self.class.define_attribute_methods
502
+ init_internals
344
503
 
345
504
  yield self if block_given?
346
505
 
@@ -379,14 +538,14 @@ module ActiveRecord
379
538
  ##
380
539
  def initialize_dup(other) # :nodoc:
381
540
  @attributes = @attributes.deep_dup
382
- @attributes.reset(self.class.primary_key)
541
+ @attributes.reset(@primary_key)
383
542
 
384
543
  _run_initialize_callbacks
385
544
 
386
545
  @new_record = true
546
+ @previously_new_record = false
387
547
  @destroyed = false
388
- @_start_transaction_state = {}
389
- @transaction_state = nil
548
+ @_start_transaction_state = nil
390
549
 
391
550
  super
392
551
  end
@@ -429,6 +588,8 @@ module ActiveRecord
429
588
  # Delegates to id in order to allow two records of the same type and id to work with something like:
430
589
  # [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
431
590
  def hash
591
+ id = self.id
592
+
432
593
  if id
433
594
  self.class.hash ^ id.hash
434
595
  else
@@ -458,12 +619,61 @@ module ActiveRecord
458
619
  end
459
620
  end
460
621
 
461
- # Returns +true+ if the record is read only. Records loaded through joins with piggy-back
462
- # attributes will be marked as read only since they cannot be saved.
622
+ def present? # :nodoc:
623
+ true
624
+ end
625
+
626
+ def blank? # :nodoc:
627
+ false
628
+ end
629
+
630
+ # Returns +true+ if the record is read only.
463
631
  def readonly?
464
632
  @readonly
465
633
  end
466
634
 
635
+ # Returns +true+ if the record is in strict_loading mode.
636
+ def strict_loading?
637
+ @strict_loading
638
+ end
639
+
640
+ # Sets the record to strict_loading mode. This will raise an error
641
+ # if the record tries to lazily load an association.
642
+ #
643
+ # user = User.first
644
+ # user.strict_loading! # => true
645
+ # user.comments
646
+ # => ActiveRecord::StrictLoadingViolationError
647
+ #
648
+ # === Parameters:
649
+ #
650
+ # * value - Boolean specifying whether to enable or disable strict loading.
651
+ # * mode - Symbol specifying strict loading mode. Defaults to :all. Using
652
+ # :n_plus_one_only mode will only raise an error if an association
653
+ # that will lead to an n plus one query is lazily loaded.
654
+ #
655
+ # === Example:
656
+ #
657
+ # user = User.first
658
+ # user.strict_loading!(false) # => false
659
+ # user.comments
660
+ # => #<ActiveRecord::Associations::CollectionProxy>
661
+ def strict_loading!(value = true, mode: :all)
662
+ unless [:all, :n_plus_one_only].include?(mode)
663
+ raise ArgumentError, "The :mode option must be one of [:all, :n_plus_one_only]."
664
+ end
665
+
666
+ @strict_loading_mode = mode
667
+ @strict_loading = value
668
+ end
669
+
670
+ attr_reader :strict_loading_mode
671
+
672
+ # Returns +true+ if the record uses strict_loading with +:n_plus_one_only+ mode enabled.
673
+ def strict_loading_n_plus_one_only?
674
+ @strict_loading_mode == :n_plus_one_only
675
+ end
676
+
467
677
  # Marks this record as read only.
468
678
  def readonly!
469
679
  @readonly = true
@@ -478,11 +688,11 @@ module ActiveRecord
478
688
  # We check defined?(@attributes) not to issue warnings if the object is
479
689
  # allocated but not initialized.
480
690
  inspection = if defined?(@attributes) && @attributes
481
- self.class.attribute_names.collect do |name|
482
- if has_attribute?(name)
691
+ self.class.attribute_names.filter_map do |name|
692
+ if _has_attribute?(name)
483
693
  "#{name}: #{attribute_for_inspect(name)}"
484
694
  end
485
- end.compact.join(", ")
695
+ end.join(", ")
486
696
  else
487
697
  "not initialized"
488
698
  end
@@ -496,15 +706,16 @@ module ActiveRecord
496
706
  return super if custom_inspect_method_defined?
497
707
  pp.object_address_group(self) do
498
708
  if defined?(@attributes) && @attributes
499
- column_names = self.class.column_names.select { |name| has_attribute?(name) || new_record? }
500
- pp.seplist(column_names, proc { pp.text "," }) do |column_name|
501
- column_value = read_attribute(column_name)
709
+ attr_names = self.class.attribute_names.select { |name| _has_attribute?(name) }
710
+ pp.seplist(attr_names, proc { pp.text "," }) do |attr_name|
502
711
  pp.breakable " "
503
712
  pp.group(1) do
504
- pp.text column_name
713
+ pp.text attr_name
505
714
  pp.text ":"
506
715
  pp.breakable
507
- pp.pp column_value
716
+ value = _read_attribute(attr_name)
717
+ value = inspection_filter.filter_param(attr_name, value) unless value.nil?
718
+ pp.pp value
508
719
  end
509
720
  end
510
721
  else
@@ -516,11 +727,15 @@ module ActiveRecord
516
727
 
517
728
  # Returns a hash of the given methods with their names as keys and returned values as values.
518
729
  def slice(*methods)
519
- Hash[methods.flatten.map! { |method| [method, public_send(method)] }].with_indifferent_access
730
+ methods.flatten.index_with { |method| public_send(method) }.with_indifferent_access
520
731
  end
521
732
 
522
- private
733
+ # Returns an array of the values returned by the given methods.
734
+ def values_at(*methods)
735
+ methods.flatten.map! { |method| public_send(method) }
736
+ end
523
737
 
738
+ private
524
739
  # +Array#flatten+ will call +#to_ary+ (recursively) on each of the elements of
525
740
  # the array, and then rescues from the possible +NoMethodError+. If those elements are
526
741
  # +ActiveRecord::Base+'s, then this triggers the various +method_missing+'s that we have,
@@ -535,25 +750,37 @@ module ActiveRecord
535
750
 
536
751
  def init_internals
537
752
  @readonly = false
753
+ @previously_new_record = false
538
754
  @destroyed = false
539
755
  @marked_for_destruction = false
540
756
  @destroyed_by_association = nil
541
- @new_record = true
542
- @_start_transaction_state = {}
543
- @transaction_state = nil
757
+ @_start_transaction_state = nil
758
+
759
+ klass = self.class
760
+
761
+ @primary_key = klass.primary_key
762
+ @strict_loading = klass.strict_loading_by_default
763
+ @strict_loading_mode = :all
764
+
765
+ klass.define_attribute_methods
544
766
  end
545
767
 
546
768
  def initialize_internals_callback
547
769
  end
548
770
 
549
- def thaw
550
- if frozen?
551
- @attributes = @attributes.dup
771
+ def custom_inspect_method_defined?
772
+ self.class.instance_method(:inspect).owner != ActiveRecord::Base.instance_method(:inspect).owner
773
+ end
774
+
775
+ class InspectionMask < DelegateClass(::String)
776
+ def pretty_print(pp)
777
+ pp.text __getobj__
552
778
  end
553
779
  end
780
+ private_constant :InspectionMask
554
781
 
555
- def custom_inspect_method_defined?
556
- self.class.instance_method(:inspect).owner != ActiveRecord::Base.instance_method(:inspect).owner
782
+ def inspection_filter
783
+ self.class.inspection_filter
557
784
  end
558
785
  end
559
786
  end