activerecord 5.0.7.2 → 6.1.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 (363) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +829 -2015
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +11 -9
  5. data/examples/performance.rb +31 -29
  6. data/examples/simple.rb +5 -3
  7. data/lib/active_record.rb +37 -29
  8. data/lib/active_record/aggregations.rb +249 -247
  9. data/lib/active_record/association_relation.rb +30 -18
  10. data/lib/active_record/associations.rb +1714 -1596
  11. data/lib/active_record/associations/alias_tracker.rb +36 -42
  12. data/lib/active_record/associations/association.rb +143 -68
  13. data/lib/active_record/associations/association_scope.rb +98 -94
  14. data/lib/active_record/associations/belongs_to_association.rb +76 -46
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -12
  16. data/lib/active_record/associations/builder/association.rb +27 -28
  17. data/lib/active_record/associations/builder/belongs_to.rb +52 -60
  18. data/lib/active_record/associations/builder/collection_association.rb +12 -22
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +40 -62
  20. data/lib/active_record/associations/builder/has_many.rb +10 -2
  21. data/lib/active_record/associations/builder/has_one.rb +35 -2
  22. data/lib/active_record/associations/builder/singular_association.rb +5 -1
  23. data/lib/active_record/associations/collection_association.rb +104 -259
  24. data/lib/active_record/associations/collection_proxy.rb +169 -125
  25. data/lib/active_record/associations/foreign_association.rb +22 -0
  26. data/lib/active_record/associations/has_many_association.rb +46 -31
  27. data/lib/active_record/associations/has_many_through_association.rb +66 -46
  28. data/lib/active_record/associations/has_one_association.rb +71 -52
  29. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  30. data/lib/active_record/associations/join_dependency.rb +169 -180
  31. data/lib/active_record/associations/join_dependency/join_association.rb +53 -79
  32. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  33. data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
  34. data/lib/active_record/associations/preloader.rb +97 -104
  35. data/lib/active_record/associations/preloader/association.rb +109 -97
  36. data/lib/active_record/associations/preloader/through_association.rb +77 -76
  37. data/lib/active_record/associations/singular_association.rb +12 -45
  38. data/lib/active_record/associations/through_association.rb +27 -15
  39. data/lib/active_record/attribute_assignment.rb +55 -60
  40. data/lib/active_record/attribute_methods.rb +111 -141
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -9
  42. data/lib/active_record/attribute_methods/dirty.rb +172 -112
  43. data/lib/active_record/attribute_methods/primary_key.rb +88 -91
  44. data/lib/active_record/attribute_methods/query.rb +6 -8
  45. data/lib/active_record/attribute_methods/read.rb +18 -50
  46. data/lib/active_record/attribute_methods/serialization.rb +38 -10
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -66
  48. data/lib/active_record/attribute_methods/write.rb +25 -32
  49. data/lib/active_record/attributes.rb +69 -31
  50. data/lib/active_record/autosave_association.rb +102 -66
  51. data/lib/active_record/base.rb +16 -25
  52. data/lib/active_record/callbacks.rb +202 -43
  53. data/lib/active_record/coders/json.rb +2 -0
  54. data/lib/active_record/coders/yaml_column.rb +11 -12
  55. data/lib/active_record/connection_adapters.rb +50 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +661 -375
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +14 -38
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +269 -105
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +54 -35
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +137 -93
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +155 -113
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -162
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +68 -80
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +591 -259
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +229 -91
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +392 -244
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +457 -582
  69. data/lib/active_record/connection_adapters/column.rb +55 -13
  70. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  71. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +8 -31
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +135 -49
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +24 -23
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -20
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +79 -49
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +66 -56
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +70 -36
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +268 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +20 -12
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +74 -37
  82. data/lib/active_record/connection_adapters/pool_config.rb +63 -0
  83. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  84. data/lib/active_record/connection_adapters/postgresql/column.rb +39 -28
  85. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +70 -101
  86. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +5 -3
  87. data/lib/active_record/connection_adapters/postgresql/oid.rb +26 -21
  88. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +22 -11
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +6 -5
  90. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -6
  93. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +14 -4
  95. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  96. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -4
  97. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +19 -18
  98. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  101. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -5
  104. data/lib/active_record/connection_adapters/postgresql/oid/{json.rb → oid.rb} +6 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +30 -9
  106. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -30
  107. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
  108. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  109. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +18 -4
  110. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  111. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  112. data/lib/active_record/connection_adapters/postgresql/quoting.rb +98 -38
  113. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +21 -27
  114. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +80 -0
  115. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +147 -105
  116. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +34 -32
  117. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +426 -324
  118. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +32 -23
  119. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -6
  120. data/lib/active_record/connection_adapters/postgresql_adapter.rb +418 -293
  121. data/lib/active_record/connection_adapters/schema_cache.rb +135 -18
  122. data/lib/active_record/connection_adapters/sql_type_metadata.rb +22 -7
  123. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
  124. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +3 -1
  125. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +72 -18
  126. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -6
  127. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  128. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  129. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +170 -0
  130. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +282 -290
  131. data/lib/active_record/connection_adapters/statement_pool.rb +9 -8
  132. data/lib/active_record/connection_handling.rb +287 -45
  133. data/lib/active_record/core.rb +385 -181
  134. data/lib/active_record/counter_cache.rb +60 -28
  135. data/lib/active_record/database_configurations.rb +272 -0
  136. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  137. data/lib/active_record/database_configurations/database_config.rb +80 -0
  138. data/lib/active_record/database_configurations/hash_config.rb +96 -0
  139. data/lib/active_record/database_configurations/url_config.rb +53 -0
  140. data/lib/active_record/delegated_type.rb +209 -0
  141. data/lib/active_record/destroy_association_async_job.rb +36 -0
  142. data/lib/active_record/dynamic_matchers.rb +87 -87
  143. data/lib/active_record/enum.rb +122 -47
  144. data/lib/active_record/errors.rb +153 -22
  145. data/lib/active_record/explain.rb +13 -8
  146. data/lib/active_record/explain_registry.rb +3 -1
  147. data/lib/active_record/explain_subscriber.rb +9 -4
  148. data/lib/active_record/fixture_set/file.rb +20 -22
  149. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  150. data/lib/active_record/fixture_set/render_context.rb +17 -0
  151. data/lib/active_record/fixture_set/table_row.rb +152 -0
  152. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  153. data/lib/active_record/fixtures.rb +246 -507
  154. data/lib/active_record/gem_version.rb +6 -4
  155. data/lib/active_record/inheritance.rb +168 -95
  156. data/lib/active_record/insert_all.rb +208 -0
  157. data/lib/active_record/integration.rb +114 -25
  158. data/lib/active_record/internal_metadata.rb +30 -24
  159. data/lib/active_record/legacy_yaml_adapter.rb +11 -5
  160. data/lib/active_record/locking/optimistic.rb +81 -85
  161. data/lib/active_record/locking/pessimistic.rb +22 -6
  162. data/lib/active_record/log_subscriber.rb +68 -31
  163. data/lib/active_record/middleware/database_selector.rb +77 -0
  164. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  165. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  166. data/lib/active_record/migration.rb +439 -342
  167. data/lib/active_record/migration/command_recorder.rb +152 -98
  168. data/lib/active_record/migration/compatibility.rb +229 -60
  169. data/lib/active_record/migration/join_table.rb +8 -7
  170. data/lib/active_record/model_schema.rb +230 -122
  171. data/lib/active_record/nested_attributes.rb +213 -203
  172. data/lib/active_record/no_touching.rb +11 -2
  173. data/lib/active_record/null_relation.rb +12 -34
  174. data/lib/active_record/persistence.rb +471 -97
  175. data/lib/active_record/query_cache.rb +23 -12
  176. data/lib/active_record/querying.rb +43 -25
  177. data/lib/active_record/railtie.rb +155 -43
  178. data/lib/active_record/railties/console_sandbox.rb +2 -0
  179. data/lib/active_record/railties/controller_runtime.rb +34 -33
  180. data/lib/active_record/railties/databases.rake +507 -195
  181. data/lib/active_record/readonly_attributes.rb +9 -4
  182. data/lib/active_record/reflection.rb +245 -269
  183. data/lib/active_record/relation.rb +475 -324
  184. data/lib/active_record/relation/batches.rb +125 -72
  185. data/lib/active_record/relation/batches/batch_enumerator.rb +28 -10
  186. data/lib/active_record/relation/calculations.rb +267 -171
  187. data/lib/active_record/relation/delegation.rb +73 -69
  188. data/lib/active_record/relation/finder_methods.rb +238 -248
  189. data/lib/active_record/relation/from_clause.rb +7 -9
  190. data/lib/active_record/relation/merger.rb +95 -77
  191. data/lib/active_record/relation/predicate_builder.rb +109 -110
  192. data/lib/active_record/relation/predicate_builder/array_handler.rb +22 -17
  193. data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -0
  194. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +6 -4
  195. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +55 -0
  196. data/lib/active_record/relation/predicate_builder/range_handler.rb +7 -18
  197. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  198. data/lib/active_record/relation/query_attribute.rb +33 -2
  199. data/lib/active_record/relation/query_methods.rb +654 -374
  200. data/lib/active_record/relation/record_fetch_warning.rb +8 -6
  201. data/lib/active_record/relation/spawn_methods.rb +15 -14
  202. data/lib/active_record/relation/where_clause.rb +171 -109
  203. data/lib/active_record/result.rb +88 -51
  204. data/lib/active_record/runtime_registry.rb +5 -3
  205. data/lib/active_record/sanitization.rb +73 -100
  206. data/lib/active_record/schema.rb +7 -14
  207. data/lib/active_record/schema_dumper.rb +101 -69
  208. data/lib/active_record/schema_migration.rb +16 -12
  209. data/lib/active_record/scoping.rb +20 -20
  210. data/lib/active_record/scoping/default.rb +92 -95
  211. data/lib/active_record/scoping/named.rb +39 -30
  212. data/lib/active_record/secure_token.rb +19 -9
  213. data/lib/active_record/serialization.rb +7 -3
  214. data/lib/active_record/signed_id.rb +116 -0
  215. data/lib/active_record/statement_cache.rb +80 -29
  216. data/lib/active_record/store.rb +122 -42
  217. data/lib/active_record/suppressor.rb +6 -3
  218. data/lib/active_record/table_metadata.rb +51 -39
  219. data/lib/active_record/tasks/database_tasks.rb +332 -115
  220. data/lib/active_record/tasks/mysql_database_tasks.rb +66 -104
  221. data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -56
  222. data/lib/active_record/tasks/sqlite_database_tasks.rb +40 -19
  223. data/lib/active_record/test_databases.rb +24 -0
  224. data/lib/active_record/test_fixtures.rb +246 -0
  225. data/lib/active_record/timestamp.rb +70 -38
  226. data/lib/active_record/touch_later.rb +26 -24
  227. data/lib/active_record/transactions.rb +121 -184
  228. data/lib/active_record/translation.rb +3 -1
  229. data/lib/active_record/type.rb +29 -17
  230. data/lib/active_record/type/adapter_specific_registry.rb +44 -48
  231. data/lib/active_record/type/date.rb +2 -0
  232. data/lib/active_record/type/date_time.rb +2 -0
  233. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  234. data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
  235. data/lib/active_record/type/internal/timezone.rb +2 -0
  236. data/lib/active_record/type/json.rb +30 -0
  237. data/lib/active_record/type/serialized.rb +20 -9
  238. data/lib/active_record/type/text.rb +11 -0
  239. data/lib/active_record/type/time.rb +12 -1
  240. data/lib/active_record/type/type_map.rb +14 -17
  241. data/lib/active_record/type/unsigned_integer.rb +16 -0
  242. data/lib/active_record/type_caster.rb +4 -2
  243. data/lib/active_record/type_caster/connection.rb +17 -13
  244. data/lib/active_record/type_caster/map.rb +10 -6
  245. data/lib/active_record/validations.rb +8 -5
  246. data/lib/active_record/validations/absence.rb +2 -0
  247. data/lib/active_record/validations/associated.rb +4 -3
  248. data/lib/active_record/validations/length.rb +2 -0
  249. data/lib/active_record/validations/numericality.rb +35 -0
  250. data/lib/active_record/validations/presence.rb +4 -2
  251. data/lib/active_record/validations/uniqueness.rb +52 -45
  252. data/lib/active_record/version.rb +3 -1
  253. data/lib/arel.rb +54 -0
  254. data/lib/arel/alias_predication.rb +9 -0
  255. data/lib/arel/attributes/attribute.rb +41 -0
  256. data/lib/arel/collectors/bind.rb +29 -0
  257. data/lib/arel/collectors/composite.rb +39 -0
  258. data/lib/arel/collectors/plain_string.rb +20 -0
  259. data/lib/arel/collectors/sql_string.rb +27 -0
  260. data/lib/arel/collectors/substitute_binds.rb +35 -0
  261. data/lib/arel/crud.rb +42 -0
  262. data/lib/arel/delete_manager.rb +18 -0
  263. data/lib/arel/errors.rb +9 -0
  264. data/lib/arel/expressions.rb +29 -0
  265. data/lib/arel/factory_methods.rb +49 -0
  266. data/lib/arel/insert_manager.rb +49 -0
  267. data/lib/arel/math.rb +45 -0
  268. data/lib/arel/nodes.rb +70 -0
  269. data/lib/arel/nodes/and.rb +32 -0
  270. data/lib/arel/nodes/ascending.rb +23 -0
  271. data/lib/arel/nodes/binary.rb +126 -0
  272. data/lib/arel/nodes/bind_param.rb +44 -0
  273. data/lib/arel/nodes/case.rb +55 -0
  274. data/lib/arel/nodes/casted.rb +62 -0
  275. data/lib/arel/nodes/comment.rb +29 -0
  276. data/lib/arel/nodes/count.rb +12 -0
  277. data/lib/arel/nodes/delete_statement.rb +45 -0
  278. data/lib/arel/nodes/descending.rb +23 -0
  279. data/lib/arel/nodes/equality.rb +15 -0
  280. data/lib/arel/nodes/extract.rb +24 -0
  281. data/lib/arel/nodes/false.rb +16 -0
  282. data/lib/arel/nodes/full_outer_join.rb +8 -0
  283. data/lib/arel/nodes/function.rb +44 -0
  284. data/lib/arel/nodes/grouping.rb +11 -0
  285. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  286. data/lib/arel/nodes/in.rb +15 -0
  287. data/lib/arel/nodes/infix_operation.rb +92 -0
  288. data/lib/arel/nodes/inner_join.rb +8 -0
  289. data/lib/arel/nodes/insert_statement.rb +37 -0
  290. data/lib/arel/nodes/join_source.rb +20 -0
  291. data/lib/arel/nodes/matches.rb +18 -0
  292. data/lib/arel/nodes/named_function.rb +23 -0
  293. data/lib/arel/nodes/node.rb +51 -0
  294. data/lib/arel/nodes/node_expression.rb +13 -0
  295. data/lib/arel/nodes/ordering.rb +27 -0
  296. data/lib/arel/nodes/outer_join.rb +8 -0
  297. data/lib/arel/nodes/over.rb +15 -0
  298. data/lib/arel/nodes/regexp.rb +16 -0
  299. data/lib/arel/nodes/right_outer_join.rb +8 -0
  300. data/lib/arel/nodes/select_core.rb +67 -0
  301. data/lib/arel/nodes/select_statement.rb +41 -0
  302. data/lib/arel/nodes/sql_literal.rb +19 -0
  303. data/lib/arel/nodes/string_join.rb +11 -0
  304. data/lib/arel/nodes/table_alias.rb +31 -0
  305. data/lib/arel/nodes/terminal.rb +16 -0
  306. data/lib/arel/nodes/true.rb +16 -0
  307. data/lib/arel/nodes/unary.rb +44 -0
  308. data/lib/arel/nodes/unary_operation.rb +20 -0
  309. data/lib/arel/nodes/unqualified_column.rb +22 -0
  310. data/lib/arel/nodes/update_statement.rb +41 -0
  311. data/lib/arel/nodes/values_list.rb +9 -0
  312. data/lib/arel/nodes/window.rb +126 -0
  313. data/lib/arel/nodes/with.rb +11 -0
  314. data/lib/arel/order_predications.rb +13 -0
  315. data/lib/arel/predications.rb +250 -0
  316. data/lib/arel/select_manager.rb +270 -0
  317. data/lib/arel/table.rb +118 -0
  318. data/lib/arel/tree_manager.rb +72 -0
  319. data/lib/arel/update_manager.rb +34 -0
  320. data/lib/arel/visitors.rb +13 -0
  321. data/lib/arel/visitors/dot.rb +308 -0
  322. data/lib/arel/visitors/mysql.rb +93 -0
  323. data/lib/arel/visitors/postgresql.rb +120 -0
  324. data/lib/arel/visitors/sqlite.rb +38 -0
  325. data/lib/arel/visitors/to_sql.rb +899 -0
  326. data/lib/arel/visitors/visitor.rb +45 -0
  327. data/lib/arel/window_predications.rb +9 -0
  328. data/lib/rails/generators/active_record.rb +7 -5
  329. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
  330. data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  331. data/lib/rails/generators/active_record/migration.rb +22 -3
  332. data/lib/rails/generators/active_record/migration/migration_generator.rb +38 -35
  333. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +3 -1
  334. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +7 -5
  335. data/lib/rails/generators/active_record/model/model_generator.rb +41 -25
  336. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  337. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +10 -1
  338. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  339. metadata +141 -57
  340. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  341. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  342. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  343. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  344. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  345. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  346. data/lib/active_record/associations/preloader/singular_association.rb +0 -20
  347. data/lib/active_record/attribute.rb +0 -213
  348. data/lib/active_record/attribute/user_provided_default.rb +0 -28
  349. data/lib/active_record/attribute_decorators.rb +0 -67
  350. data/lib/active_record/attribute_mutation_tracker.rb +0 -70
  351. data/lib/active_record/attribute_set.rb +0 -110
  352. data/lib/active_record/attribute_set/builder.rb +0 -132
  353. data/lib/active_record/collection_cache_key.rb +0 -50
  354. data/lib/active_record/connection_adapters/connection_specification.rb +0 -263
  355. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -22
  356. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +0 -50
  357. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  358. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  359. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -17
  360. data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
  361. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -57
  362. data/lib/active_record/relation/where_clause_factory.rb +0 -38
  363. data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  class StatementPool # :nodoc:
