activerecord 6.0.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 (340) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1086 -0
  3. data/MIT-LICENSE +22 -0
  4. data/README.rdoc +219 -0
  5. data/examples/performance.rb +185 -0
  6. data/examples/simple.rb +15 -0
  7. data/lib/active_record.rb +195 -0
  8. data/lib/active_record/aggregations.rb +285 -0
  9. data/lib/active_record/association_relation.rb +49 -0
  10. data/lib/active_record/associations.rb +1865 -0
  11. data/lib/active_record/associations/alias_tracker.rb +81 -0
  12. data/lib/active_record/associations/association.rb +340 -0
  13. data/lib/active_record/associations/association_scope.rb +166 -0
  14. data/lib/active_record/associations/belongs_to_association.rb +124 -0
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +36 -0
  16. data/lib/active_record/associations/builder/association.rb +136 -0
  17. data/lib/active_record/associations/builder/belongs_to.rb +130 -0
  18. data/lib/active_record/associations/builder/collection_association.rb +72 -0
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +114 -0
  20. data/lib/active_record/associations/builder/has_many.rb +19 -0
  21. data/lib/active_record/associations/builder/has_one.rb +64 -0
  22. data/lib/active_record/associations/builder/singular_association.rb +44 -0
  23. data/lib/active_record/associations/collection_association.rb +498 -0
  24. data/lib/active_record/associations/collection_proxy.rb +1128 -0
  25. data/lib/active_record/associations/foreign_association.rb +20 -0
  26. data/lib/active_record/associations/has_many_association.rb +136 -0
  27. data/lib/active_record/associations/has_many_through_association.rb +220 -0
  28. data/lib/active_record/associations/has_one_association.rb +118 -0
  29. data/lib/active_record/associations/has_one_through_association.rb +45 -0
  30. data/lib/active_record/associations/join_dependency.rb +262 -0
  31. data/lib/active_record/associations/join_dependency/join_association.rb +80 -0
  32. data/lib/active_record/associations/join_dependency/join_base.rb +23 -0
  33. data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
  34. data/lib/active_record/associations/preloader.rb +201 -0
  35. data/lib/active_record/associations/preloader/association.rb +133 -0
  36. data/lib/active_record/associations/preloader/through_association.rb +116 -0
  37. data/lib/active_record/associations/singular_association.rb +59 -0
  38. data/lib/active_record/associations/through_association.rb +121 -0
  39. data/lib/active_record/attribute_assignment.rb +85 -0
  40. data/lib/active_record/attribute_decorators.rb +90 -0
  41. data/lib/active_record/attribute_methods.rb +420 -0
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +81 -0
  43. data/lib/active_record/attribute_methods/dirty.rb +221 -0
  44. data/lib/active_record/attribute_methods/primary_key.rb +136 -0
  45. data/lib/active_record/attribute_methods/query.rb +41 -0
  46. data/lib/active_record/attribute_methods/read.rb +47 -0
  47. data/lib/active_record/attribute_methods/serialization.rb +90 -0
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +91 -0
  49. data/lib/active_record/attribute_methods/write.rb +61 -0
  50. data/lib/active_record/attributes.rb +279 -0
  51. data/lib/active_record/autosave_association.rb +512 -0
  52. data/lib/active_record/base.rb +328 -0
  53. data/lib/active_record/callbacks.rb +339 -0
  54. data/lib/active_record/coders/json.rb +15 -0
  55. data/lib/active_record/coders/yaml_column.rb +50 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1175 -0
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +85 -0
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +516 -0
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +155 -0
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +251 -0
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +713 -0
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +93 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1475 -0
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +323 -0
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +772 -0
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +830 -0
  69. data/lib/active_record/connection_adapters/column.rb +95 -0
  70. data/lib/active_record/connection_adapters/connection_specification.rb +297 -0
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +202 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +72 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +146 -0
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -0
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +184 -0
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +53 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +113 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +205 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +222 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +776 -0
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +953 -0
  117. data/lib/active_record/connection_adapters/schema_cache.rb +141 -0
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
  119. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
  120. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  121. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +103 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
  126. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +561 -0
  127. data/lib/active_record/connection_adapters/statement_pool.rb +61 -0
  128. data/lib/active_record/connection_handling.rb +274 -0
  129. data/lib/active_record/core.rb +603 -0
  130. data/lib/active_record/counter_cache.rb +193 -0
  131. data/lib/active_record/database_configurations.rb +233 -0
  132. data/lib/active_record/database_configurations/database_config.rb +37 -0
  133. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  134. data/lib/active_record/database_configurations/url_config.rb +79 -0
  135. data/lib/active_record/define_callbacks.rb +22 -0
  136. data/lib/active_record/dynamic_matchers.rb +122 -0
  137. data/lib/active_record/enum.rb +274 -0
  138. data/lib/active_record/errors.rb +388 -0
  139. data/lib/active_record/explain.rb +50 -0
  140. data/lib/active_record/explain_registry.rb +32 -0
  141. data/lib/active_record/explain_subscriber.rb +34 -0
  142. data/lib/active_record/fixture_set/file.rb +82 -0
  143. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  144. data/lib/active_record/fixture_set/render_context.rb +17 -0
  145. data/lib/active_record/fixture_set/table_row.rb +153 -0
  146. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  147. data/lib/active_record/fixtures.rb +738 -0
  148. data/lib/active_record/gem_version.rb +17 -0
  149. data/lib/active_record/inheritance.rb +293 -0
  150. data/lib/active_record/insert_all.rb +179 -0
  151. data/lib/active_record/integration.rb +207 -0
  152. data/lib/active_record/internal_metadata.rb +53 -0
  153. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  154. data/lib/active_record/locale/en.yml +48 -0
  155. data/lib/active_record/locking/optimistic.rb +197 -0
  156. data/lib/active_record/locking/pessimistic.rb +89 -0
  157. data/lib/active_record/log_subscriber.rb +118 -0
  158. data/lib/active_record/middleware/database_selector.rb +75 -0
  159. data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
  160. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  161. data/lib/active_record/migration.rb +1397 -0
  162. data/lib/active_record/migration/command_recorder.rb +284 -0
  163. data/lib/active_record/migration/compatibility.rb +244 -0
  164. data/lib/active_record/migration/join_table.rb +17 -0
  165. data/lib/active_record/model_schema.rb +545 -0
  166. data/lib/active_record/nested_attributes.rb +600 -0
  167. data/lib/active_record/no_touching.rb +65 -0
  168. data/lib/active_record/null_relation.rb +68 -0
  169. data/lib/active_record/persistence.rb +967 -0
  170. data/lib/active_record/query_cache.rb +52 -0
  171. data/lib/active_record/querying.rb +82 -0
  172. data/lib/active_record/railtie.rb +263 -0
  173. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  174. data/lib/active_record/railties/console_sandbox.rb +7 -0
  175. data/lib/active_record/railties/controller_runtime.rb +51 -0
  176. data/lib/active_record/railties/databases.rake +527 -0
  177. data/lib/active_record/readonly_attributes.rb +24 -0
  178. data/lib/active_record/reflection.rb +1042 -0
  179. data/lib/active_record/relation.rb +860 -0
  180. data/lib/active_record/relation/batches.rb +290 -0
  181. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  182. data/lib/active_record/relation/calculations.rb +424 -0
  183. data/lib/active_record/relation/delegation.rb +130 -0
  184. data/lib/active_record/relation/finder_methods.rb +561 -0
  185. data/lib/active_record/relation/from_clause.rb +26 -0
  186. data/lib/active_record/relation/merger.rb +184 -0
  187. data/lib/active_record/relation/predicate_builder.rb +150 -0
  188. data/lib/active_record/relation/predicate_builder/array_handler.rb +49 -0
  189. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  190. data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
  191. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  192. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  193. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  194. data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
  195. data/lib/active_record/relation/query_attribute.rb +50 -0
  196. data/lib/active_record/relation/query_methods.rb +1371 -0
  197. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  198. data/lib/active_record/relation/spawn_methods.rb +77 -0
  199. data/lib/active_record/relation/where_clause.rb +190 -0
  200. data/lib/active_record/relation/where_clause_factory.rb +33 -0
  201. data/lib/active_record/result.rb +168 -0
  202. data/lib/active_record/runtime_registry.rb +24 -0
  203. data/lib/active_record/sanitization.rb +214 -0
  204. data/lib/active_record/schema.rb +61 -0
  205. data/lib/active_record/schema_dumper.rb +270 -0
  206. data/lib/active_record/schema_migration.rb +60 -0
  207. data/lib/active_record/scoping.rb +106 -0
  208. data/lib/active_record/scoping/default.rb +151 -0
  209. data/lib/active_record/scoping/named.rb +217 -0
  210. data/lib/active_record/secure_token.rb +40 -0
  211. data/lib/active_record/serialization.rb +22 -0
  212. data/lib/active_record/statement_cache.rb +148 -0
  213. data/lib/active_record/store.rb +290 -0
  214. data/lib/active_record/suppressor.rb +61 -0
  215. data/lib/active_record/table_metadata.rb +75 -0
  216. data/lib/active_record/tasks/database_tasks.rb +506 -0
  217. data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
  218. data/lib/active_record/tasks/postgresql_database_tasks.rb +141 -0
  219. data/lib/active_record/tasks/sqlite_database_tasks.rb +77 -0
  220. data/lib/active_record/test_databases.rb +23 -0
  221. data/lib/active_record/test_fixtures.rb +224 -0
  222. data/lib/active_record/timestamp.rb +167 -0
  223. data/lib/active_record/touch_later.rb +66 -0
  224. data/lib/active_record/transactions.rb +493 -0
  225. data/lib/active_record/translation.rb +24 -0
  226. data/lib/active_record/type.rb +78 -0
  227. data/lib/active_record/type/adapter_specific_registry.rb +129 -0
  228. data/lib/active_record/type/date.rb +9 -0
  229. data/lib/active_record/type/date_time.rb +9 -0
  230. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  231. data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
  232. data/lib/active_record/type/internal/timezone.rb +17 -0
  233. data/lib/active_record/type/json.rb +30 -0
  234. data/lib/active_record/type/serialized.rb +71 -0
  235. data/lib/active_record/type/text.rb +11 -0
  236. data/lib/active_record/type/time.rb +21 -0
  237. data/lib/active_record/type/type_map.rb +62 -0
  238. data/lib/active_record/type/unsigned_integer.rb +17 -0
  239. data/lib/active_record/type_caster.rb +9 -0
  240. data/lib/active_record/type_caster/connection.rb +34 -0
  241. data/lib/active_record/type_caster/map.rb +20 -0
  242. data/lib/active_record/validations.rb +94 -0
  243. data/lib/active_record/validations/absence.rb +25 -0
  244. data/lib/active_record/validations/associated.rb +60 -0
  245. data/lib/active_record/validations/length.rb +26 -0
  246. data/lib/active_record/validations/presence.rb +68 -0
  247. data/lib/active_record/validations/uniqueness.rb +226 -0
  248. data/lib/active_record/version.rb +10 -0
  249. data/lib/arel.rb +58 -0
  250. data/lib/arel/alias_predication.rb +9 -0
  251. data/lib/arel/attributes.rb +22 -0
  252. data/lib/arel/attributes/attribute.rb +37 -0
  253. data/lib/arel/collectors/bind.rb +24 -0
  254. data/lib/arel/collectors/composite.rb +31 -0
  255. data/lib/arel/collectors/plain_string.rb +20 -0
  256. data/lib/arel/collectors/sql_string.rb +20 -0
  257. data/lib/arel/collectors/substitute_binds.rb +28 -0
  258. data/lib/arel/crud.rb +42 -0
  259. data/lib/arel/delete_manager.rb +18 -0
  260. data/lib/arel/errors.rb +9 -0
  261. data/lib/arel/expressions.rb +29 -0
  262. data/lib/arel/factory_methods.rb +49 -0
  263. data/lib/arel/insert_manager.rb +49 -0
  264. data/lib/arel/math.rb +45 -0
  265. data/lib/arel/nodes.rb +68 -0
  266. data/lib/arel/nodes/and.rb +32 -0
  267. data/lib/arel/nodes/ascending.rb +23 -0
  268. data/lib/arel/nodes/binary.rb +52 -0
  269. data/lib/arel/nodes/bind_param.rb +36 -0
  270. data/lib/arel/nodes/case.rb +55 -0
  271. data/lib/arel/nodes/casted.rb +50 -0
  272. data/lib/arel/nodes/comment.rb +29 -0
  273. data/lib/arel/nodes/count.rb +12 -0
  274. data/lib/arel/nodes/delete_statement.rb +45 -0
  275. data/lib/arel/nodes/descending.rb +23 -0
  276. data/lib/arel/nodes/equality.rb +18 -0
  277. data/lib/arel/nodes/extract.rb +24 -0
  278. data/lib/arel/nodes/false.rb +16 -0
  279. data/lib/arel/nodes/full_outer_join.rb +8 -0
  280. data/lib/arel/nodes/function.rb +44 -0
  281. data/lib/arel/nodes/grouping.rb +8 -0
  282. data/lib/arel/nodes/in.rb +8 -0
  283. data/lib/arel/nodes/infix_operation.rb +80 -0
  284. data/lib/arel/nodes/inner_join.rb +8 -0
  285. data/lib/arel/nodes/insert_statement.rb +37 -0
  286. data/lib/arel/nodes/join_source.rb +20 -0
  287. data/lib/arel/nodes/matches.rb +18 -0
  288. data/lib/arel/nodes/named_function.rb +23 -0
  289. data/lib/arel/nodes/node.rb +50 -0
  290. data/lib/arel/nodes/node_expression.rb +13 -0
  291. data/lib/arel/nodes/outer_join.rb +8 -0
  292. data/lib/arel/nodes/over.rb +15 -0
  293. data/lib/arel/nodes/regexp.rb +16 -0
  294. data/lib/arel/nodes/right_outer_join.rb +8 -0
  295. data/lib/arel/nodes/select_core.rb +67 -0
  296. data/lib/arel/nodes/select_statement.rb +41 -0
  297. data/lib/arel/nodes/sql_literal.rb +16 -0
  298. data/lib/arel/nodes/string_join.rb +11 -0
  299. data/lib/arel/nodes/table_alias.rb +27 -0
  300. data/lib/arel/nodes/terminal.rb +16 -0
  301. data/lib/arel/nodes/true.rb +16 -0
  302. data/lib/arel/nodes/unary.rb +45 -0
  303. data/lib/arel/nodes/unary_operation.rb +20 -0
  304. data/lib/arel/nodes/unqualified_column.rb +22 -0
  305. data/lib/arel/nodes/update_statement.rb +41 -0
  306. data/lib/arel/nodes/values_list.rb +9 -0
  307. data/lib/arel/nodes/window.rb +126 -0
  308. data/lib/arel/nodes/with.rb +11 -0
  309. data/lib/arel/order_predications.rb +13 -0
  310. data/lib/arel/predications.rb +257 -0
  311. data/lib/arel/select_manager.rb +271 -0
  312. data/lib/arel/table.rb +110 -0
  313. data/lib/arel/tree_manager.rb +72 -0
  314. data/lib/arel/update_manager.rb +34 -0
  315. data/lib/arel/visitors.rb +20 -0
  316. data/lib/arel/visitors/depth_first.rb +204 -0
  317. data/lib/arel/visitors/dot.rb +297 -0
  318. data/lib/arel/visitors/ibm_db.rb +34 -0
  319. data/lib/arel/visitors/informix.rb +62 -0
  320. data/lib/arel/visitors/mssql.rb +157 -0
  321. data/lib/arel/visitors/mysql.rb +83 -0
  322. data/lib/arel/visitors/oracle.rb +159 -0
  323. data/lib/arel/visitors/oracle12.rb +66 -0
  324. data/lib/arel/visitors/postgresql.rb +110 -0
  325. data/lib/arel/visitors/sqlite.rb +39 -0
  326. data/lib/arel/visitors/to_sql.rb +889 -0
  327. data/lib/arel/visitors/visitor.rb +46 -0
  328. data/lib/arel/visitors/where_sql.rb +23 -0
  329. data/lib/arel/window_predications.rb +9 -0
  330. data/lib/rails/generators/active_record.rb +19 -0
  331. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  332. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  333. data/lib/rails/generators/active_record/migration.rb +48 -0
  334. data/lib/rails/generators/active_record/migration/migration_generator.rb +75 -0
  335. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  336. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +48 -0
  337. data/lib/rails/generators/active_record/model/model_generator.rb +49 -0
  338. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  339. data/lib/rails/generators/active_record/model/templates/module.rb.tt +7 -0
  340. metadata +418 -0
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ class StatementPool # :nodoc:
6
+ include Enumerable
7
+
8
+ DEFAULT_STATEMENT_LIMIT = 1000
9
+
10
+ def initialize(statement_limit = nil)
11
+ @cache = Hash.new { |h, pid| h[pid] = {} }
12
+ @statement_limit = statement_limit || DEFAULT_STATEMENT_LIMIT
13
+ end
14
+
15
+ def each(&block)
16
+ cache.each(&block)
17
+ end
18
+
19
+ def key?(key)
20
+ cache.key?(key)
21
+ end
22
+
23
+ def [](key)
24
+ cache[key]
25
+ end
26
+
27
+ def length
28
+ cache.length
29
+ end
30
+
31
+ def []=(sql, stmt)
32
+ while @statement_limit <= cache.size
33
+ dealloc(cache.shift.last)
34
+ end
35
+ cache[sql] = stmt
36
+ end
37
+
38
+ def clear
39
+ cache.each_value do |stmt|
40
+ dealloc stmt
41
+ end
42
+ cache.clear
43
+ end
44
+
45
+ def delete(key)
46
+ dealloc cache[key]
47
+ cache.delete(key)
48
+ end
49
+
50
+ private
51
+
52
+ def cache
53
+ @cache[Process.pid]
54
+ end
55
+
56
+ def dealloc(stmt)
57
+ raise NotImplementedError
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,274 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionHandling
5
+ RAILS_ENV = -> { (Rails.env if defined?(Rails.env)) || ENV["RAILS_ENV"].presence || ENV["RACK_ENV"].presence }
6
+ DEFAULT_ENV = -> { RAILS_ENV.call || "default_env" }
7
+
8
+ # Establishes the connection to the database. Accepts a hash as input where
9
+ # the <tt>:adapter</tt> key must be specified with the name of a database adapter (in lower-case)
10
+ # example for regular databases (MySQL, PostgreSQL, etc):
11
+ #
12
+ # ActiveRecord::Base.establish_connection(
13
+ # adapter: "mysql2",
14
+ # host: "localhost",
15
+ # username: "myuser",
16
+ # password: "mypass",
17
+ # database: "somedatabase"
18
+ # )
19
+ #
20
+ # Example for SQLite database:
21
+ #
22
+ # ActiveRecord::Base.establish_connection(
23
+ # adapter: "sqlite3",
24
+ # database: "path/to/dbfile"
25
+ # )
26
+ #
27
+ # Also accepts keys as strings (for parsing from YAML for example):
28
+ #
29
+ # ActiveRecord::Base.establish_connection(
30
+ # "adapter" => "sqlite3",
31
+ # "database" => "path/to/dbfile"
32
+ # )
33
+ #
34
+ # Or a URL:
35
+ #
36
+ # ActiveRecord::Base.establish_connection(
37
+ # "postgres://myuser:mypass@localhost/somedatabase"
38
+ # )
39
+ #
40
+ # In case {ActiveRecord::Base.configurations}[rdoc-ref:Core.configurations]
41
+ # is set (Rails automatically loads the contents of config/database.yml into it),
42
+ # a symbol can also be given as argument, representing a key in the
43
+ # configuration hash:
44
+ #
45
+ # ActiveRecord::Base.establish_connection(:production)
46
+ #
47
+ # The exceptions AdapterNotSpecified, AdapterNotFound and +ArgumentError+
48
+ # may be returned on an error.
49
+ def establish_connection(config_or_env = nil)
50
+ config_hash = resolve_config_for_connection(config_or_env)
51
+ connection_handler.establish_connection(config_hash)
52
+ end
53
+
54
+ # Connects a model to the databases specified. The +database+ keyword
55
+ # takes a hash consisting of a +role+ and a +database_key+.
56
+ #
57
+ # This will create a connection handler for switching between connections,
58
+ # look up the config hash using the +database_key+ and finally
59
+ # establishes a connection to that config.
60
+ #
61
+ # class AnimalsModel < ApplicationRecord
62
+ # self.abstract_class = true
63
+ #
64
+ # connects_to database: { writing: :primary, reading: :primary_replica }
65
+ # end
66
+ #
67
+ # Returns an array of established connections.
68
+ def connects_to(database: {})
69
+ connections = []
70
+
71
+ database.each do |role, database_key|
72
+ config_hash = resolve_config_for_connection(database_key)
73
+ handler = lookup_connection_handler(role.to_sym)
74
+
75
+ connections << handler.establish_connection(config_hash)
76
+ end
77
+
78
+ connections
79
+ end
80
+
81
+ # Connects to a database or role (ex writing, reading, or another
82
+ # custom role) for the duration of the block.
83
+ #
84
+ # If a role is passed, Active Record will look up the connection
85
+ # based on the requested role:
86
+ #
87
+ # ActiveRecord::Base.connected_to(role: :writing) do
88
+ # Dog.create! # creates dog using dog writing connection
89
+ # end
90
+ #
91
+ # ActiveRecord::Base.connected_to(role: :reading) do
92
+ # Dog.create! # throws exception because we're on a replica
93
+ # end
94
+ #
95
+ # ActiveRecord::Base.connected_to(role: :unknown_role) do
96
+ # # raises exception due to non-existent role
97
+ # end
98
+ #
99
+ # For cases where you may want to connect to a database outside of the model,
100
+ # you can use +connected_to+ with a +database+ argument. The +database+ argument
101
+ # expects a symbol that corresponds to the database key in your config.
102
+ #
103
+ # ActiveRecord::Base.connected_to(database: :animals_slow_replica) do
104
+ # Dog.run_a_long_query # runs a long query while connected to the +animals_slow_replica+
105
+ # end
106
+ #
107
+ # This will connect to a new database for the queries inside the block. By
108
+ # default the `:writing` role will be used since all connections must be assigned
109
+ # a role. If you would like to use a different role you can pass a hash to database:
110
+ #
111
+ # ActiveRecord::Base.connected_to(database: { readonly_slow: :animals_slow_replica }) do
112
+ # # runs a long query while connected to the +animals_slow_replica+ using the readonly_slow role.
113
+ # Dog.run_a_long_query
114
+ # end
115
+ #
116
+ # When using the database key a new connection will be established every time. It is not
117
+ # recommended to use this outside of one-off scripts.
118
+ def connected_to(database: nil, role: nil, prevent_writes: false, &blk)
119
+ if database && role
120
+ raise ArgumentError, "connected_to can only accept a `database` or a `role` argument, but not both arguments."
121
+ elsif database
122
+ if database.is_a?(Hash)
123
+ role, database = database.first
124
+ role = role.to_sym
125
+ end
126
+
127
+ config_hash = resolve_config_for_connection(database)
128
+ handler = lookup_connection_handler(role)
129
+
130
+ handler.establish_connection(config_hash)
131
+
132
+ with_handler(role, &blk)
133
+ elsif role
134
+ if role == writing_role
135
+ with_handler(role.to_sym) do
136
+ connection_handler.while_preventing_writes(prevent_writes, &blk)
137
+ end
138
+ else
139
+ with_handler(role.to_sym, &blk)
140
+ end
141
+ else
142
+ raise ArgumentError, "must provide a `database` or a `role`."
143
+ end
144
+ end
145
+
146
+ # Returns true if role is the current connected role.
147
+ #
148
+ # ActiveRecord::Base.connected_to(role: :writing) do
149
+ # ActiveRecord::Base.connected_to?(role: :writing) #=> true
150
+ # ActiveRecord::Base.connected_to?(role: :reading) #=> false
151
+ # end
152
+ def connected_to?(role:)
153
+ current_role == role.to_sym
154
+ end
155
+
156
+ # Returns the symbol representing the current connected role.
157
+ #
158
+ # ActiveRecord::Base.connected_to(role: :writing) do
159
+ # ActiveRecord::Base.current_role #=> :writing
160
+ # end
161
+ #
162
+ # ActiveRecord::Base.connected_to(role: :reading) do
163
+ # ActiveRecord::Base.current_role #=> :reading
164
+ # end
165
+ def current_role
166
+ connection_handlers.key(connection_handler)
167
+ end
168
+
169
+ def lookup_connection_handler(handler_key) # :nodoc:
170
+ handler_key ||= ActiveRecord::Base.writing_role
171
+ connection_handlers[handler_key] ||= ActiveRecord::ConnectionAdapters::ConnectionHandler.new
172
+ end
173
+
174
+ def with_handler(handler_key, &blk) # :nodoc:
175
+ handler = lookup_connection_handler(handler_key)
176
+ swap_connection_handler(handler, &blk)
177
+ end
178
+
179
+ def resolve_config_for_connection(config_or_env) # :nodoc:
180
+ raise "Anonymous class is not allowed." unless name
181
+
182
+ config_or_env ||= DEFAULT_ENV.call.to_sym
183
+ pool_name = primary_class? ? "primary" : name
184
+ self.connection_specification_name = pool_name
185
+
186
+ resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new(Base.configurations)
187
+ config_hash = resolver.resolve(config_or_env, pool_name).symbolize_keys
188
+ config_hash[:name] = pool_name
189
+
190
+ config_hash
191
+ end
192
+
193
+ # Clears the query cache for all connections associated with the current thread.
194
+ def clear_query_caches_for_current_thread
195
+ ActiveRecord::Base.connection_handlers.each_value do |handler|
196
+ handler.connection_pool_list.each do |pool|
197
+ pool.connection.clear_query_cache if pool.active_connection?
198
+ end
199
+ end
200
+ end
201
+
202
+ # Returns the connection currently associated with the class. This can
203
+ # also be used to "borrow" the connection to do database work unrelated
204
+ # to any of the specific Active Records.
205
+ def connection
206
+ retrieve_connection
207
+ end
208
+
209
+ attr_writer :connection_specification_name
210
+
211
+ # Return the specification name from the current class or its parent.
212
+ def connection_specification_name
213
+ if !defined?(@connection_specification_name) || @connection_specification_name.nil?
214
+ return self == Base ? "primary" : superclass.connection_specification_name
215
+ end
216
+ @connection_specification_name
217
+ end
218
+
219
+ def primary_class? # :nodoc:
220
+ self == Base || defined?(ApplicationRecord) && self == ApplicationRecord
221
+ end
222
+
223
+ # Returns the configuration of the associated connection as a hash:
224
+ #
225
+ # ActiveRecord::Base.connection_config
226
+ # # => {pool: 5, timeout: 5000, database: "db/development.sqlite3", adapter: "sqlite3"}
227
+ #
228
+ # Please use only for reading.
229
+ def connection_config
230
+ connection_pool.spec.config
231
+ end
232
+
233
+ def connection_pool
234
+ connection_handler.retrieve_connection_pool(connection_specification_name) || raise(ConnectionNotEstablished)
235
+ end
236
+
237
+ def retrieve_connection
238
+ connection_handler.retrieve_connection(connection_specification_name)
239
+ end
240
+
241
+ # Returns +true+ if Active Record is connected.
242
+ def connected?
243
+ connection_handler.connected?(connection_specification_name)
244
+ end
245
+
246
+ def remove_connection(name = nil)
247
+ name ||= @connection_specification_name if defined?(@connection_specification_name)
248
+ # if removing a connection that has a pool, we reset the
249
+ # connection_specification_name so it will use the parent
250
+ # pool.
251
+ if connection_handler.retrieve_connection_pool(name)
252
+ self.connection_specification_name = nil
253
+ end
254
+
255
+ connection_handler.remove_connection(name)
256
+ end
257
+
258
+ def clear_cache! # :nodoc:
259
+ connection.schema_cache.clear!
260
+ end
261
+
262
+ delegate :clear_active_connections!, :clear_reloadable_connections!,
263
+ :clear_all_connections!, :flush_idle_connections!, to: :connection_handler
264
+
265
+ private
266
+
267
+ def swap_connection_handler(handler, &blk) # :nodoc:
268
+ old_handler, ActiveRecord::Base.connection_handler = ActiveRecord::Base.connection_handler, handler
269
+ yield
270
+ ensure
271
+ ActiveRecord::Base.connection_handler = old_handler
272
+ end
273
+ end
274
+ end
@@ -0,0 +1,603 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/hash/indifferent_access"
4
+ require "active_support/core_ext/string/filters"
5
+ require "active_support/parameter_filter"
6
+ require "concurrent/map"
7
+
8
+ module ActiveRecord
9
+ module Core
10
+ extend ActiveSupport::Concern
11
+
12
+ included do
13
+ ##
14
+ # :singleton-method:
15
+ #
16
+ # Accepts a logger conforming to the interface of Log4r which is then
17
+ # passed on to any new database connections made and which can be
18
+ # retrieved on both a class and instance level by calling +logger+.
19
+ mattr_accessor :logger, instance_writer: false
20
+
21
+ ##
22
+ # :singleton-method:
23
+ #
24
+ # Specifies if the methods calling database queries should be logged below
25
+ # their relevant queries. Defaults to false.
26
+ mattr_accessor :verbose_query_logs, instance_writer: false, default: false
27
+
28
+ ##
29
+ # Contains the database configuration - as is typically stored in config/database.yml -
30
+ # as an ActiveRecord::DatabaseConfigurations object.
31
+ #
32
+ # For example, the following database.yml...
33
+ #
34
+ # development:
35
+ # adapter: sqlite3
36
+ # database: db/development.sqlite3
37
+ #
38
+ # production:
39
+ # adapter: sqlite3
40
+ # database: db/production.sqlite3
41
+ #
42
+ # ...would result in ActiveRecord::Base.configurations to look like this:
43
+ #
44
+ # #<ActiveRecord::DatabaseConfigurations:0x00007fd1acbdf800 @configurations=[
45
+ # #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10 @env_name="development",
46
+ # @spec_name="primary", @config={"adapter"=>"sqlite3", "database"=>"db/development.sqlite3"}>,
47
+ # #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbdea90 @env_name="production",
48
+ # @spec_name="primary", @config={"adapter"=>"mysql2", "database"=>"db/production.sqlite3"}>
49
+ # ]>
50
+ def self.configurations=(config)
51
+ @@configurations = ActiveRecord::DatabaseConfigurations.new(config)
52
+ end
53
+ self.configurations = {}
54
+
55
+ # Returns fully resolved ActiveRecord::DatabaseConfigurations object
56
+ def self.configurations
57
+ @@configurations
58
+ end
59
+
60
+ ##
61
+ # :singleton-method:
62
+ # Determines whether to use Time.utc (using :utc) or Time.local (using :local) when pulling
63
+ # dates and times from the database. This is set to :utc by default.
64
+ mattr_accessor :default_timezone, instance_writer: false, default: :utc
65
+
66
+ ##
67
+ # :singleton-method:
68
+ # Specifies the format to use when dumping the database schema with Rails'
69
+ # Rakefile. If :sql, the schema is dumped as (potentially database-
70
+ # specific) SQL statements. If :ruby, the schema is dumped as an
71
+ # ActiveRecord::Schema file which can be loaded into any database that
72
+ # supports migrations. Use :ruby if you want to have different database
73
+ # adapters for, e.g., your development and test environments.
74
+ mattr_accessor :schema_format, instance_writer: false, default: :ruby
75
+
76
+ ##
77
+ # :singleton-method:
78
+ # Specifies if an error should be raised if the query has an order being
79
+ # ignored when doing batch queries. Useful in applications where the
80
+ # scope being ignored is error-worthy, rather than a warning.
81
+ mattr_accessor :error_on_ignored_order, instance_writer: false, default: false
82
+
83
+ # :singleton-method:
84
+ # Specify the behavior for unsafe raw query methods. Values are as follows
85
+ # deprecated - Warnings are logged when unsafe raw SQL is passed to
86
+ # query methods.
87
+ # disabled - Unsafe raw SQL passed to query methods results in
88
+ # UnknownAttributeReference exception.
89
+ mattr_accessor :allow_unsafe_raw_sql, instance_writer: false, default: :deprecated
90
+
91
+ ##
92
+ # :singleton-method:
93
+ # Specify whether or not to use timestamps for migration versions
94
+ mattr_accessor :timestamped_migrations, instance_writer: false, default: true
95
+
96
+ ##
97
+ # :singleton-method:
98
+ # Specify whether schema dump should happen at the end of the
99
+ # db:migrate rails command. This is true by default, which is useful for the
100
+ # development environment. This should ideally be false in the production
101
+ # environment where dumping schema is rarely needed.
102
+ mattr_accessor :dump_schema_after_migration, instance_writer: false, default: true
103
+
104
+ ##
105
+ # :singleton-method:
106
+ # Specifies which database schemas to dump when calling db:structure:dump.
107
+ # If the value is :schema_search_path (the default), any schemas listed in
108
+ # schema_search_path are dumped. Use :all to dump all schemas regardless
109
+ # of schema_search_path, or a string of comma separated schemas for a
110
+ # custom list.
111
+ mattr_accessor :dump_schemas, instance_writer: false, default: :schema_search_path
112
+
113
+ ##
114
+ # :singleton-method:
115
+ # Specify a threshold for the size of query result sets. If the number of
116
+ # records in the set exceeds the threshold, a warning is logged. This can
117
+ # be used to identify queries which load thousands of records and
118
+ # potentially cause memory bloat.
119
+ mattr_accessor :warn_on_records_fetched_greater_than, instance_writer: false
120
+
121
+ mattr_accessor :maintain_test_schema, instance_accessor: false
122
+
123
+ mattr_accessor :belongs_to_required_by_default, instance_accessor: false
124
+
125
+ mattr_accessor :connection_handlers, instance_accessor: false, default: {}
126
+
127
+ mattr_accessor :writing_role, instance_accessor: false, default: :writing
128
+
129
+ mattr_accessor :reading_role, instance_accessor: false, default: :reading
130
+
131
+ class_attribute :default_connection_handler, instance_writer: false
132
+
133
+ self.filter_attributes = []
134
+
135
+ def self.connection_handler
136
+ Thread.current.thread_variable_get("ar_connection_handler") || default_connection_handler
137
+ end
138
+
139
+ def self.connection_handler=(handler)
140
+ Thread.current.thread_variable_set("ar_connection_handler", handler)
141
+ end
142
+
143
+ self.default_connection_handler = ConnectionAdapters::ConnectionHandler.new
144
+ end
145
+
146
+ module ClassMethods
147
+ def initialize_find_by_cache # :nodoc:
148
+ @find_by_statement_cache = { true => Concurrent::Map.new, false => Concurrent::Map.new }
149
+ end
150
+
151
+ def inherited(child_class) # :nodoc:
152
+ # initialize cache at class definition for thread safety
153
+ child_class.initialize_find_by_cache
154
+ super
155
+ end
156
+
157
+ def find(*ids) # :nodoc:
158
+ # We don't have cache keys for this stuff yet
159
+ return super unless ids.length == 1
160
+ return super if block_given? ||
161
+ primary_key.nil? ||
162
+ scope_attributes? ||
163
+ columns_hash.key?(inheritance_column) && !base_class?
164
+
165
+ id = ids.first
166
+
167
+ return super if StatementCache.unsupported_value?(id)
168
+
169
+ key = primary_key
170
+
171
+ statement = cached_find_by_statement(key) { |params|
172
+ where(key => params.bind).limit(1)
173
+ }
174
+
175
+ record = statement.execute([id], connection)&.first
176
+ unless record
177
+ raise RecordNotFound.new("Couldn't find #{name} with '#{key}'=#{id}", name, key, id)
178
+ end
179
+ record
180
+ end
181
+
182
+ def find_by(*args) # :nodoc:
183
+ return super if scope_attributes? || reflect_on_all_aggregations.any? ||
184
+ columns_hash.key?(inheritance_column) && !base_class?
185
+
186
+ hash = args.first
187
+
188
+ return super if !(Hash === hash) || hash.values.any? { |v|
189
+ StatementCache.unsupported_value?(v)
190
+ }
191
+
192
+ # We can't cache Post.find_by(author: david) ...yet
193
+ return super unless hash.keys.all? { |k| columns_hash.has_key?(k.to_s) }
194
+
195
+ keys = hash.keys
196
+
197
+ statement = cached_find_by_statement(keys) { |params|
198
+ wheres = keys.each_with_object({}) { |param, o|
199
+ o[param] = params.bind
200
+ }
201
+ where(wheres).limit(1)
202
+ }
203
+ begin
204
+ statement.execute(hash.values, connection)&.first
205
+ rescue TypeError
206
+ raise ActiveRecord::StatementInvalid
207
+ end
208
+ end
209
+
210
+ def find_by!(*args) # :nodoc:
211
+ find_by(*args) || raise(RecordNotFound.new("Couldn't find #{name}", name))
212
+ end
213
+
214
+ def initialize_generated_modules # :nodoc:
215
+ generated_association_methods
216
+ end
217
+
218
+ def generated_association_methods # :nodoc:
219
+ @generated_association_methods ||= begin
220
+ mod = const_set(:GeneratedAssociationMethods, Module.new)
221
+ private_constant :GeneratedAssociationMethods
222
+ include mod
223
+
224
+ mod
225
+ end
226
+ end
227
+
228
+ # Returns columns which shouldn't be exposed while calling +#inspect+.
229
+ def filter_attributes
230
+ if defined?(@filter_attributes)
231
+ @filter_attributes
232
+ else
233
+ superclass.filter_attributes
234
+ end
235
+ end
236
+
237
+ # Specifies columns which shouldn't be exposed while calling +#inspect+.
238
+ attr_writer :filter_attributes
239
+
240
+ # Returns a string like 'Post(id:integer, title:string, body:text)'
241
+ def inspect # :nodoc:
242
+ if self == Base
243
+ super
244
+ elsif abstract_class?
245
+ "#{super}(abstract)"
246
+ elsif !connected?
247
+ "#{super} (call '#{super}.connection' to establish a connection)"
248
+ elsif table_exists?
249
+ attr_list = attribute_types.map { |name, type| "#{name}: #{type.type}" } * ", "
250
+ "#{super}(#{attr_list})"
251
+ else
252
+ "#{super}(Table doesn't exist)"
253
+ end
254
+ end
255
+
256
+ # Overwrite the default class equality method to provide support for decorated models.
257
+ def ===(object) # :nodoc:
258
+ object.is_a?(self)
259
+ end
260
+
261
+ # Returns an instance of <tt>Arel::Table</tt> loaded with the current table name.
262
+ #
263
+ # class Post < ActiveRecord::Base
264
+ # scope :published_and_commented, -> { published.and(arel_table[:comments_count].gt(0)) }
265
+ # end
266
+ def arel_table # :nodoc:
267
+ @arel_table ||= Arel::Table.new(table_name, type_caster: type_caster)
268
+ end
269
+
270
+ def arel_attribute(name, table = arel_table) # :nodoc:
271
+ name = name.to_s
272
+ name = attribute_aliases[name] || name
273
+ table[name]
274
+ end
275
+
276
+ def predicate_builder # :nodoc:
277
+ @predicate_builder ||= PredicateBuilder.new(table_metadata)
278
+ end
279
+
280
+ def type_caster # :nodoc:
281
+ TypeCaster::Map.new(self)
282
+ end
283
+
284
+ def _internal? # :nodoc:
285
+ false
286
+ end
287
+
288
+ private
289
+
290
+ def cached_find_by_statement(key, &block)
291
+ cache = @find_by_statement_cache[connection.prepared_statements]
292
+ cache.compute_if_absent(key) { StatementCache.create(connection, &block) }
293
+ end
294
+
295
+ def relation
296
+ relation = Relation.create(self)
297
+
298
+ if finder_needs_type_condition? && !ignore_default_scope?
299
+ relation.where!(type_condition)
300
+ relation.create_with!(inheritance_column.to_s => sti_name)
301
+ else
302
+ relation
303
+ end
304
+ end
305
+
306
+ def table_metadata
307
+ TableMetadata.new(self, arel_table)
308
+ end
309
+ end
310
+
311
+ # New objects can be instantiated as either empty (pass no construction parameter) or pre-set with
312
+ # attributes but not yet saved (pass a hash with key names matching the associated table column names).
313
+ # In both instances, valid attribute keys are determined by the column names of the associated table --
314
+ # hence you can't have attributes that aren't part of the table columns.
315
+ #
316
+ # ==== Example:
317
+ # # Instantiates a single new object
318
+ # User.new(first_name: 'Jamie')
319
+ def initialize(attributes = nil)
320
+ @new_record = true
321
+ @attributes = self.class._default_attributes.deep_dup
322
+
323
+ init_internals
324
+ initialize_internals_callback
325
+
326
+ assign_attributes(attributes) if attributes
327
+
328
+ yield self if block_given?
329
+ _run_initialize_callbacks
330
+ end
331
+
332
+ # Initialize an empty model object from +coder+. +coder+ should be
333
+ # the result of previously encoding an Active Record model, using
334
+ # #encode_with.
335
+ #
336
+ # class Post < ActiveRecord::Base
337
+ # end
338
+ #
339
+ # old_post = Post.new(title: "hello world")
340
+ # coder = {}
341
+ # old_post.encode_with(coder)
342
+ #
343
+ # post = Post.allocate
344
+ # post.init_with(coder)
345
+ # post.title # => 'hello world'
346
+ def init_with(coder, &block)
347
+ coder = LegacyYamlAdapter.convert(self.class, coder)
348
+ attributes = self.class.yaml_encoder.decode(coder)
349
+ init_with_attributes(attributes, coder["new_record"], &block)
350
+ end
351
+
352
+ ##
353
+ # Initialize an empty model object from +attributes+.
354
+ # +attributes+ should be an attributes object, and unlike the
355
+ # `initialize` method, no assignment calls are made per attribute.
356
+ def init_with_attributes(attributes, new_record = false) # :nodoc:
357
+ @new_record = new_record
358
+ @attributes = attributes
359
+
360
+ init_internals
361
+
362
+ yield self if block_given?
363
+
364
+ _run_find_callbacks
365
+ _run_initialize_callbacks
366
+
367
+ self
368
+ end
369
+
370
+ ##
371
+ # :method: clone
372
+ # Identical to Ruby's clone method. This is a "shallow" copy. Be warned that your attributes are not copied.
373
+ # That means that modifying attributes of the clone will modify the original, since they will both point to the
374
+ # same attributes hash. If you need a copy of your attributes hash, please use the #dup method.
375
+ #
376
+ # user = User.first
377
+ # new_user = user.clone
378
+ # user.name # => "Bob"
379
+ # new_user.name = "Joe"
380
+ # user.name # => "Joe"
381
+ #
382
+ # user.object_id == new_user.object_id # => false
383
+ # user.name.object_id == new_user.name.object_id # => true
384
+ #
385
+ # user.name.object_id == user.dup.name.object_id # => false
386
+
387
+ ##
388
+ # :method: dup
389
+ # Duped objects have no id assigned and are treated as new records. Note
390
+ # that this is a "shallow" copy as it copies the object's attributes
391
+ # only, not its associations. The extent of a "deep" copy is application
392
+ # specific and is therefore left to the application to implement according
393
+ # to its need.
394
+ # The dup method does not preserve the timestamps (created|updated)_(at|on).
395
+
396
+ ##
397
+ def initialize_dup(other) # :nodoc:
398
+ @attributes = @attributes.deep_dup
399
+ @attributes.reset(@primary_key)
400
+
401
+ _run_initialize_callbacks
402
+
403
+ @new_record = true
404
+ @destroyed = false
405
+ @_start_transaction_state = nil
406
+ @transaction_state = nil
407
+
408
+ super
409
+ end
410
+
411
+ # Populate +coder+ with attributes about this record that should be
412
+ # serialized. The structure of +coder+ defined in this method is
413
+ # guaranteed to match the structure of +coder+ passed to the #init_with
414
+ # method.
415
+ #
416
+ # Example:
417
+ #
418
+ # class Post < ActiveRecord::Base
419
+ # end
420
+ # coder = {}
421
+ # Post.new.encode_with(coder)
422
+ # coder # => {"attributes" => {"id" => nil, ... }}
423
+ def encode_with(coder)
424
+ self.class.yaml_encoder.encode(@attributes, coder)
425
+ coder["new_record"] = new_record?
426
+ coder["active_record_yaml_version"] = 2
427
+ end
428
+
429
+ # Returns true if +comparison_object+ is the same exact object, or +comparison_object+
430
+ # is of the same type and +self+ has an ID and it is equal to +comparison_object.id+.
431
+ #
432
+ # Note that new records are different from any other record by definition, unless the
433
+ # other record is the receiver itself. Besides, if you fetch existing records with
434
+ # +select+ and leave the ID out, you're on your own, this predicate will return false.
435
+ #
436
+ # Note also that destroying a record preserves its ID in the model instance, so deleted
437
+ # models are still comparable.
438
+ def ==(comparison_object)
439
+ super ||
440
+ comparison_object.instance_of?(self.class) &&
441
+ !id.nil? &&
442
+ comparison_object.id == id
443
+ end
444
+ alias :eql? :==
445
+
446
+ # Delegates to id in order to allow two records of the same type and id to work with something like:
447
+ # [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
448
+ def hash
449
+ if id
450
+ self.class.hash ^ id.hash
451
+ else
452
+ super
453
+ end
454
+ end
455
+
456
+ # Clone and freeze the attributes hash such that associations are still
457
+ # accessible, even on destroyed records, but cloned models will not be
458
+ # frozen.
459
+ def freeze
460
+ @attributes = @attributes.clone.freeze
461
+ self
462
+ end
463
+
464
+ # Returns +true+ if the attributes hash has been frozen.
465
+ def frozen?
466
+ sync_with_transaction_state if @transaction_state&.finalized?
467
+ @attributes.frozen?
468
+ end
469
+
470
+ # Allows sort on objects
471
+ def <=>(other_object)
472
+ if other_object.is_a?(self.class)
473
+ to_key <=> other_object.to_key
474
+ else
475
+ super
476
+ end
477
+ end
478
+
479
+ def present? # :nodoc:
480
+ true
481
+ end
482
+
483
+ def blank? # :nodoc:
484
+ false
485
+ end
486
+
487
+ # Returns +true+ if the record is read only. Records loaded through joins with piggy-back
488
+ # attributes will be marked as read only since they cannot be saved.
489
+ def readonly?
490
+ @readonly
491
+ end
492
+
493
+ # Marks this record as read only.
494
+ def readonly!
495
+ @readonly = true
496
+ end
497
+
498
+ def connection_handler
499
+ self.class.connection_handler
500
+ end
501
+
502
+ # Returns the contents of the record as a nicely formatted string.
503
+ def inspect
504
+ # We check defined?(@attributes) not to issue warnings if the object is
505
+ # allocated but not initialized.
506
+ inspection = if defined?(@attributes) && @attributes
507
+ self.class.attribute_names.collect do |name|
508
+ if has_attribute?(name)
509
+ attr = _read_attribute(name)
510
+ value = if attr.nil?
511
+ attr.inspect
512
+ else
513
+ attr = format_for_inspect(attr)
514
+ inspection_filter.filter_param(name, attr)
515
+ end
516
+ "#{name}: #{value}"
517
+ end
518
+ end.compact.join(", ")
519
+ else
520
+ "not initialized"
521
+ end
522
+
523
+ "#<#{self.class} #{inspection}>"
524
+ end
525
+
526
+ # Takes a PP and prettily prints this record to it, allowing you to get a nice result from <tt>pp record</tt>
527
+ # when pp is required.
528
+ def pretty_print(pp)
529
+ return super if custom_inspect_method_defined?
530
+ pp.object_address_group(self) do
531
+ if defined?(@attributes) && @attributes
532
+ attr_names = self.class.attribute_names.select { |name| has_attribute?(name) }
533
+ pp.seplist(attr_names, proc { pp.text "," }) do |attr_name|
534
+ pp.breakable " "
535
+ pp.group(1) do
536
+ pp.text attr_name
537
+ pp.text ":"
538
+ pp.breakable
539
+ value = _read_attribute(attr_name)
540
+ value = inspection_filter.filter_param(attr_name, value) unless value.nil?
541
+ pp.pp value
542
+ end
543
+ end
544
+ else
545
+ pp.breakable " "
546
+ pp.text "not initialized"
547
+ end
548
+ end
549
+ end
550
+
551
+ # Returns a hash of the given methods with their names as keys and returned values as values.
552
+ def slice(*methods)
553
+ Hash[methods.flatten.map! { |method| [method, public_send(method)] }].with_indifferent_access
554
+ end
555
+
556
+ private
557
+
558
+ # +Array#flatten+ will call +#to_ary+ (recursively) on each of the elements of
559
+ # the array, and then rescues from the possible +NoMethodError+. If those elements are
560
+ # +ActiveRecord::Base+'s, then this triggers the various +method_missing+'s that we have,
561
+ # which significantly impacts upon performance.
562
+ #
563
+ # So we can avoid the +method_missing+ hit by explicitly defining +#to_ary+ as +nil+ here.
564
+ #
565
+ # See also https://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary.html
566
+ def to_ary
567
+ nil
568
+ end
569
+
570
+ def init_internals
571
+ @primary_key = self.class.primary_key
572
+ @readonly = false
573
+ @destroyed = false
574
+ @marked_for_destruction = false
575
+ @destroyed_by_association = nil
576
+ @_start_transaction_state = nil
577
+ @transaction_state = nil
578
+
579
+ self.class.define_attribute_methods
580
+ end
581
+
582
+ def initialize_internals_callback
583
+ end
584
+
585
+ def custom_inspect_method_defined?
586
+ self.class.instance_method(:inspect).owner != ActiveRecord::Base.instance_method(:inspect).owner
587
+ end
588
+
589
+ class InspectionMask < DelegateClass(::String)
590
+ def pretty_print(pp)
591
+ pp.text __getobj__
592
+ end
593
+ end
594
+ private_constant :InspectionMask
595
+
596
+ def inspection_filter
597
+ @inspection_filter ||= begin
598
+ mask = InspectionMask.new(ActiveSupport::ParameterFilter::FILTERED)
599
+ ActiveSupport::ParameterFilter.new(self.class.filter_attributes, mask: mask)
600
+ end
601
+ end
602
+ end
603
+ end