activerecord 6.0.0

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 +1013 -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 +40 -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 +332 -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 +258 -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 +508 -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 +1165 -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 +512 -0
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +154 -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 +761 -0
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +821 -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 +200 -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 +182 -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 +949 -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 +118 -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 +557 -0
  127. data/lib/active_record/connection_adapters/statement_pool.rb +61 -0
  128. data/lib/active_record/connection_handling.rb +267 -0
  129. data/lib/active_record/core.rb +599 -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 +92 -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 +542 -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 +859 -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 +552 -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 +1359 -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 +51 -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 +415 -0
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Associations
5
+ class SingularAssociation < Association #:nodoc:
6
+ # Implements the reader method, e.g. foo.bar for Foo.has_one :bar
7
+ def reader
8
+ if !loaded? || stale_target?
9
+ reload
10
+ end
11
+
12
+ target
13
+ end
14
+
15
+ # Implements the writer method, e.g. foo.bar= for Foo.belongs_to :bar
16
+ def writer(record)
17
+ replace(record)
18
+ end
19
+
20
+ def build(attributes = {}, &block)
21
+ record = build_record(attributes, &block)
22
+ set_new_record(record)
23
+ record
24
+ end
25
+
26
+ # Implements the reload reader method, e.g. foo.reload_bar for
27
+ # Foo.has_one :bar
28
+ def force_reload_reader
29
+ reload(true)
30
+ target
31
+ end
32
+
33
+ private
34
+ def scope_for_create
35
+ super.except!(klass.primary_key)
36
+ end
37
+
38
+ def find_target
39
+ super.first
40
+ end
41
+
42
+ def replace(record)
43
+ raise NotImplementedError, "Subclasses must implement a replace(record) method"
44
+ end
45
+
46
+ def set_new_record(record)
47
+ replace(record)
48
+ end
49
+
50
+ def _create_record(attributes, raise_error = false, &block)
51
+ record = build_record(attributes, &block)
52
+ saved = record.save
53
+ set_new_record(record)
54
+ raise RecordInvalid.new(record) if !saved && raise_error
55
+ record
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Associations
5
+ # = Active Record Through Association
6
+ module ThroughAssociation #:nodoc:
7
+ delegate :source_reflection, to: :reflection
8
+
9
+ private
10
+ def through_reflection
11
+ @through_reflection ||= begin
12
+ refl = reflection.through_reflection
13
+
14
+ while refl.through_reflection?
15
+ refl = refl.through_reflection
16
+ end
17
+
18
+ refl
19
+ end
20
+ end
21
+
22
+ def through_association
23
+ @through_association ||= owner.association(through_reflection.name)
24
+ end
25
+
26
+ # We merge in these scopes for two reasons:
27
+ #
28
+ # 1. To get the default_scope conditions for any of the other reflections in the chain
29
+ # 2. To get the type conditions for any STI models in the chain
30
+ def target_scope
31
+ scope = super
32
+ reflection.chain.drop(1).each do |reflection|
33
+ relation = reflection.klass.scope_for_association
34
+ scope.merge!(
35
+ relation.except(:select, :create_with, :includes, :preload, :joins, :eager_load)
36
+ )
37
+ end
38
+ scope
39
+ end
40
+
41
+ # Construct attributes for :through pointing to owner and associate. This is used by the
42
+ # methods which create and delete records on the association.
43
+ #
44
+ # We only support indirectly modifying through associations which have a belongs_to source.
45
+ # This is the "has_many :tags, through: :taggings" situation, where the join model
46
+ # typically has a belongs_to on both side. In other words, associations which could also
47
+ # be represented as has_and_belongs_to_many associations.
48
+ #
49
+ # We do not support creating/deleting records on the association where the source has
50
+ # some other type, because this opens up a whole can of worms, and in basically any
51
+ # situation it is more natural for the user to just create or modify their join records
52
+ # directly as required.
53
+ def construct_join_attributes(*records)
54
+ ensure_mutable
55
+
56
+ association_primary_key = source_reflection.association_primary_key(reflection.klass)
57
+
58
+ if association_primary_key == reflection.klass.primary_key && !options[:source_type]
59
+ join_attributes = { source_reflection.name => records }
60
+ else
61
+ join_attributes = {
62
+ source_reflection.foreign_key => records.map(&association_primary_key.to_sym)
63
+ }
64
+ end
65
+
66
+ if options[:source_type]
67
+ join_attributes[source_reflection.foreign_type] = [ options[:source_type] ]
68
+ end
69
+
70
+ if records.count == 1
71
+ join_attributes.transform_values!(&:first)
72
+ else
73
+ join_attributes
74
+ end
75
+ end
76
+
77
+ # Note: this does not capture all cases, for example it would be crazy to try to
78
+ # properly support stale-checking for nested associations.
79
+ def stale_state
80
+ if through_reflection.belongs_to?
81
+ owner[through_reflection.foreign_key] && owner[through_reflection.foreign_key].to_s
82
+ end
83
+ end
84
+
85
+ def foreign_key_present?
86
+ through_reflection.belongs_to? && !owner[through_reflection.foreign_key].nil?
87
+ end
88
+
89
+ def ensure_mutable
90
+ unless source_reflection.belongs_to?
91
+ if reflection.has_one?
92
+ raise HasOneThroughCantAssociateThroughHasOneOrManyReflection.new(owner, reflection)
93
+ else
94
+ raise HasManyThroughCantAssociateThroughHasOneOrManyReflection.new(owner, reflection)
95
+ end
96
+ end
97
+ end
98
+
99
+ def ensure_not_nested
100
+ if reflection.nested?
101
+ if reflection.has_one?
102
+ raise HasOneThroughNestedAssociationsAreReadonly.new(owner, reflection)
103
+ else
104
+ raise HasManyThroughNestedAssociationsAreReadonly.new(owner, reflection)
105
+ end
106
+ end
107
+ end
108
+
109
+ def build_record(attributes)
110
+ inverse = source_reflection.inverse_of
111
+ target = through_association.target
112
+
113
+ if inverse && target && !target.is_a?(Array)
114
+ attributes[inverse.foreign_key] = target.id
115
+ end
116
+
117
+ super
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_model/forbidden_attributes_protection"
4
+
5
+ module ActiveRecord
6
+ module AttributeAssignment
7
+ include ActiveModel::AttributeAssignment
8
+
9
+ private
10
+
11
+ def _assign_attributes(attributes)
12
+ multi_parameter_attributes = {}
13
+ nested_parameter_attributes = {}
14
+
15
+ attributes.each do |k, v|
16
+ if k.include?("(")
17
+ multi_parameter_attributes[k] = attributes.delete(k)
18
+ elsif v.is_a?(Hash)
19
+ nested_parameter_attributes[k] = attributes.delete(k)
20
+ end
21
+ end
22
+ super(attributes)
23
+
24
+ assign_nested_parameter_attributes(nested_parameter_attributes) unless nested_parameter_attributes.empty?
25
+ assign_multiparameter_attributes(multi_parameter_attributes) unless multi_parameter_attributes.empty?
26
+ end
27
+
28
+ # Assign any deferred nested attributes after the base attributes have been set.
29
+ def assign_nested_parameter_attributes(pairs)
30
+ pairs.each { |k, v| _assign_attribute(k, v) }
31
+ end
32
+
33
+ # Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
34
+ # by calling new on the column type or aggregation type (through composed_of) object with these parameters.
35
+ # So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
36
+ # written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
37
+ # parentheses to have the parameters typecasted before they're used in the constructor. Use i for Integer and
38
+ # f for Float. If all the values for a given attribute are empty, the attribute will be set to +nil+.
39
+ def assign_multiparameter_attributes(pairs)
40
+ execute_callstack_for_multiparameter_attributes(
41
+ extract_callstack_for_multiparameter_attributes(pairs)
42
+ )
43
+ end
44
+
45
+ def execute_callstack_for_multiparameter_attributes(callstack)
46
+ errors = []
47
+ callstack.each do |name, values_with_empty_parameters|
48
+ if values_with_empty_parameters.each_value.all?(&:nil?)
49
+ values = nil
50
+ else
51
+ values = values_with_empty_parameters
52
+ end
53
+ send("#{name}=", values)
54
+ rescue => ex
55
+ errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name)
56
+ end
57
+ unless errors.empty?
58
+ error_descriptions = errors.map(&:message).join(",")
59
+ raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes [#{error_descriptions}]"
60
+ end
61
+ end
62
+
63
+ def extract_callstack_for_multiparameter_attributes(pairs)
64
+ attributes = {}
65
+
66
+ pairs.each do |(multiparameter_name, value)|
67
+ attribute_name = multiparameter_name.split("(").first
68
+ attributes[attribute_name] ||= {}
69
+
70
+ parameter_value = value.empty? ? nil : type_cast_attribute_value(multiparameter_name, value)
71
+ attributes[attribute_name][find_parameter_position(multiparameter_name)] ||= parameter_value
72
+ end
73
+
74
+ attributes
75
+ end
76
+
77
+ def type_cast_attribute_value(multiparameter_name, value)
78
+ multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value
79
+ end
80
+
81
+ def find_parameter_position(multiparameter_name)
82
+ multiparameter_name.scan(/\(([0-9]*).*\)/).first.first.to_i
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module AttributeDecorators # :nodoc:
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ class_attribute :attribute_type_decorations, instance_accessor: false, default: TypeDecorator.new # :internal:
9
+ end
10
+
11
+ module ClassMethods # :nodoc:
12
+ # This method is an internal API used to create class macros such as
13
+ # +serialize+, and features like time zone aware attributes.
14
+ #
15
+ # Used to wrap the type of an attribute in a new type.
16
+ # When the schema for a model is loaded, attributes with the same name as
17
+ # +column_name+ will have their type yielded to the given block. The
18
+ # return value of that block will be used instead.
19
+ #
20
+ # Subsequent calls where +column_name+ and +decorator_name+ are the same
21
+ # will override the previous decorator, not decorate twice. This can be
22
+ # used to create idempotent class macros like +serialize+
23
+ def decorate_attribute_type(column_name, decorator_name, &block)
24
+ matcher = ->(name, _) { name == column_name.to_s }
25
+ key = "_#{column_name}_#{decorator_name}"
26
+ decorate_matching_attribute_types(matcher, key, &block)
27
+ end
28
+
29
+ # This method is an internal API used to create higher level features like
30
+ # time zone aware attributes.
31
+ #
32
+ # When the schema for a model is loaded, +matcher+ will be called for each
33
+ # attribute with its name and type. If the matcher returns a truthy value,
34
+ # the type will then be yielded to the given block, and the return value
35
+ # of that block will replace the type.
36
+ #
37
+ # Subsequent calls to this method with the same value for +decorator_name+
38
+ # will replace the previous decorator, not decorate twice. This can be
39
+ # used to ensure that class macros are idempotent.
40
+ def decorate_matching_attribute_types(matcher, decorator_name, &block)
41
+ reload_schema_from_cache
42
+ decorator_name = decorator_name.to_s
43
+
44
+ # Create new hashes so we don't modify parent classes
45
+ self.attribute_type_decorations = attribute_type_decorations.merge(decorator_name => [matcher, block])
46
+ end
47
+
48
+ private
49
+
50
+ def load_schema!
51
+ super
52
+ attribute_types.each do |name, type|
53
+ decorated_type = attribute_type_decorations.apply(name, type)
54
+ define_attribute(name, decorated_type)
55
+ end
56
+ end
57
+ end
58
+
59
+ class TypeDecorator # :nodoc:
60
+ delegate :clear, to: :@decorations
61
+
62
+ def initialize(decorations = {})
63
+ @decorations = decorations
64
+ end
65
+
66
+ def merge(*args)
67
+ TypeDecorator.new(@decorations.merge(*args))
68
+ end
69
+
70
+ def apply(name, type)
71
+ decorations = decorators_for(name, type)
72
+ decorations.inject(type) do |new_type, block|
73
+ block.call(new_type)
74
+ end
75
+ end
76
+
77
+ private
78
+
79
+ def decorators_for(name, type)
80
+ matching(name, type).map(&:last)
81
+ end
82
+
83
+ def matching(name, type)
84
+ @decorations.values.select do |(matcher, _)|
85
+ matcher.call(name, type)
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,420 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mutex_m"
4
+
5
+ module ActiveRecord
6
+ # = Active Record Attribute Methods
7
+ module AttributeMethods
8
+ extend ActiveSupport::Concern
9
+ include ActiveModel::AttributeMethods
10
+
11
+ included do
12
+ initialize_generated_modules
13
+ include Read
14
+ include Write
15
+ include BeforeTypeCast
16
+ include Query
17
+ include PrimaryKey
18
+ include TimeZoneConversion
19
+ include Dirty
20
+ include Serialization
21
+
22
+ delegate :column_for_attribute, to: :class
23
+ end
24
+
25
+ RESTRICTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
26
+
27
+ class GeneratedAttributeMethods < Module #:nodoc:
28
+ include Mutex_m
29
+ end
30
+
31
+ module ClassMethods
32
+ def inherited(child_class) #:nodoc:
33
+ child_class.initialize_generated_modules
34
+ super
35
+ end
36
+
37
+ def initialize_generated_modules # :nodoc:
38
+ @generated_attribute_methods = const_set(:GeneratedAttributeMethods, GeneratedAttributeMethods.new)
39
+ private_constant :GeneratedAttributeMethods
40
+ @attribute_methods_generated = false
41
+ include @generated_attribute_methods
42
+
43
+ super
44
+ end
45
+
46
+ # Generates all the attribute related methods for columns in the database
47
+ # accessors, mutators and query methods.
48
+ def define_attribute_methods # :nodoc:
49
+ return false if @attribute_methods_generated
50
+ # Use a mutex; we don't want two threads simultaneously trying to define
51
+ # attribute methods.
52
+ generated_attribute_methods.synchronize do
53
+ return false if @attribute_methods_generated
54
+ superclass.define_attribute_methods unless base_class?
55
+ super(attribute_names)
56
+ @attribute_methods_generated = true
57
+ end
58
+ end
59
+
60
+ def undefine_attribute_methods # :nodoc:
61
+ generated_attribute_methods.synchronize do
62
+ super if defined?(@attribute_methods_generated) && @attribute_methods_generated
63
+ @attribute_methods_generated = false
64
+ end
65
+ end
66
+
67
+ # Raises an ActiveRecord::DangerousAttributeError exception when an
68
+ # \Active \Record method is defined in the model, otherwise +false+.
69
+ #
70
+ # class Person < ActiveRecord::Base
71
+ # def save
72
+ # 'already defined by Active Record'
73
+ # end
74
+ # end
75
+ #
76
+ # Person.instance_method_already_implemented?(:save)
77
+ # # => ActiveRecord::DangerousAttributeError: save is defined by Active Record. Check to make sure that you don't have an attribute or method with the same name.
78
+ #
79
+ # Person.instance_method_already_implemented?(:name)
80
+ # # => false
81
+ def instance_method_already_implemented?(method_name)
82
+ if dangerous_attribute_method?(method_name)
83
+ raise DangerousAttributeError, "#{method_name} is defined by Active Record. Check to make sure that you don't have an attribute or method with the same name."
84
+ end
85
+
86
+ if superclass == Base
87
+ super
88
+ else
89
+ # If ThisClass < ... < SomeSuperClass < ... < Base and SomeSuperClass
90
+ # defines its own attribute method, then we don't want to overwrite that.
91
+ defined = method_defined_within?(method_name, superclass, Base) &&
92
+ ! superclass.instance_method(method_name).owner.is_a?(GeneratedAttributeMethods)
93
+ defined || super
94
+ end
95
+ end
96
+
97
+ # A method name is 'dangerous' if it is already (re)defined by Active Record, but
98
+ # not by any ancestors. (So 'puts' is not dangerous but 'save' is.)
99
+ def dangerous_attribute_method?(name) # :nodoc:
100
+ method_defined_within?(name, Base)
101
+ end
102
+
103
+ def method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc:
104
+ if klass.method_defined?(name) || klass.private_method_defined?(name)
105
+ if superklass.method_defined?(name) || superklass.private_method_defined?(name)
106
+ klass.instance_method(name).owner != superklass.instance_method(name).owner
107
+ else
108
+ true
109
+ end
110
+ else
111
+ false
112
+ end
113
+ end
114
+
115
+ # A class method is 'dangerous' if it is already (re)defined by Active Record, but
116
+ # not by any ancestors. (So 'puts' is not dangerous but 'new' is.)
117
+ def dangerous_class_method?(method_name)
118
+ RESTRICTED_CLASS_METHODS.include?(method_name.to_s) || class_method_defined_within?(method_name, Base)
119
+ end
120
+
121
+ def class_method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc:
122
+ if klass.respond_to?(name, true)
123
+ if superklass.respond_to?(name, true)
124
+ klass.method(name).owner != superklass.method(name).owner
125
+ else
126
+ true
127
+ end
128
+ else
129
+ false
130
+ end
131
+ end
132
+
133
+ # Returns +true+ if +attribute+ is an attribute method and table exists,
134
+ # +false+ otherwise.
135
+ #
136
+ # class Person < ActiveRecord::Base
137
+ # end
138
+ #
139
+ # Person.attribute_method?('name') # => true
140
+ # Person.attribute_method?(:age=) # => true
141
+ # Person.attribute_method?(:nothing) # => false
142
+ def attribute_method?(attribute)
143
+ super || (table_exists? && column_names.include?(attribute.to_s.sub(/=$/, "")))
144
+ end
145
+
146
+ # Returns an array of column names as strings if it's not an abstract class and
147
+ # table exists. Otherwise it returns an empty array.
148
+ #
149
+ # class Person < ActiveRecord::Base
150
+ # end
151
+ #
152
+ # Person.attribute_names
153
+ # # => ["id", "created_at", "updated_at", "name", "age"]
154
+ def attribute_names
155
+ @attribute_names ||= if !abstract_class? && table_exists?
156
+ attribute_types.keys
157
+ else
158
+ []
159
+ end
160
+ end
161
+
162
+ # Returns true if the given attribute exists, otherwise false.
163
+ #
164
+ # class Person < ActiveRecord::Base
165
+ # end
166
+ #
167
+ # Person.has_attribute?('name') # => true
168
+ # Person.has_attribute?(:age) # => true
169
+ # Person.has_attribute?(:nothing) # => false
170
+ def has_attribute?(attr_name)
171
+ attribute_types.key?(attr_name.to_s)
172
+ end
173
+
174
+ # Returns the column object for the named attribute.
175
+ # Returns a +ActiveRecord::ConnectionAdapters::NullColumn+ if the
176
+ # named attribute does not exist.
177
+ #
178
+ # class Person < ActiveRecord::Base
179
+ # end
180
+ #
181
+ # person = Person.new
182
+ # person.column_for_attribute(:name) # the result depends on the ConnectionAdapter
183
+ # # => #<ActiveRecord::ConnectionAdapters::Column:0x007ff4ab083980 @name="name", @sql_type="varchar(255)", @null=true, ...>
184
+ #
185
+ # person.column_for_attribute(:nothing)
186
+ # # => #<ActiveRecord::ConnectionAdapters::NullColumn:0xXXX @name=nil, @sql_type=nil, @cast_type=#<Type::Value>, ...>
187
+ def column_for_attribute(name)
188
+ name = name.to_s
189
+ columns_hash.fetch(name) do
190
+ ConnectionAdapters::NullColumn.new(name)
191
+ end
192
+ end
193
+ end
194
+
195
+ # A Person object with a name attribute can ask <tt>person.respond_to?(:name)</tt>,
196
+ # <tt>person.respond_to?(:name=)</tt>, and <tt>person.respond_to?(:name?)</tt>
197
+ # which will all return +true+. It also defines the attribute methods if they have
198
+ # not been generated.
199
+ #
200
+ # class Person < ActiveRecord::Base
201
+ # end
202
+ #
203
+ # person = Person.new
204
+ # person.respond_to?(:name) # => true
205
+ # person.respond_to?(:name=) # => true
206
+ # person.respond_to?(:name?) # => true
207
+ # person.respond_to?('age') # => true
208
+ # person.respond_to?('age=') # => true
209
+ # person.respond_to?('age?') # => true
210
+ # person.respond_to?(:nothing) # => false
211
+ def respond_to?(name, include_private = false)
212
+ return false unless super
213
+
214
+ # If the result is true then check for the select case.
215
+ # For queries selecting a subset of columns, return false for unselected columns.
216
+ # We check defined?(@attributes) not to issue warnings if called on objects that
217
+ # have been allocated but not yet initialized.
218
+ if defined?(@attributes)
219
+ if name = self.class.symbol_column_to_string(name.to_sym)
220
+ return has_attribute?(name)
221
+ end
222
+ end
223
+
224
+ true
225
+ end
226
+
227
+ # Returns +true+ if the given attribute is in the attributes hash, otherwise +false+.
228
+ #
229
+ # class Person < ActiveRecord::Base
230
+ # end
231
+ #
232
+ # person = Person.new
233
+ # person.has_attribute?(:name) # => true
234
+ # person.has_attribute?('age') # => true
235
+ # person.has_attribute?(:nothing) # => false
236
+ def has_attribute?(attr_name)
237
+ @attributes.key?(attr_name.to_s)
238
+ end
239
+
240
+ # Returns an array of names for the attributes available on this object.
241
+ #
242
+ # class Person < ActiveRecord::Base
243
+ # end
244
+ #
245
+ # person = Person.new
246
+ # person.attribute_names
247
+ # # => ["id", "created_at", "updated_at", "name", "age"]
248
+ def attribute_names
249
+ @attributes.keys
250
+ end
251
+
252
+ # Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
253
+ #
254
+ # class Person < ActiveRecord::Base
255
+ # end
256
+ #
257
+ # person = Person.create(name: 'Francesco', age: 22)
258
+ # person.attributes
259
+ # # => {"id"=>3, "created_at"=>Sun, 21 Oct 2012 04:53:04, "updated_at"=>Sun, 21 Oct 2012 04:53:04, "name"=>"Francesco", "age"=>22}
260
+ def attributes
261
+ @attributes.to_hash
262
+ end
263
+
264
+ # Returns an <tt>#inspect</tt>-like string for the value of the
265
+ # attribute +attr_name+. String attributes are truncated up to 50
266
+ # characters, Date and Time attributes are returned in the
267
+ # <tt>:db</tt> format. Other attributes return the value of
268
+ # <tt>#inspect</tt> without modification.
269
+ #
270
+ # person = Person.create!(name: 'David Heinemeier Hansson ' * 3)
271
+ #
272
+ # person.attribute_for_inspect(:name)
273
+ # # => "\"David Heinemeier Hansson David Heinemeier Hansson ...\""
274
+ #
275
+ # person.attribute_for_inspect(:created_at)
276
+ # # => "\"2012-10-22 00:15:07\""
277
+ #
278
+ # person.attribute_for_inspect(:tag_ids)
279
+ # # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]"
280
+ def attribute_for_inspect(attr_name)
281
+ value = _read_attribute(attr_name)
282
+ format_for_inspect(value)
283
+ end
284
+
285
+ # Returns +true+ if the specified +attribute+ has been set by the user or by a
286
+ # database load and is neither +nil+ nor <tt>empty?</tt> (the latter only applies
287
+ # to objects that respond to <tt>empty?</tt>, most notably Strings). Otherwise, +false+.
288
+ # Note that it always returns +true+ with boolean attributes.
289
+ #
290
+ # class Task < ActiveRecord::Base
291
+ # end
292
+ #
293
+ # task = Task.new(title: '', is_done: false)
294
+ # task.attribute_present?(:title) # => false
295
+ # task.attribute_present?(:is_done) # => true
296
+ # task.title = 'Buy milk'
297
+ # task.is_done = true
298
+ # task.attribute_present?(:title) # => true
299
+ # task.attribute_present?(:is_done) # => true
300
+ def attribute_present?(attribute)
301
+ value = _read_attribute(attribute)
302
+ !value.nil? && !(value.respond_to?(:empty?) && value.empty?)
303
+ end
304
+
305
+ # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
306
+ # "2004-12-12" in a date column is cast to a date object, like Date.new(2004, 12, 12)). It raises
307
+ # <tt>ActiveModel::MissingAttributeError</tt> if the identified attribute is missing.
308
+ #
309
+ # Note: +:id+ is always present.
310
+ #
311
+ # class Person < ActiveRecord::Base
312
+ # belongs_to :organization
313
+ # end
314
+ #
315
+ # person = Person.new(name: 'Francesco', age: '22')
316
+ # person[:name] # => "Francesco"
317
+ # person[:age] # => 22
318
+ #
319
+ # person = Person.select('id').first
320
+ # person[:name] # => ActiveModel::MissingAttributeError: missing attribute: name
321
+ # person[:organization_id] # => ActiveModel::MissingAttributeError: missing attribute: organization_id
322
+ def [](attr_name)
323
+ read_attribute(attr_name) { |n| missing_attribute(n, caller) }
324
+ end
325
+
326
+ # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
327
+ # (Alias for the protected #write_attribute method).
328
+ #
329
+ # class Person < ActiveRecord::Base
330
+ # end
331
+ #
332
+ # person = Person.new
333
+ # person[:age] = '22'
334
+ # person[:age] # => 22
335
+ # person[:age].class # => Integer
336
+ def []=(attr_name, value)
337
+ write_attribute(attr_name, value)
338
+ end
339
+
340
+ # Returns the name of all database fields which have been read from this
341
+ # model. This can be useful in development mode to determine which fields
342
+ # need to be selected. For performance critical pages, selecting only the
343
+ # required fields can be an easy performance win (assuming you aren't using
344
+ # all of the fields on the model).
345
+ #
346
+ # For example:
347
+ #
348
+ # class PostsController < ActionController::Base
349
+ # after_action :print_accessed_fields, only: :index
350
+ #
351
+ # def index
352
+ # @posts = Post.all
353
+ # end
354
+ #
355
+ # private
356
+ #
357
+ # def print_accessed_fields
358
+ # p @posts.first.accessed_fields
359
+ # end
360
+ # end
361
+ #
362
+ # Which allows you to quickly change your code to:
363
+ #
364
+ # class PostsController < ActionController::Base
365
+ # def index
366
+ # @posts = Post.select(:id, :title, :author_id, :updated_at)
367
+ # end
368
+ # end
369
+ def accessed_fields
370
+ @attributes.accessed
371
+ end
372
+
373
+ private
374
+ def attribute_method?(attr_name)
375
+ # We check defined? because Syck calls respond_to? before actually calling initialize.
376
+ defined?(@attributes) && @attributes.key?(attr_name)
377
+ end
378
+
379
+ def attributes_with_values(attribute_names)
380
+ attribute_names.each_with_object({}) do |name, attrs|
381
+ attrs[name] = _read_attribute(name)
382
+ end
383
+ end
384
+
385
+ # Filters the primary keys and readonly attributes from the attribute names.
386
+ def attributes_for_update(attribute_names)
387
+ attribute_names &= self.class.column_names
388
+ attribute_names.delete_if do |name|
389
+ readonly_attribute?(name)
390
+ end
391
+ end
392
+
393
+ # Filters out the primary keys, from the attribute names, when the primary
394
+ # key is to be generated (e.g. the id attribute has no value).
395
+ def attributes_for_create(attribute_names)
396
+ attribute_names &= self.class.column_names
397
+ attribute_names.delete_if do |name|
398
+ pk_attribute?(name) && id.nil?
399
+ end
400
+ end
401
+
402
+ def format_for_inspect(value)
403
+ if value.is_a?(String) && value.length > 50
404
+ "#{value[0, 50]}...".inspect
405
+ elsif value.is_a?(Date) || value.is_a?(Time)
406
+ %("#{value.to_s(:db)}")
407
+ else
408
+ value.inspect
409
+ end
410
+ end
411
+
412
+ def readonly_attribute?(name)
413
+ self.class.readonly_attributes.include?(name)
414
+ end
415
+
416
+ def pk_attribute?(name)
417
+ name == @primary_key
418
+ end
419
+ end
420
+ end