@@ -6,7 +8,7 @@ module ActiveRecord
6
8
  DEFAULT_STATEMENT_LIMIT = 1000
7
9
 
8
10
  def initialize(statement_limit = nil)
9
- @cache = Hash.new { |h,pid| h[pid] = {} }
11
+ @cache = Hash.new { |h, pid| h[pid] = {} }
10
12
  @statement_limit = statement_limit || DEFAULT_STATEMENT_LIMIT
11
13
  end
12
14
 
@@ -46,14 +48,13 @@ module ActiveRecord
46
48
  end
47
49
 
48
50
  private
51
+ def cache
52
+ @cache[Process.pid]
53
+ end
49
54
 
50
- def cache
51
- @cache[Process.pid]
52
- end
53
-
54
- def dealloc(stmt)
55
- raise NotImplementedError
56
- end
55
+ def dealloc(stmt)
56
+ raise NotImplementedError
57
+ end
57
58
  end
58
59
  end
59
60
  end
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionHandling
3
- RAILS_ENV = -> { (Rails.env if defined?(Rails.env)) || ENV["RAILS_ENV"] || ENV["RACK_ENV"] }
5
+ RAILS_ENV = -> { (Rails.env if defined?(Rails.env)) || ENV["RAILS_ENV"].presence || ENV["RACK_ENV"].presence }
4
6
  DEFAULT_ENV = -> { RAILS_ENV.call || "default_env" }
5
7
 
6
8
  # Establishes the connection to the database. Accepts a hash as input where
@@ -44,44 +46,226 @@ module ActiveRecord
44
46
  #
45
47
  # The exceptions AdapterNotSpecified, AdapterNotFound and +ArgumentError+
46
48
  # may be returned on an error.
47
- def establish_connection(spec = nil)
48
- raise RuntimeError, "Anonymous class is not allowed." unless name
49
+ def establish_connection(config_or_env = nil)
50
+ config_or_env ||= DEFAULT_ENV.call.to_sym
51
+ db_config, owner_name = resolve_config_for_connection(config_or_env)
52
+ connection_handler.establish_connection(db_config, owner_name: owner_name, role: current_role, shard: current_shard)
53
+ end
49
54
 
50
- spec ||= DEFAULT_ENV.call.to_sym
51
- resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new configurations
52
- # TODO: uses name on establish_connection, for backwards compatibility
53
- spec = resolver.spec(spec, self == Base ? "primary" : name)
55
+ # Connects a model to the databases specified. The +database+ keyword
56
+ # takes a hash consisting of a +role+ and a +database_key+.
57
+ #
58
+ # This will create a connection handler for switching between connections,
59
+ # look up the config hash using the +database_key+ and finally
60
+ # establishes a connection to that config.
61
+ #
62
+ # class AnimalsModel < ApplicationRecord
63
+ # self.abstract_class = true
64
+ #
65
+ # connects_to database: { writing: :primary, reading: :primary_replica }
66
+ # end
67
+ #
68
+ # +connects_to+ also supports horizontal sharding. The horizontal sharding API
69
+ # also supports read replicas. Connect a model to a list of shards like this:
70
+ #
71
+ # class AnimalsModel < ApplicationRecord
72
+ # self.abstract_class = true
73
+ #
74
+ # connects_to shards: {
75
+ # default: { writing: :primary, reading: :primary_replica },
76
+ # shard_two: { writing: :primary_shard_two, reading: :primary_shard_replica_two }
77
+ # }
78
+ # end
79
+ #
80
+ # Returns an array of database connections.
81
+ def connects_to(database: {}, shards: {})
82
+ raise NotImplementedError, "`connects_to` can only be called on ActiveRecord::Base or abstract classes" unless self == Base || abstract_class?
54
83
 
55
- unless respond_to?(spec.adapter_method)
56
- raise AdapterNotFound, "database configuration specifies nonexistent #{spec.config[:adapter]} adapter"
84
+ if database.present? && shards.present?
85
+ raise ArgumentError, "`connects_to` can only accept a `database` or `shards` argument, but not both arguments."
57
86
  end
58
87
 
59
- remove_connection(spec.name)
60
- self.connection_specification_name = spec.name
61
- connection_handler.establish_connection spec
88
+ connections = []
89
+
90
+ database.each do |role, database_key|
91
+ db_config, owner_name = resolve_config_for_connection(database_key)
92
+ handler = lookup_connection_handler(role.to_sym)
93
+
94
+ connections << handler.establish_connection(db_config, owner_name: owner_name, role: role)
95
+ end
96
+
97
+ shards.each do |shard, database_keys|
98
+ database_keys.each do |role, database_key|
99
+ db_config, owner_name = resolve_config_for_connection(database_key)
100
+ handler = lookup_connection_handler(role.to_sym)
101
+
102
+ connections << handler.establish_connection(db_config, owner_name: owner_name, role: role, shard: shard.to_sym)
103
+ end
104
+ end
105
+
106
+ connections
62
107
  end
63
108
 
64
- class MergeAndResolveDefaultUrlConfig # :nodoc:
65
- def initialize(raw_configurations)
66
- @raw_config = raw_configurations.dup
67
- @env = DEFAULT_ENV.call.to_s
109
+ # Connects to a role (ex writing, reading or a custom role) and/or
110
+ # shard for the duration of the block. At the end of the block the
111
+ # connection will be returned to the original role / shard.
112
+ #
113
+ # If only a role is passed, Active Record will look up the connection
114
+ # based on the requested role. If a non-established role is requested
115
+ # an `ActiveRecord::ConnectionNotEstablished` error will be raised:
116
+ #
117
+ # ActiveRecord::Base.connected_to(role: :writing) do
118
+ # Dog.create! # creates dog using dog writing connection
119
+ # end
120
+ #
121
+ # ActiveRecord::Base.connected_to(role: :reading) do
122
+ # Dog.create! # throws exception because we're on a replica
123
+ # end
124
+ #
125
+ # When swapping to a shard, the role must be passed as well. If a non-existent
126
+ # shard is passed, an `ActiveRecord::ConnectionNotEstablished` error will be
127
+ # raised.
128
+ #
129
+ # When a shard and role is passed, Active Record will first lookup the role,
130
+ # and then look up the connection by shard key.
131
+ #
132
+ # ActiveRecord::Base.connected_to(role: :reading, shard: :shard_one_replica) do
133
+ # Dog.first # finds first Dog record stored on the shard one replica
134
+ # end
135
+ #
136
+ # The database kwarg is deprecated and will be removed in 6.2.0 without replacement.
137
+ def connected_to(database: nil, role: nil, shard: nil, prevent_writes: false, &blk)
138
+ if legacy_connection_handling
139
+ if self != Base
140
+ raise NotImplementedError, "`connected_to` can only be called on ActiveRecord::Base with legacy connection handling."
141
+ end
142
+ else
143
+ if self != Base && !abstract_class
144
+ raise NotImplementedError, "calling `connected_to` is only allowed on ActiveRecord::Base or abstract classes."
145
+ end
146
+ end
147
+
148
+ if database && (role || shard)
149
+ raise ArgumentError, "`connected_to` cannot accept a `database` argument with any other arguments."
150
+ elsif database
151
+ ActiveSupport::Deprecation.warn("The database key in `connected_to` is deprecated. It will be removed in Rails 6.2.0 without replacement.")
152
+
153
+ if database.is_a?(Hash)
154
+ role, database = database.first
155
+ role = role.to_sym
156
+ end
157
+
158
+ db_config, owner_name = resolve_config_for_connection(database)
159
+ handler = lookup_connection_handler(role)
160
+
161
+ handler.establish_connection(db_config, owner_name: owner_name, role: role)
162
+
163
+ with_handler(role, &blk)
164
+ elsif role || shard
165
+ unless role
166
+ raise ArgumentError, "`connected_to` cannot accept a `shard` argument without a `role`."
167
+ end
168
+
169
+ with_role_and_shard(role, shard, prevent_writes, &blk)
170
+ else
171
+ raise ArgumentError, "must provide a `shard` and/or `role`."
68
172
  end
173
+ end
69
174
 
70
- # Returns fully resolved connection hashes.
71
- # Merges connection information from `ENV['DATABASE_URL']` if available.
72
- def resolve
73
- ConnectionAdapters::ConnectionSpecification::Resolver.new(config).resolve_all
175
+ # Connects a role and/or shard to the provided connection names. Optionally `prevent_writes`
176
+ # can be passed to block writes on a connection. `reading` will automatically set
177
+ # `prevent_writes` to true.
178
+ #
179
+ # `connected_to_many` is an alternative to deeply nested `connected_to` blocks.
180
+ #
181
+ # Usage:
182
+ #
183
+ # ActiveRecord::Base.connected_to(AnimalsRecord, MealsRecord], role: :reading) do
184
+ # Dog.first # Read from animals replica
185
+ # Dinner.first # Read from meals replica
186
+ # Person.first # Read from primary writer
187
+ # end
188
+ def connected_to_many(classes, role:, shard: nil, prevent_writes: false)
189
+ if legacy_connection_handling
190
+ raise NotImplementedError, "connected_to_many is not available with legacy connection handling"
74
191
  end
75
192
 
76
- private
77
- def config
78
- @raw_config.dup.tap do |cfg|
79
- if url = ENV['DATABASE_URL']
80
- cfg[@env] ||= {}
81
- cfg[@env]["url"] ||= url
82
- end
83
- end
193
+ if self != Base || classes.include?(Base)
194
+ raise NotImplementedError, "connected_to_many can only be called on ActiveRecord::Base."
195
+ end
196
+
197
+ prevent_writes = true if role == reading_role
198
+
199
+ connected_to_stack << { role: role, shard: shard, prevent_writes: prevent_writes, klasses: classes }
200
+ yield
201
+ ensure
202
+ connected_to_stack.pop
203
+ end
204
+
205
+ # Use a specified connection.
206
+ #
207
+ # This method is useful for ensuring that a specific connection is
208
+ # being used. For example, when booting a console in readonly mode.
209
+ #
210
+ # It is not recommended to use this method in a request since it
211
+ # does not yield to a block like `connected_to`.
212
+ def connecting_to(role: default_role, shard: default_shard, prevent_writes: false)
213
+ if legacy_connection_handling
214
+ raise NotImplementedError, "`connecting_to` is not available with `legacy_connection_handling`."
215
+ end
216
+
217
+ prevent_writes = true if role == reading_role
218
+
219
+ self.connected_to_stack << { role: role, shard: shard, prevent_writes: prevent_writes, klasses: [self] }
220
+ end
221
+
222
+ # Prevent writing to the database regardless of role.
223
+ #
224
+ # In some cases you may want to prevent writes to the database
225
+ # even if you are on a database that can write. `while_preventing_writes`
226
+ # will prevent writes to the database for the duration of the block.
227
+ #
228
+ # This method does not provide the same protection as a readonly
229
+ # user and is meant to be a safeguard against accidental writes.
230
+ #
231
+ # See `READ_QUERY` for the queries that are blocked by this
232
+ # method.
233
+ def while_preventing_writes(enabled = true, &block)
234
+ if legacy_connection_handling
235
+ connection_handler.while_preventing_writes(enabled, &block)
236
+ else
237
+ connected_to(role: current_role, prevent_writes: enabled, &block)
238
+ end
239
+ end
240
+
241
+ # Returns true if role is the current connected role.
242
+ #
243
+ # ActiveRecord::Base.connected_to(role: :writing) do
244
+ # ActiveRecord::Base.connected_to?(role: :writing) #=> true
245
+ # ActiveRecord::Base.connected_to?(role: :reading) #=> false
246
+ # end
247
+ def connected_to?(role:, shard: ActiveRecord::Base.default_shard)
248
+ current_role == role.to_sym && current_shard == shard.to_sym
249
+ end
250
+
251
+ def lookup_connection_handler(handler_key) # :nodoc:
252
+ if ActiveRecord::Base.legacy_connection_handling
253
+ handler_key ||= ActiveRecord::Base.writing_role
254
+ connection_handlers[handler_key] ||= ActiveRecord::ConnectionAdapters::ConnectionHandler.new
255
+ else
256
+ ActiveRecord::Base.connection_handler
257
+ end
258
+ end
259
+
260
+ # Clears the query cache for all connections associated with the current thread.
261
+ def clear_query_caches_for_current_thread
262
+ if ActiveRecord::Base.legacy_connection_handling
263
+ ActiveRecord::Base.connection_handlers.each_value do |handler|
264
+ clear_on_handler(handler)
84
265
  end
266
+ else
267
+ clear_on_handler(ActiveRecord::Base.connection_handler)
268
+ end
85
269
  end
86
270
 
87
271
  # Returns the connection currently associated with the class. This can
@@ -93,21 +277,16 @@ module ActiveRecord
93
277
 
94
278
  attr_writer :connection_specification_name
95
279
 
96
- # Return the specification id from this class otherwise look it up
97
- # in the parent.
280
+ # Return the connection specification name from the current class or its parent.
98
281
  def connection_specification_name
99
282
  if !defined?(@connection_specification_name) || @connection_specification_name.nil?
100
- return self == Base ? "primary" : superclass.connection_specification_name
283
+ return self == Base ? Base.name : superclass.connection_specification_name
101
284
  end
102
285
  @connection_specification_name
103
286
  end
104
287
 
105
- def connection_id
106
- ActiveRecord::RuntimeRegistry.connection_id ||= Thread.current.object_id
107
- end
108
-
109
- def connection_id=(connection_id)
110
- ActiveRecord::RuntimeRegistry.connection_id = connection_id
288
+ def primary_class? # :nodoc:
289
+ self == Base || defined?(ApplicationRecord) && self == ApplicationRecord
111
290
  end
112
291
 
113
292
  # Returns the configuration of the associated connection as a hash:
@@ -117,32 +296,44 @@ module ActiveRecord
117
296
  #
118
297
  # Please use only for reading.
119
298
  def connection_config
120
- connection_pool.spec.config
299
+ connection_pool.db_config.configuration_hash
300
+ end
301
+ deprecate connection_config: "Use connection_db_config instead"
302
+
303
+ # Returns the db_config object from the associated connection:
304
+ #
305
+ # ActiveRecord::Base.connection_db_config
306
+ # #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10 @env_name="development",
307
+ # @name="primary", @config={pool: 5, timeout: 5000, database: "db/development.sqlite3", adapter: "sqlite3"}>
308
+ #
309
+ # Use only for reading.
310
+ def connection_db_config
311
+ connection_pool.db_config
121
312
  end
122
313
 
123
314
  def connection_pool
124
- connection_handler.retrieve_connection_pool(connection_specification_name) or raise ConnectionNotEstablished
315
+ connection_handler.retrieve_connection_pool(connection_specification_name, role: current_role, shard: current_shard) || raise(ConnectionNotEstablished)
125
316
  end
126
317
 
127
318
  def retrieve_connection
128
- connection_handler.retrieve_connection(connection_specification_name)
319
+ connection_handler.retrieve_connection(connection_specification_name, role: current_role, shard: current_shard)
129
320
  end
130
321
 
131
322
  # Returns +true+ if Active Record is connected.
132
323
  def connected?
133
- connection_handler.connected?(connection_specification_name)
324
+ connection_handler.connected?(connection_specification_name, role: current_role, shard: current_shard)
134
325
  end
135
326
 
136
327
  def remove_connection(name = nil)
137
328
  name ||= @connection_specification_name if defined?(@connection_specification_name)
138
- # if removing a connection that have a pool, we reset the
329
+ # if removing a connection that has a pool, we reset the
139
330
  # connection_specification_name so it will use the parent
140
331
  # pool.
141
- if connection_handler.retrieve_connection_pool(name)
332
+ if connection_handler.retrieve_connection_pool(name, role: current_role, shard: current_shard)
142
333
  self.connection_specification_name = nil
143
334
  end
144
335
 
145
- connection_handler.remove_connection(name)
336
+ connection_handler.remove_connection_pool(name, role: current_role, shard: current_shard)
146
337
  end
147
338
 
148
339
  def clear_cache! # :nodoc:
@@ -150,6 +341,57 @@ module ActiveRecord
150
341
  end
151
342
 
152
343
  delegate :clear_active_connections!, :clear_reloadable_connections!,
153
- :clear_all_connections!, :to => :connection_handler
344
+ :clear_all_connections!, :flush_idle_connections!, to: :connection_handler
345
+
346
+ private
347
+ def clear_on_handler(handler)
348
+ handler.all_connection_pools.each do |pool|
349
+ pool.connection.clear_query_cache if pool.active_connection?
350
+ end
351
+ end
352
+
353
+ def resolve_config_for_connection(config_or_env)
354
+ raise "Anonymous class is not allowed." unless name
355
+
356
+ owner_name = primary_class? ? Base.name : name
357
+ self.connection_specification_name = owner_name
358
+
359
+ db_config = Base.configurations.resolve(config_or_env)
360
+ [db_config, owner_name]
361
+ end
362
+
363
+ def with_handler(handler_key, &blk)
364
+ handler = lookup_connection_handler(handler_key)
365
+ swap_connection_handler(handler, &blk)
366
+ end
367
+
368
+ def with_role_and_shard(role, shard, prevent_writes)
369
+ prevent_writes = true if role == reading_role
370
+
371
+ if ActiveRecord::Base.legacy_connection_handling
372
+ with_handler(role.to_sym) do
373
+ connection_handler.while_preventing_writes(prevent_writes) do
374
+ self.connected_to_stack << { shard: shard, klasses: [self] }
375
+ yield
376
+ end
377
+ end
378
+ else
379
+ self.connected_to_stack << { role: role, shard: shard, prevent_writes: prevent_writes, klasses: [self] }
380
+ return_value = yield
381
+ return_value.load if return_value.is_a? ActiveRecord::Relation
382
+ return_value
383
+ end
384
+ ensure
385
+ self.connected_to_stack.pop
386
+ end
387
+
388
+ def swap_connection_handler(handler, &blk) # :nodoc:
389
+ old_handler, ActiveRecord::Base.connection_handler = ActiveRecord::Base.connection_handler, handler
390
+ return_value = yield
391
+ return_value.load if return_value.is_a? ActiveRecord::Relation
392
+ return_value
393
+ ensure
394
+ ActiveRecord::Base.connection_handler = old_handler
395
+ end
154
396
  end
155
397
  end
@@ -1,7 +1,10 @@
1
- require 'thread'
2
- require 'active_support/core_ext/hash/indifferent_access'
3
- require 'active_support/core_ext/object/duplicable'
4
- require 'active_support/core_ext/string/filters'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/enumerable"
4
+ require "active_support/core_ext/hash/indifferent_access"
5
+ require "active_support/core_ext/string/filters"
6
+ require "active_support/parameter_filter"
7
+ require "concurrent/map"
5
8
 
6
9
  module ActiveRecord
7
10
  module Core
@@ -16,9 +19,28 @@ module ActiveRecord
16
19
  # retrieved on both a class and instance level by calling +logger+.
17
20
  mattr_accessor :logger, instance_writer: false
18
21
 
22
+ ##
23
+ # :singleton-method:
24
+ #
25
+ # Specifies if the methods calling database queries should be logged below
26
+ # their relevant queries. Defaults to false.
27
+ mattr_accessor :verbose_query_logs, instance_writer: false, default: false
28
+
29
+ ##
30
+ # :singleton-method:
31
+ #
32
+ # Specifies the names of the queues used by background jobs.
33
+ mattr_accessor :queues, instance_accessor: false, default: {}
34
+
35
+ ##
36
+ # :singleton-method:
37
+ #
38
+ # Specifies the job used to destroy associations in the background
39
+ class_attribute :destroy_association_async_job, instance_writer: false, instance_predicate: false, default: false
40
+
19
41
  ##
20
42
  # Contains the database configuration - as is typically stored in config/database.yml -
21
- # as a Hash.
43
+ # as an ActiveRecord::DatabaseConfigurations object.
22
44
  #
23
45
  # For example, the following database.yml...
24
46
  #
@@ -32,22 +54,18 @@ module ActiveRecord
32
54
  #
33
55
  # ...would result in ActiveRecord::Base.configurations to look like this:
34
56
  #
35
- # {
36
- # 'development' => {
37
- # 'adapter' => 'sqlite3',
38
- # 'database' => 'db/development.sqlite3'
39
- # },
40
- # 'production' => {
41
- # 'adapter' => 'sqlite3',
42
- # 'database' => 'db/production.sqlite3'
43
- # }
44
- # }
57
+ # #<ActiveRecord::DatabaseConfigurations:0x00007fd1acbdf800 @configurations=[
58
+ # #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10 @env_name="development",
59
+ # @name="primary", @config={adapter: "sqlite3", database: "db/development.sqlite3"}>,
60
+ # #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbdea90 @env_name="production",
61
+ # @name="primary", @config={adapter: "sqlite3", database: "db/production.sqlite3"}>
62
+ # ]>
45
63
  def self.configurations=(config)
46
- @@configurations = ActiveRecord::ConnectionHandling::MergeAndResolveDefaultUrlConfig.new(config).resolve
64
+ @@configurations = ActiveRecord::DatabaseConfigurations.new(config)
47
65
  end
48
66
  self.configurations = {}
49
67
 
50
- # Returns fully resolved configurations hash
68
+ # Returns fully resolved ActiveRecord::DatabaseConfigurations object
51
69
  def self.configurations
52
70
  @@configurations
53
71
  end
@@ -56,8 +74,7 @@ module ActiveRecord
56
74
  # :singleton-method:
57
75
  # Determines whether to use Time.utc (using :utc) or Time.local (using :local) when pulling
58
76
  # dates and times from the database. This is set to :utc by default.
59
- mattr_accessor :default_timezone, instance_writer: false
60
- self.default_timezone = :utc
77
+ mattr_accessor :default_timezone, instance_writer: false, default: :utc
61
78
 
62
79
  ##
63
80
  # :singleton-method:
@@ -67,41 +84,36 @@ module ActiveRecord
67
84
  # ActiveRecord::Schema file which can be loaded into any database that
68
85
  # supports migrations. Use :ruby if you want to have different database
69
86
  # adapters for, e.g., your development and test environments.
70
- mattr_accessor :schema_format, instance_writer: false
71
- self.schema_format = :ruby
87
+ mattr_accessor :schema_format, instance_writer: false, default: :ruby
72
88
 
73
89
  ##
74
90
  # :singleton-method:
75
- # Specifies if an error should be raised on query limit or order being
91
+ # Specifies if an error should be raised if the query has an order being
76
92
  # ignored when doing batch queries. Useful in applications where the
77
- # limit or scope being ignored is error-worthy, rather than a warning.
78
- mattr_accessor :error_on_ignored_order_or_limit, instance_writer: false
79
- self.error_on_ignored_order_or_limit = false
93
+ # scope being ignored is error-worthy, rather than a warning.
94
+ mattr_accessor :error_on_ignored_order, instance_writer: false, default: false
80
95
 
81
96
  ##
82
97
  # :singleton-method:
83
98
  # Specify whether or not to use timestamps for migration versions
84
- mattr_accessor :timestamped_migrations, instance_writer: false
85
- self.timestamped_migrations = true
99
+ mattr_accessor :timestamped_migrations, instance_writer: false, default: true
86
100
 
87
101
  ##
88
102
  # :singleton-method:
89
103
  # Specify whether schema dump should happen at the end of the
90
- # db:migrate rake task. This is true by default, which is useful for the
104
+ # db:migrate rails command. This is true by default, which is useful for the
91
105
  # development environment. This should ideally be false in the production
92
106
  # environment where dumping schema is rarely needed.
93
- mattr_accessor :dump_schema_after_migration, instance_writer: false
94
- self.dump_schema_after_migration = true
107
+ mattr_accessor :dump_schema_after_migration, instance_writer: false, default: true
95
108
 
96
109
  ##
97
110
  # :singleton-method:
98
- # Specifies which database schemas to dump when calling db:structure:dump.
111
+ # Specifies which database schemas to dump when calling db:schema:dump.
99
112
  # If the value is :schema_search_path (the default), any schemas listed in
100
113
  # schema_search_path are dumped. Use :all to dump all schemas regardless
101
114
  # of schema_search_path, or a string of comma separated schemas for a
102
115
  # custom list.
103
- mattr_accessor :dump_schemas, instance_writer: false
104
- self.dump_schemas = :schema_search_path
116
+ mattr_accessor :dump_schemas, instance_writer: false, default: :schema_search_path
105
117
 
106
118
  ##
107
119
  # :singleton-method:
@@ -110,122 +122,278 @@ module ActiveRecord
110
122
  # be used to identify queries which load thousands of records and
111
123
  # potentially cause memory bloat.
112
124
  mattr_accessor :warn_on_records_fetched_greater_than, instance_writer: false
113
- self.warn_on_records_fetched_greater_than = nil
125
+
126
+ ##
127
+ # :singleton-method:
128
+ # Show a warning when Rails couldn't parse your database.yml
129
+ # for multiple databases.
130
+ mattr_accessor :suppress_multiple_database_warning, instance_writer: false, default: false
114
131
 
115
132
  mattr_accessor :maintain_test_schema, instance_accessor: false
116
133
 
117
- mattr_accessor :belongs_to_required_by_default, instance_accessor: false
134
+ class_attribute :belongs_to_required_by_default, instance_accessor: false
135
+
136
+ ##
137
+ # :singleton-method:
138
+ # Set the application to log or raise when an association violates strict loading.
139
+ # Defaults to :raise.
140
+ mattr_accessor :action_on_strict_loading_violation, instance_accessor: false, default: :raise
141
+
142
+ class_attribute :strict_loading_by_default, instance_accessor: false, default: false
143
+
144
+ mattr_accessor :writing_role, instance_accessor: false, default: :writing
145
+
146
+ mattr_accessor :reading_role, instance_accessor: false, default: :reading
147
+
148
+ mattr_accessor :has_many_inversing, instance_accessor: false, default: false
118
149
 
119
150
  class_attribute :default_connection_handler, instance_writer: false
120
151
 
152
+ class_attribute :default_role, instance_writer: false
153
+
154
+ class_attribute :default_shard, instance_writer: false
155
+
156
+ mattr_accessor :legacy_connection_handling, instance_writer: false, default: true
157
+
158
+ self.filter_attributes = []
159
+
121
160
  def self.connection_handler
122
- ActiveRecord::RuntimeRegistry.connection_handler || default_connection_handler
161
+ Thread.current.thread_variable_get(:ar_connection_handler) || default_connection_handler
123
162
  end
124
163
 
125
164
  def self.connection_handler=(handler)
126
- ActiveRecord::RuntimeRegistry.connection_handler = handler
165
+ Thread.current.thread_variable_set(:ar_connection_handler, handler)
166
+ end
167
+
168
+ def self.connection_handlers
169
+ unless legacy_connection_handling
170
+ raise NotImplementedError, "The new connection handling does not support accessing multiple connection handlers."
171
+ end
172
+
173
+ @@connection_handlers ||= {}
174
+ end
175
+
176
+ def self.connection_handlers=(handlers)
177
+ unless legacy_connection_handling
178
+ raise NotImplementedError, "The new connection handling does not setting support multiple connection handlers."
179
+ end
180
+
181
+ @@connection_handlers = handlers
182
+ end
183
+
184
+ # Returns the symbol representing the current connected role.
185
+ #
186
+ # ActiveRecord::Base.connected_to(role: :writing) do
187
+ # ActiveRecord::Base.current_role #=> :writing
188
+ # end
189
+ #
190
+ # ActiveRecord::Base.connected_to(role: :reading) do
191
+ # ActiveRecord::Base.current_role #=> :reading
192
+ # end
193
+ def self.current_role
194
+ if ActiveRecord::Base.legacy_connection_handling
195
+ connection_handlers.key(connection_handler) || default_role
196
+ else
197
+ connected_to_stack.reverse_each do |hash|
198
+ return hash[:role] if hash[:role] && hash[:klasses].include?(Base)
199
+ return hash[:role] if hash[:role] && hash[:klasses].include?(abstract_base_class)
200
+ end
201
+
202
+ default_role
203
+ end
204
+ end
205
+
206
+ # Returns the symbol representing the current connected shard.
207
+ #
208
+ # ActiveRecord::Base.connected_to(role: :reading) do
209
+ # ActiveRecord::Base.current_shard #=> :default
210
+ # end
211
+ #
212
+ # ActiveRecord::Base.connected_to(role: :writing, shard: :one) do
213
+ # ActiveRecord::Base.current_shard #=> :one
214
+ # end
215
+ def self.current_shard
216
+ connected_to_stack.reverse_each do |hash|
217
+ return hash[:shard] if hash[:shard] && hash[:klasses].include?(Base)
218
+ return hash[:shard] if hash[:shard] && hash[:klasses].include?(abstract_base_class)
219
+ end
220
+
221
+ default_shard
222
+ end
223
+
224
+ # Returns the symbol representing the current setting for
225
+ # preventing writes.
226
+ #
227
+ # ActiveRecord::Base.connected_to(role: :reading) do
228
+ # ActiveRecord::Base.current_preventing_writes #=> true
229
+ # end
230
+ #
231
+ # ActiveRecord::Base.connected_to(role: :writing) do
232
+ # ActiveRecord::Base.current_preventing_writes #=> false
233
+ # end
234
+ def self.current_preventing_writes
235
+ if legacy_connection_handling
236
+ connection_handler.prevent_writes
237
+ else
238
+ connected_to_stack.reverse_each do |hash|
239
+ return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(Base)
240
+ return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(abstract_base_class)
241
+ end
242
+
243
+ false
244
+ end
245
+ end
246
+
247
+ def self.connected_to_stack # :nodoc:
248
+ if connected_to_stack = Thread.current.thread_variable_get(:ar_connected_to_stack)
249
+ connected_to_stack
250
+ else
251
+ connected_to_stack = Concurrent::Array.new
252
+ Thread.current.thread_variable_set(:ar_connected_to_stack, connected_to_stack)
253
+ connected_to_stack
254
+ end
255
+ end
256
+
257
+ def self.abstract_base_class # :nodoc:
258
+ klass = self
259
+
260
+ until klass == Base
261
+ break if klass.abstract_class?
262
+ klass = klass.superclass
263
+ end
264
+
265
+ klass
266
+ end
267
+
268
+ def self.allow_unsafe_raw_sql # :nodoc:
269
+ ActiveSupport::Deprecation.warn("ActiveRecord::Base.allow_unsafe_raw_sql is deprecated and will be removed in Rails 6.2")
270
+ end
271
+
272
+ def self.allow_unsafe_raw_sql=(value) # :nodoc:
273
+ ActiveSupport::Deprecation.warn("ActiveRecord::Base.allow_unsafe_raw_sql= is deprecated and will be removed in Rails 6.2")
127
274
  end
128
275
 
129
276
  self.default_connection_handler = ConnectionAdapters::ConnectionHandler.new
277
+ self.default_role = writing_role
278
+ self.default_shard = :default
279
+
280
+ def self.strict_loading_violation!(owner:, association:) # :nodoc:
281
+ case action_on_strict_loading_violation
282
+ when :raise
283
+ message = "`#{association}` called on `#{owner}` is marked for strict_loading and cannot be lazily loaded."
284
+ raise ActiveRecord::StrictLoadingViolationError.new(message)
285
+ when :log
286
+ name = "strict_loading_violation.active_record"
287
+ ActiveSupport::Notifications.instrument(name, owner: owner, association: association)
288
+ end
289
+ end
130
290
  end
131
291
 
132
292
  module ClassMethods
133
- def allocate
134
- define_attribute_methods
135
- super
136
- end
137
-
138
293
  def initialize_find_by_cache # :nodoc:
139
- @find_by_statement_cache = { true => {}.extend(Mutex_m), false => {}.extend(Mutex_m) }
294
+ @find_by_statement_cache = { true => Concurrent::Map.new, false => Concurrent::Map.new }
140
295
  end
141
296
 
142
297
  def inherited(child_class) # :nodoc:
143
298
  # initialize cache at class definition for thread safety
144
299
  child_class.initialize_find_by_cache
300
+ unless child_class.base_class?
301
+ klass = self
302
+ until klass.base_class?
303
+ klass.initialize_find_by_cache
304
+ klass = klass.superclass
305
+ end
306
+ end
145
307
  super
146
308
  end
147
309
 
148
310
  def find(*ids) # :nodoc:
149
311
  # We don't have cache keys for this stuff yet
150
312
  return super unless ids.length == 1
151
- return super if block_given? ||
152
- primary_key.nil? ||
153
- scope_attributes? ||
154
- columns_hash.include?(inheritance_column) ||
155
- ids.first.kind_of?(Array)
156
-
157
- id = ids.first
158
- if ActiveRecord::Base === id
159
- id = id.id
160
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
161
- You are passing an instance of ActiveRecord::Base to `find`.
162
- Please pass the id of the object by calling `.id`.
163
- MSG
164
- end
313
+ return super if block_given? || primary_key.nil? || scope_attributes?
314
+
315
+ id = ids.first
316
+
317
+ return super if StatementCache.unsupported_value?(id)
165
318
 
166
319
  key = primary_key
167
320
 
168
321
  statement = cached_find_by_statement(key) { |params|
169
322
  where(key => params.bind).limit(1)
170
323
  }
171
- record = statement.execute([id], self, connection).first
172
- unless record
173
- raise RecordNotFound.new("Couldn't find #{name} with '#{primary_key}'=#{id}",
174
- name, primary_key, id)
175
- end
176
- record
177
- rescue RangeError
178
- raise RecordNotFound.new("Couldn't find #{name} with an out of range value for '#{primary_key}'",
179
- name, primary_key)
324
+
325
+ statement.execute([id], connection).first ||
326
+ raise(RecordNotFound.new("Couldn't find #{name} with '#{key}'=#{id}", name, key, id))
180
327
  end
181
328
 
182
329
  def find_by(*args) # :nodoc:
183
- return super if scope_attributes? || reflect_on_all_aggregations.any?
330
+ return super if scope_attributes?
184
331
 
185
332
  hash = args.first
333
+ return super unless Hash === hash
334
+
335
+ values = hash.values.map! { |value| value.respond_to?(:id) ? value.id : value }
336
+ return super if values.any? { |v| StatementCache.unsupported_value?(v) }
337
+
338
+ keys = hash.keys.map! do |key|
339
+ attribute_aliases[name = key.to_s] || begin
340
+ reflection = _reflect_on_association(name)
341
+ if reflection&.belongs_to? && !reflection.polymorphic?
342
+ reflection.join_foreign_key
343
+ elsif reflect_on_aggregation(name)
344
+ return super
345
+ else
346
+ name
347
+ end
348
+ end
349
+ end
186
350
 
187
- return super if !(Hash === hash) || hash.values.any? { |v|
188
- v.nil? || Array === v || Hash === v || Relation === v || Base === v
189
- }
190
-
191
- # We can't cache Post.find_by(author: david) ...yet
192
- return super unless hash.keys.all? { |k| columns_hash.has_key?(k.to_s) }
193
-
194
- keys = hash.keys
351
+ return super unless keys.all? { |k| columns_hash.key?(k) }
195
352
 
196
353
  statement = cached_find_by_statement(keys) { |params|
197
- wheres = keys.each_with_object({}) { |param, o|
198
- o[param] = params.bind
199
- }
354
+ wheres = keys.index_with { params.bind }
200
355
  where(wheres).limit(1)
201
356
  }
357
+
202
358
  begin
203
- statement.execute(hash.values, self, connection).first
359
+ statement.execute(values, connection).first
204
360
  rescue TypeError
205
361
  raise ActiveRecord::StatementInvalid
206
- rescue RangeError
207
- nil
208
362
  end
209
363
  end
210
364
 
211
365
  def find_by!(*args) # :nodoc:
212
- find_by(*args) or raise RecordNotFound.new("Couldn't find #{name}", name)
366
+ find_by(*args) || raise(RecordNotFound.new("Couldn't find #{name}", name))
213
367
  end
214
368
 
215
369
  def initialize_generated_modules # :nodoc:
216
370
  generated_association_methods
217
371
  end
218
372
 
219
- def generated_association_methods
373
+ def generated_association_methods # :nodoc:
220
374
  @generated_association_methods ||= begin
221
375
  mod = const_set(:GeneratedAssociationMethods, Module.new)
376
+ private_constant :GeneratedAssociationMethods
222
377
  include mod
378
+
223
379
  mod
224
380
  end
225
381
  end
226
382
 
383
+ # Returns columns which shouldn't be exposed while calling +#inspect+.
384
+ def filter_attributes
385
+ if defined?(@filter_attributes)
386
+ @filter_attributes
387
+ else
388
+ superclass.filter_attributes
389
+ end
390
+ end
391
+
392
+ # Specifies columns which shouldn't be exposed while calling +#inspect+.
393
+ attr_writer :filter_attributes
394
+
227
395
  # Returns a string like 'Post(id:integer, title:string, body:text)'
228
- def inspect
396
+ def inspect # :nodoc:
229
397
  if self == Base
230
398
  super
231
399
  elsif abstract_class?
@@ -233,41 +401,31 @@ module ActiveRecord
233
401
  elsif !connected?
234
402
  "#{super} (call '#{super}.connection' to establish a connection)"
235
403
  elsif table_exists?
236
- attr_list = attribute_types.map { |name, type| "#{name}: #{type.type}" } * ', '
404
+ attr_list = attribute_types.map { |name, type| "#{name}: #{type.type}" } * ", "
237
405
  "#{super}(#{attr_list})"
238
406
  else
239
407
  "#{super}(Table doesn't exist)"
240
408
  end
241
409
  end
242
410
 
243
- # Overwrite the default class equality method to provide support for association proxies.
244
- def ===(object)
411
+ # Overwrite the default class equality method to provide support for decorated models.
412
+ def ===(object) # :nodoc:
245
413
  object.is_a?(self)
246
414
  end
247
415
 
248
416
  # Returns an instance of <tt>Arel::Table</tt> loaded with the current table name.
249
417
  #
250
418
  # class Post < ActiveRecord::Base
251
- # scope :published_and_commented, -> { published.and(self.arel_table[:comments_count].gt(0)) }
419
+ # scope :published_and_commented, -> { published.and(arel_table[:comments_count].gt(0)) }
252
420
  # end
253
421
  def arel_table # :nodoc:
254
- @arel_table ||= Arel::Table.new(table_name, type_caster: type_caster)
255
- end
256
-
257
- # Returns the Arel engine.
258
- def arel_engine # :nodoc:
259
- @arel_engine ||=
260
- if Base == self || connection_handler.retrieve_connection_pool(connection_specification_name)
261
- self
262
- else
263
- superclass.arel_engine
264
- end
422
+ @arel_table ||= Arel::Table.new(table_name, klass: self)
265
423
  end
266
424
 
267
425
  def arel_attribute(name, table = arel_table) # :nodoc:
268
- name = attribute_alias(name) if attribute_alias?(name)
269
426
  table[name]
270
427
  end
428
+ deprecate :arel_attribute
271
429
 
272
430
  def predicate_builder # :nodoc:
273
431
  @predicate_builder ||= PredicateBuilder.new(table_metadata)
@@ -277,28 +435,29 @@ module ActiveRecord
277
435
  TypeCaster::Map.new(self)
278
436
  end
279
437
 
280
- private
438
+ def _internal? # :nodoc:
439
+ false
440
+ end
281
441
 
282
442
  def cached_find_by_statement(key, &block) # :nodoc:
283
443
  cache = @find_by_statement_cache[connection.prepared_statements]
284
- cache[key] || cache.synchronize {
285
- cache[key] ||= StatementCache.create(connection, &block)
286
- }
444
+ cache.compute_if_absent(key) { StatementCache.create(connection, &block) }
287
445
  end
288
446
 
289
- def relation # :nodoc:
290
- relation = Relation.create(self, arel_table, predicate_builder)
447
+ private
448
+ def relation
449
+ relation = Relation.create(self)
291
450
 
292
- if finder_needs_type_condition? && !ignore_default_scope?
293
- relation.where(type_condition).create_with(inheritance_column.to_sym => sti_name)
294
- else
295
- relation
451
+ if finder_needs_type_condition? && !ignore_default_scope?
452
+ relation.where!(type_condition)
453
+ else
454
+ relation
455
+ end
296
456
  end
297
- end
298
457
 
299
- def table_metadata # :nodoc:
300
- TableMetadata.new(self, arel_table)
301
- end
458
+ def table_metadata
459
+ TableMetadata.new(self, arel_table)
460
+ end
302
461
  end
303
462
 
304
463
  # New objects can be instantiated as either empty (pass no construction parameter) or pre-set with
@@ -310,7 +469,7 @@ module ActiveRecord
310
469
  # # Instantiates a single new object
311
470
  # User.new(first_name: 'Jamie')
312
471
  def initialize(attributes = nil)
313
- self.class.define_attribute_methods
472
+ @new_record = true
314
473
  @attributes = self.class._default_attributes.deep_dup
315
474
 
316
475
  init_internals
@@ -336,15 +495,21 @@ module ActiveRecord
336
495
  # post = Post.allocate
337
496
  # post.init_with(coder)
338
497
  # post.title # => 'hello world'
339
- def init_with(coder)
498
+ def init_with(coder, &block)
340
499
  coder = LegacyYamlAdapter.convert(self.class, coder)
341
- @attributes = coder['attributes']
342
-
343
- init_internals
500
+ attributes = self.class.yaml_encoder.decode(coder)
501
+ init_with_attributes(attributes, coder["new_record"], &block)
502
+ end
344
503
 
345
- @new_record = coder['new_record']
504
+ ##
505
+ # Initialize an empty model object from +attributes+.
506
+ # +attributes+ should be an attributes object, and unlike the
507
+ # `initialize` method, no assignment calls are made per attribute.
508
+ def init_with_attributes(attributes, new_record = false) # :nodoc:
509
+ @new_record = new_record
510
+ @attributes = attributes
346
511
 
347
- self.class.define_attribute_methods
512
+ init_internals
348
513
 
349
514
  yield self if block_given?
350
515
 
@@ -383,12 +548,14 @@ module ActiveRecord
383
548
  ##
384
549
  def initialize_dup(other) # :nodoc:
385
550
  @attributes = @attributes.deep_dup
386
- @attributes.reset(self.class.primary_key)
551
+ @attributes.reset(@primary_key)
387
552
 
388
553
  _run_initialize_callbacks
389
554
 
390
- @new_record = true
391
- @destroyed = false
555
+ @new_record = true
556
+ @previously_new_record = false
557
+ @destroyed = false
558
+ @_start_transaction_state = nil
392
559
 
393
560
  super
394
561
  end
@@ -406,11 +573,9 @@ module ActiveRecord
406
573
  # Post.new.encode_with(coder)
407
574
  # coder # => {"attributes" => {"id" => nil, ... }}
408
575
  def encode_with(coder)
409
- # FIXME: Remove this when we better serialize attributes
410
- coder['raw_attributes'] = attributes_before_type_cast
411
- coder['attributes'] = @attributes
412
- coder['new_record'] = new_record?
413
- coder['active_record_yaml_version'] = 1
576
+ self.class.yaml_encoder.encode(@attributes, coder)
577
+ coder["new_record"] = new_record?
578
+ coder["active_record_yaml_version"] = 2
414
579
  end
415
580
 
416
581
  # Returns true if +comparison_object+ is the same exact object, or +comparison_object+
@@ -434,7 +599,7 @@ module ActiveRecord
434
599
  # [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
435
600
  def hash
436
601
  if id
437
- id.hash
602
+ self.class.hash ^ id.hash
438
603
  else
439
604
  super
440
605
  end
@@ -456,18 +621,41 @@ module ActiveRecord
456
621
  # Allows sort on objects
457
622
  def <=>(other_object)
458
623
  if other_object.is_a?(self.class)
459
- self.to_key <=> other_object.to_key
624
+ to_key <=> other_object.to_key
460
625
  else
461
626
  super
462
627
  end
463
628
  end
464
629
 
465
- # Returns +true+ if the record is read only. Records loaded through joins with piggy-back
466
- # attributes will be marked as read only since they cannot be saved.
630
+ def present? # :nodoc:
631
+ true
632
+ end
633
+
634
+ def blank? # :nodoc:
635
+ false
636
+ end
637
+
638
+ # Returns +true+ if the record is read only.
467
639
  def readonly?
468
640
  @readonly
469
641
  end
470
642
 
643
+ # Returns +true+ if the record is in strict_loading mode.
644
+ def strict_loading?
645
+ @strict_loading
646
+ end
647
+
648
+ # Sets the record to strict_loading mode. This will raise an error
649
+ # if the record tries to lazily load an association.
650
+ #
651
+ # user = User.first
652
+ # user.strict_loading!
653
+ # user.comments.to_a
654
+ # => ActiveRecord::StrictLoadingViolationError
655
+ def strict_loading!
656
+ @strict_loading = true
657
+ end
658
+
471
659
  # Marks this record as read only.
472
660
  def readonly!
473
661
  @readonly = true
@@ -482,14 +670,15 @@ module ActiveRecord
482
670
  # We check defined?(@attributes) not to issue warnings if the object is
483
671
  # allocated but not initialized.
484
672
  inspection = if defined?(@attributes) && @attributes
485
- self.class.column_names.collect { |name|
486
- if has_attribute?(name)
487
- "#{name}: #{attribute_for_inspect(name)}"
488
- end
489
- }.compact.join(", ")
490
- else
491
- "not initialized"
492
- end
673
+ self.class.attribute_names.collect do |name|
674
+ if _has_attribute?(name)
675
+ "#{name}: #{attribute_for_inspect(name)}"
676
+ end
677
+ end.compact.join(", ")
678
+ else
679
+ "not initialized"
680
+ end
681
+
493
682
  "#<#{self.class} #{inspection}>"
494
683
  end
495
684
 
@@ -499,65 +688,80 @@ module ActiveRecord
499
688
  return super if custom_inspect_method_defined?
500
689
  pp.object_address_group(self) do
501
690
  if defined?(@attributes) && @attributes
502
- column_names = self.class.column_names.select { |name| has_attribute?(name) || new_record? }
503
- pp.seplist(column_names, proc { pp.text ',' }) do |column_name|
504
- column_value = read_attribute(column_name)
505
- pp.breakable ' '
691
+ attr_names = self.class.attribute_names.select { |name| _has_attribute?(name) }
692
+ pp.seplist(attr_names, proc { pp.text "," }) do |attr_name|
693
+ pp.breakable " "
506
694
  pp.group(1) do
507
- pp.text column_name
508
- pp.text ':'
695
+ pp.text attr_name
696
+ pp.text ":"
509
697
  pp.breakable
510
- pp.pp column_value
698
+ value = _read_attribute(attr_name)
699
+ value = inspection_filter.filter_param(attr_name, value) unless value.nil?
700
+ pp.pp value
511
701
  end
512
702
  end
513
703
  else
514
- pp.breakable ' '
515
- pp.text 'not initialized'
704
+ pp.breakable " "
705
+ pp.text "not initialized"
516
706
  end
517
707
  end
518
708
  end
519
709
 
520
710
  # Returns a hash of the given methods with their names as keys and returned values as values.
521
711
  def slice(*methods)
522
- Hash[methods.map! { |method| [method, public_send(method)] }].with_indifferent_access
712
+ methods.flatten.index_with { |method| public_send(method) }.with_indifferent_access
713
+ end
714
+
715
+ # Returns an array of the values returned by the given methods.
716
+ def values_at(*methods)
717
+ methods.flatten.map! { |method| public_send(method) }
523
718
  end
524
719
 
525
720
  private
721
+ # +Array#flatten+ will call +#to_ary+ (recursively) on each of the elements of
722
+ # the array, and then rescues from the possible +NoMethodError+. If those elements are
723
+ # +ActiveRecord::Base+'s, then this triggers the various +method_missing+'s that we have,
724
+ # which significantly impacts upon performance.
725
+ #
726
+ # So we can avoid the +method_missing+ hit by explicitly defining +#to_ary+ as +nil+ here.
727
+ #
728
+ # See also https://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary.html
729
+ def to_ary
730
+ nil
731
+ end
526
732
 
527
- # Under Ruby 1.9, Array#flatten will call #to_ary (recursively) on each of the elements
528
- # of the array, and then rescues from the possible NoMethodError. If those elements are
529
- # ActiveRecord::Base's, then this triggers the various method_missing's that we have,
530
- # which significantly impacts upon performance.
531
- #
532
- # So we can avoid the method_missing hit by explicitly defining #to_ary as nil here.
533
- #
534
- # See also http://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary.html
535
- def to_ary # :nodoc:
536
- nil
537
- end
733
+ def init_internals
734
+ @primary_key = self.class.primary_key
735
+ @readonly = false
736
+ @previously_new_record = false
737
+ @destroyed = false
738
+ @marked_for_destruction = false
739
+ @destroyed_by_association = nil
740
+ @_start_transaction_state = nil
741
+ @strict_loading = self.class.strict_loading_by_default
742
+
743
+ self.class.define_attribute_methods
744
+ end
538
745
 
539
- def init_internals
540
- @readonly = false
541
- @destroyed = false
542
- @marked_for_destruction = false
543
- @destroyed_by_association = nil
544
- @new_record = true
545
- @txn = nil
546
- @_start_transaction_state = {}
547
- @transaction_state = nil
548
- end
746
+ def initialize_internals_callback
747
+ end
549
748
 
550
- def initialize_internals_callback
551
- end
749
+ def custom_inspect_method_defined?
750
+ self.class.instance_method(:inspect).owner != ActiveRecord::Base.instance_method(:inspect).owner
751
+ end
552
752
 
553
- def thaw
554
- if frozen?
555
- @attributes = @attributes.dup
753
+ class InspectionMask < DelegateClass(::String)
754
+ def pretty_print(pp)
755
+ pp.text __getobj__
756
+ end
556
757
  end
557
- end
758
+ private_constant :InspectionMask
558
759
 
559
- def custom_inspect_method_defined?
560
- self.class.instance_method(:inspect).owner != ActiveRecord::Base.instance_method(:inspect).owner
561
- end
760
+ def inspection_filter
761
+ @inspection_filter ||= begin
762
+ mask = InspectionMask.new(ActiveSupport::ParameterFilter::FILTERED)
763
+ ActiveSupport::ParameterFilter.new(self.class.filter_attributes, mask: mask)
764
+ end
765
+ end
562
766
  end
563
767
  end