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,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/notifications"
4
+ require "active_record/explain_registry"
5
+
6
+ module ActiveRecord
7
+ class ExplainSubscriber # :nodoc:
8
+ def start(name, id, payload)
9
+ # unused
10
+ end
11
+
12
+ def finish(name, id, payload)
13
+ if ExplainRegistry.collect? && !ignore_payload?(payload)
14
+ ExplainRegistry.queries << payload.values_at(:sql, :binds)
15
+ end
16
+ end
17
+
18
+ # SCHEMA queries cannot be EXPLAINed, also we do not want to run EXPLAIN on
19
+ # our own EXPLAINs no matter how loopingly beautiful that would be.
20
+ #
21
+ # On the other hand, we want to monitor the performance of our real database
22
+ # queries, not the performance of the access to the query cache.
23
+ IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN)
24
+ EXPLAINED_SQLS = /\A\s*(with|select|update|delete|insert)\b/i
25
+ def ignore_payload?(payload)
26
+ payload[:exception] ||
27
+ payload[:cached] ||
28
+ IGNORED_PAYLOADS.include?(payload[:name]) ||
29
+ payload[:sql] !~ EXPLAINED_SQLS
30
+ end
31
+
32
+ ActiveSupport::Notifications.subscribe("sql.active_record", new)
33
+ end
34
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+ require "yaml"
5
+
6
+ module ActiveRecord
7
+ class FixtureSet
8
+ class File # :nodoc:
9
+ include Enumerable
10
+
11
+ ##
12
+ # Open a fixture file named +file+. When called with a block, the block
13
+ # is called with the filehandle and the filehandle is automatically closed
14
+ # when the block finishes.
15
+ def self.open(file)
16
+ x = new file
17
+ block_given? ? yield(x) : x
18
+ end
19
+
20
+ def initialize(file)
21
+ @file = file
22
+ end
23
+
24
+ def each(&block)
25
+ rows.each(&block)
26
+ end
27
+
28
+ def model_class
29
+ config_row["model_class"]
30
+ end
31
+
32
+ private
33
+ def rows
34
+ @rows ||= raw_rows.reject { |fixture_name, _| fixture_name == "_fixture" }
35
+ end
36
+
37
+ def config_row
38
+ @config_row ||= begin
39
+ row = raw_rows.find { |fixture_name, _| fixture_name == "_fixture" }
40
+ if row
41
+ row.last
42
+ else
43
+ { 'model_class': nil }
44
+ end
45
+ end
46
+ end
47
+
48
+ def raw_rows
49
+ @raw_rows ||= begin
50
+ data = YAML.load(render(IO.read(@file)))
51
+ data ? validate(data).to_a : []
52
+ rescue ArgumentError, Psych::SyntaxError => error
53
+ raise Fixture::FormatError, "a YAML error occurred parsing #{@file}. Please note that YAML must be consistently indented using spaces. Tabs are not allowed. Please have a look at http://www.yaml.org/faq.html\nThe exact error was:\n #{error.class}: #{error}", error.backtrace
54
+ end
55
+ end
56
+
57
+ def prepare_erb(content)
58
+ erb = ERB.new(content)
59
+ erb.filename = @file
60
+ erb
61
+ end
62
+
63
+ def render(content)
64
+ context = ActiveRecord::FixtureSet::RenderContext.create_subclass.new
65
+ prepare_erb(content).result(context.get_binding)
66
+ end
67
+
68
+ # Validate our unmarshalled data.
69
+ def validate(data)
70
+ unless Hash === data || YAML::Omap === data
71
+ raise Fixture::FormatError, "fixture is not a hash: #{@file}"
72
+ end
73
+
74
+ invalid = data.reject { |_, row| Hash === row }
75
+ if invalid.any?
76
+ raise Fixture::FormatError, "fixture key is not a hash: #{@file}, keys: #{invalid.keys.inspect}"
77
+ end
78
+ data
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ class FixtureSet
5
+ class ModelMetadata # :nodoc:
6
+ def initialize(model_class)
7
+ @model_class = model_class
8
+ end
9
+
10
+ def primary_key_name
11
+ @primary_key_name ||= @model_class && @model_class.primary_key
12
+ end
13
+
14
+ def primary_key_type
15
+ @primary_key_type ||= @model_class && @model_class.type_for_attribute(@model_class.primary_key).type
16
+ end
17
+
18
+ def has_primary_key_column?
19
+ @has_primary_key_column ||= primary_key_name &&
20
+ @model_class.columns.any? { |col| col.name == primary_key_name }
21
+ end
22
+
23
+ def timestamp_column_names
24
+ @timestamp_column_names ||=
25
+ %w(created_at created_on updated_at updated_on) & @model_class.column_names
26
+ end
27
+
28
+ def inheritance_column_name
29
+ @inheritance_column_name ||= @model_class && @model_class.inheritance_column
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ # NOTE: This class has to be defined in compact style in
4
+ # order for rendering context subclassing to work correctly.
5
+ class ActiveRecord::FixtureSet::RenderContext # :nodoc:
6
+ def self.create_subclass
7
+ Class.new(ActiveRecord::FixtureSet.context_class) do
8
+ def get_binding
9
+ binding()
10
+ end
11
+
12
+ def binary(path)
13
+ %(!!binary "#{Base64.strict_encode64(File.read(path))}")
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ class FixtureSet
5
+ class TableRow # :nodoc:
6
+ class ReflectionProxy # :nodoc:
7
+ def initialize(association)
8
+ @association = association
9
+ end
10
+
11
+ def join_table
12
+ @association.join_table
13
+ end
14
+
15
+ def name
16
+ @association.name
17
+ end
18
+
19
+ def primary_key_type
20
+ @association.klass.type_for_attribute(@association.klass.primary_key).type
21
+ end
22
+ end
23
+
24
+ class HasManyThroughProxy < ReflectionProxy # :nodoc:
25
+ def rhs_key
26
+ @association.foreign_key
27
+ end
28
+
29
+ def lhs_key
30
+ @association.through_reflection.foreign_key
31
+ end
32
+
33
+ def join_table
34
+ @association.through_reflection.table_name
35
+ end
36
+ end
37
+
38
+ def initialize(fixture, table_rows:, label:, now:)
39
+ @table_rows = table_rows
40
+ @label = label
41
+ @now = now
42
+ @row = fixture.to_hash
43
+ fill_row_model_attributes
44
+ end
45
+
46
+ def to_hash
47
+ @row
48
+ end
49
+
50
+ private
51
+
52
+ def model_metadata
53
+ @table_rows.model_metadata
54
+ end
55
+
56
+ def model_class
57
+ @table_rows.model_class
58
+ end
59
+
60
+ def fill_row_model_attributes
61
+ return unless model_class
62
+ fill_timestamps
63
+ interpolate_label
64
+ generate_primary_key
65
+ resolve_enums
66
+ resolve_sti_reflections
67
+ end
68
+
69
+ def reflection_class
70
+ @reflection_class ||= if @row.include?(model_metadata.inheritance_column_name)
71
+ @row[model_metadata.inheritance_column_name].constantize rescue model_class
72
+ else
73
+ model_class
74
+ end
75
+ end
76
+
77
+ def fill_timestamps
78
+ # fill in timestamp columns if they aren't specified and the model is set to record_timestamps
79
+ if model_class.record_timestamps
80
+ model_metadata.timestamp_column_names.each do |c_name|
81
+ @row[c_name] = @now unless @row.key?(c_name)
82
+ end
83
+ end
84
+ end
85
+
86
+ def interpolate_label
87
+ # interpolate the fixture label
88
+ @row.each do |key, value|
89
+ @row[key] = value.gsub("$LABEL", @label.to_s) if value.is_a?(String)
90
+ end
91
+ end
92
+
93
+ def generate_primary_key
94
+ # generate a primary key if necessary
95
+ if model_metadata.has_primary_key_column? && !@row.include?(model_metadata.primary_key_name)
96
+ @row[model_metadata.primary_key_name] = ActiveRecord::FixtureSet.identify(
97
+ @label, model_metadata.primary_key_type
98
+ )
99
+ end
100
+ end
101
+
102
+ def resolve_enums
103
+ model_class.defined_enums.each do |name, values|
104
+ if @row.include?(name)
105
+ @row[name] = values.fetch(@row[name], @row[name])
106
+ end
107
+ end
108
+ end
109
+
110
+ def resolve_sti_reflections
111
+ # If STI is used, find the correct subclass for association reflection
112
+ reflection_class._reflections.each_value do |association|
113
+ case association.macro
114
+ when :belongs_to
115
+ # Do not replace association name with association foreign key if they are named the same
116
+ fk_name = (association.options[:foreign_key] || "#{association.name}_id").to_s
117
+
118
+ if association.name.to_s != fk_name && value = @row.delete(association.name.to_s)
119
+ if association.polymorphic? && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
120
+ # support polymorphic belongs_to as "label (Type)"
121
+ @row[association.foreign_type] = $1
122
+ end
123
+
124
+ fk_type = reflection_class.type_for_attribute(fk_name).type
125
+ @row[fk_name] = ActiveRecord::FixtureSet.identify(value, fk_type)
126
+ end
127
+ when :has_many
128
+ if association.options[:through]
129
+ add_join_records(HasManyThroughProxy.new(association))
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ def add_join_records(association)
136
+ # This is the case when the join table has no fixtures file
137
+ if (targets = @row.delete(association.name.to_s))
138
+ table_name = association.join_table
139
+ column_type = association.primary_key_type
140
+ lhs_key = association.lhs_key
141
+ rhs_key = association.rhs_key
142
+
143
+ targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
144
+ joins = targets.map do |target|
145
+ { lhs_key => @row[model_metadata.primary_key_name],
146
+ rhs_key => ActiveRecord::FixtureSet.identify(target, column_type) }
147
+ end
148
+ @table_rows.tables[table_name].concat(joins)
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record/fixture_set/table_row"
4
+ require "active_record/fixture_set/model_metadata"
5
+
6
+ module ActiveRecord
7
+ class FixtureSet
8
+ class TableRows # :nodoc:
9
+ def initialize(table_name, model_class:, fixtures:, config:)
10
+ @model_class = model_class
11
+
12
+ # track any join tables we need to insert later
13
+ @tables = Hash.new { |h, table| h[table] = [] }
14
+
15
+ # ensure this table is loaded before any HABTM associations
16
+ @tables[table_name] = nil
17
+
18
+ build_table_rows_from(table_name, fixtures, config)
19
+ end
20
+
21
+ attr_reader :tables, :model_class
22
+
23
+ def to_hash
24
+ @tables.transform_values { |rows| rows.map(&:to_hash) }
25
+ end
26
+
27
+ def model_metadata
28
+ @model_metadata ||= ModelMetadata.new(model_class)
29
+ end
30
+
31
+ private
32
+
33
+ def build_table_rows_from(table_name, fixtures, config)
34
+ now = config.default_timezone == :utc ? Time.now.utc : Time.now
35
+
36
+ @tables[table_name] = fixtures.map do |label, fixture|
37
+ TableRow.new(
38
+ fixture,
39
+ table_rows: self,
40
+ label: label,
41
+ now: now,
42
+ )
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,738 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+ require "yaml"
5
+ require "zlib"
6
+ require "set"
7
+ require "active_support/dependencies"
8
+ require "active_support/core_ext/digest/uuid"
9
+ require "active_record/fixture_set/file"
10
+ require "active_record/fixture_set/render_context"
11
+ require "active_record/fixture_set/table_rows"
12
+ require "active_record/test_fixtures"
13
+ require "active_record/errors"
14
+
15
+ module ActiveRecord
16
+ class FixtureClassNotFound < ActiveRecord::ActiveRecordError #:nodoc:
17
+ end
18
+
19
+ # \Fixtures are a way of organizing data that you want to test against; in short, sample data.
20
+ #
21
+ # They are stored in YAML files, one file per model, which are placed in the directory
22
+ # appointed by <tt>ActiveSupport::TestCase.fixture_path=(path)</tt> (this is automatically
23
+ # configured for Rails, so you can just put your files in <tt><your-rails-app>/test/fixtures/</tt>).
24
+ # The fixture file ends with the +.yml+ file extension, for example:
25
+ # <tt><your-rails-app>/test/fixtures/web_sites.yml</tt>).
26
+ #
27
+ # The format of a fixture file looks like this:
28
+ #
29
+ # rubyonrails:
30
+ # id: 1
31
+ # name: Ruby on Rails
32
+ # url: http://www.rubyonrails.org
33
+ #
34
+ # google:
35
+ # id: 2
36
+ # name: Google
37
+ # url: http://www.google.com
38
+ #
39
+ # This fixture file includes two fixtures. Each YAML fixture (ie. record) is given a name and
40
+ # is followed by an indented list of key/value pairs in the "key: value" format. Records are
41
+ # separated by a blank line for your viewing pleasure.
42
+ #
43
+ # Note: Fixtures are unordered. If you want ordered fixtures, use the omap YAML type.
44
+ # See http://yaml.org/type/omap.html
45
+ # for the specification. You will need ordered fixtures when you have foreign key constraints
46
+ # on keys in the same table. This is commonly needed for tree structures. Example:
47
+ #
48
+ # --- !omap
49
+ # - parent:
50
+ # id: 1
51
+ # parent_id: NULL
52
+ # title: Parent
53
+ # - child:
54
+ # id: 2
55
+ # parent_id: 1
56
+ # title: Child
57
+ #
58
+ # = Using Fixtures in Test Cases
59
+ #
60
+ # Since fixtures are a testing construct, we use them in our unit and functional tests. There
61
+ # are two ways to use the fixtures, but first let's take a look at a sample unit test:
62
+ #
63
+ # require 'test_helper'
64
+ #
65
+ # class WebSiteTest < ActiveSupport::TestCase
66
+ # test "web_site_count" do
67
+ # assert_equal 2, WebSite.count
68
+ # end
69
+ # end
70
+ #
71
+ # By default, +test_helper.rb+ will load all of your fixtures into your test
72
+ # database, so this test will succeed.
73
+ #
74
+ # The testing environment will automatically load all the fixtures into the database before each
75
+ # test. To ensure consistent data, the environment deletes the fixtures before running the load.
76
+ #
77
+ # In addition to being available in the database, the fixture's data may also be accessed by
78
+ # using a special dynamic method, which has the same name as the model.
79
+ #
80
+ # Passing in a fixture name to this dynamic method returns the fixture matching this name:
81
+ #
82
+ # test "find one" do
83
+ # assert_equal "Ruby on Rails", web_sites(:rubyonrails).name
84
+ # end
85
+ #
86
+ # Passing in multiple fixture names returns all fixtures matching these names:
87
+ #
88
+ # test "find all by name" do
89
+ # assert_equal 2, web_sites(:rubyonrails, :google).length
90
+ # end
91
+ #
92
+ # Passing in no arguments returns all fixtures:
93
+ #
94
+ # test "find all" do
95
+ # assert_equal 2, web_sites.length
96
+ # end
97
+ #
98
+ # Passing in any fixture name that does not exist will raise <tt>StandardError</tt>:
99
+ #
100
+ # test "find by name that does not exist" do
101
+ # assert_raise(StandardError) { web_sites(:reddit) }
102
+ # end
103
+ #
104
+ # Alternatively, you may enable auto-instantiation of the fixture data. For instance, take the
105
+ # following tests:
106
+ #
107
+ # test "find_alt_method_1" do
108
+ # assert_equal "Ruby on Rails", @web_sites['rubyonrails']['name']
109
+ # end
110
+ #
111
+ # test "find_alt_method_2" do
112
+ # assert_equal "Ruby on Rails", @rubyonrails.name
113
+ # end
114
+ #
115
+ # In order to use these methods to access fixtured data within your test cases, you must specify one of the
116
+ # following in your ActiveSupport::TestCase-derived class:
117
+ #
118
+ # - to fully enable instantiated fixtures (enable alternate methods #1 and #2 above)
119
+ # self.use_instantiated_fixtures = true
120
+ #
121
+ # - create only the hash for the fixtures, do not 'find' each instance (enable alternate method #1 only)
122
+ # self.use_instantiated_fixtures = :no_instances
123
+ #
124
+ # Using either of these alternate methods incurs a performance hit, as the fixtured data must be fully
125
+ # traversed in the database to create the fixture hash and/or instance variables. This is expensive for
126
+ # large sets of fixtured data.
127
+ #
128
+ # = Dynamic fixtures with ERB
129
+ #
130
+ # Sometimes you don't care about the content of the fixtures as much as you care about the volume.
131
+ # In these cases, you can mix ERB in with your YAML fixtures to create a bunch of fixtures for load
132
+ # testing, like:
133
+ #
134
+ # <% 1.upto(1000) do |i| %>
135
+ # fix_<%= i %>:
136
+ # id: <%= i %>
137
+ # name: guy_<%= i %>
138
+ # <% end %>
139
+ #
140
+ # This will create 1000 very simple fixtures.
141
+ #
142
+ # Using ERB, you can also inject dynamic values into your fixtures with inserts like
143
+ # <tt><%= Date.today.strftime("%Y-%m-%d") %></tt>.
144
+ # This is however a feature to be used with some caution. The point of fixtures are that they're
145
+ # stable units of predictable sample data. If you feel that you need to inject dynamic values, then
146
+ # perhaps you should reexamine whether your application is properly testable. Hence, dynamic values
147
+ # in fixtures are to be considered a code smell.
148
+ #
149
+ # Helper methods defined in a fixture will not be available in other fixtures, to prevent against
150
+ # unwanted inter-test dependencies. Methods used by multiple fixtures should be defined in a module
151
+ # that is included in ActiveRecord::FixtureSet.context_class.
152
+ #
153
+ # - define a helper method in <tt>test_helper.rb</tt>
154
+ # module FixtureFileHelpers
155
+ # def file_sha(path)
156
+ # Digest::SHA2.hexdigest(File.read(Rails.root.join('test/fixtures', path)))
157
+ # end
158
+ # end
159
+ # ActiveRecord::FixtureSet.context_class.include FixtureFileHelpers
160
+ #
161
+ # - use the helper method in a fixture
162
+ # photo:
163
+ # name: kitten.png
164
+ # sha: <%= file_sha 'files/kitten.png' %>
165
+ #
166
+ # = Transactional Tests
167
+ #
168
+ # Test cases can use begin+rollback to isolate their changes to the database instead of having to
169
+ # delete+insert for every test case.
170
+ #
171
+ # class FooTest < ActiveSupport::TestCase
172
+ # self.use_transactional_tests = true
173
+ #
174
+ # test "godzilla" do
175
+ # assert_not_empty Foo.all
176
+ # Foo.destroy_all
177
+ # assert_empty Foo.all
178
+ # end
179
+ #
180
+ # test "godzilla aftermath" do
181
+ # assert_not_empty Foo.all
182
+ # end
183
+ # end
184
+ #
185
+ # If you preload your test database with all fixture data (probably by running `rails db:fixtures:load`)
186
+ # and use transactional tests, then you may omit all fixtures declarations in your test cases since
187
+ # all the data's already there and every case rolls back its changes.
188
+ #
189
+ # In order to use instantiated fixtures with preloaded data, set +self.pre_loaded_fixtures+ to
190
+ # true. This will provide access to fixture data for every table that has been loaded through
191
+ # fixtures (depending on the value of +use_instantiated_fixtures+).
192
+ #
193
+ # When *not* to use transactional tests:
194
+ #
195
+ # 1. You're testing whether a transaction works correctly. Nested transactions don't commit until
196
+ # all parent transactions commit, particularly, the fixtures transaction which is begun in setup
197
+ # and rolled back in teardown. Thus, you won't be able to verify
198
+ # the results of your transaction until Active Record supports nested transactions or savepoints (in progress).
199
+ # 2. Your database does not support transactions. Every Active Record database supports transactions except MySQL MyISAM.
200
+ # Use InnoDB, MaxDB, or NDB instead.
201
+ #
202
+ # = Advanced Fixtures
203
+ #
204
+ # Fixtures that don't specify an ID get some extra features:
205
+ #
206
+ # * Stable, autogenerated IDs
207
+ # * Label references for associations (belongs_to, has_one, has_many)
208
+ # * HABTM associations as inline lists
209
+ #
210
+ # There are some more advanced features available even if the id is specified:
211
+ #
212
+ # * Autofilled timestamp columns
213
+ # * Fixture label interpolation
214
+ # * Support for YAML defaults
215
+ #
216
+ # == Stable, Autogenerated IDs
217
+ #
218
+ # Here, have a monkey fixture:
219
+ #
220
+ # george:
221
+ # id: 1
222
+ # name: George the Monkey
223
+ #
224
+ # reginald:
225
+ # id: 2
226
+ # name: Reginald the Pirate
227
+ #
228
+ # Each of these fixtures has two unique identifiers: one for the database
229
+ # and one for the humans. Why don't we generate the primary key instead?
230
+ # Hashing each fixture's label yields a consistent ID:
231
+ #
232
+ # george: # generated id: 503576764
233
+ # name: George the Monkey
234
+ #
235
+ # reginald: # generated id: 324201669
236
+ # name: Reginald the Pirate
237
+ #
238
+ # Active Record looks at the fixture's model class, discovers the correct
239
+ # primary key, and generates it right before inserting the fixture
240
+ # into the database.
241
+ #
242
+ # The generated ID for a given label is constant, so we can discover
243
+ # any fixture's ID without loading anything, as long as we know the label.
244
+ #
245
+ # == Label references for associations (belongs_to, has_one, has_many)
246
+ #
247
+ # Specifying foreign keys in fixtures can be very fragile, not to
248
+ # mention difficult to read. Since Active Record can figure out the ID of
249
+ # any fixture from its label, you can specify FK's by label instead of ID.
250
+ #
251
+ # === belongs_to
252
+ #
253
+ # Let's break out some more monkeys and pirates.
254
+ #
255
+ # ### in pirates.yml
256
+ #
257
+ # reginald:
258
+ # id: 1
259
+ # name: Reginald the Pirate
260
+ # monkey_id: 1
261
+ #
262
+ # ### in monkeys.yml
263
+ #
264
+ # george:
265
+ # id: 1
266
+ # name: George the Monkey
267
+ # pirate_id: 1
268
+ #
269
+ # Add a few more monkeys and pirates and break this into multiple files,
270
+ # and it gets pretty hard to keep track of what's going on. Let's
271
+ # use labels instead of IDs:
272
+ #
273
+ # ### in pirates.yml
274
+ #
275
+ # reginald:
276
+ # name: Reginald the Pirate
277
+ # monkey: george
278
+ #
279
+ # ### in monkeys.yml
280
+ #
281
+ # george:
282
+ # name: George the Monkey
283
+ # pirate: reginald
284
+ #
285
+ # Pow! All is made clear. Active Record reflects on the fixture's model class,
286
+ # finds all the +belongs_to+ associations, and allows you to specify
287
+ # a target *label* for the *association* (monkey: george) rather than
288
+ # a target *id* for the *FK* (<tt>monkey_id: 1</tt>).
289
+ #
290
+ # ==== Polymorphic belongs_to
291
+ #
292
+ # Supporting polymorphic relationships is a little bit more complicated, since
293
+ # Active Record needs to know what type your association is pointing at. Something
294
+ # like this should look familiar:
295
+ #
296
+ # ### in fruit.rb
297
+ #
298
+ # belongs_to :eater, polymorphic: true
299
+ #
300
+ # ### in fruits.yml
301
+ #
302
+ # apple:
303
+ # id: 1
304
+ # name: apple
305
+ # eater_id: 1
306
+ # eater_type: Monkey
307
+ #
308
+ # Can we do better? You bet!
309
+ #
310
+ # apple:
311
+ # eater: george (Monkey)
312
+ #
313
+ # Just provide the polymorphic target type and Active Record will take care of the rest.
314
+ #
315
+ # === has_and_belongs_to_many
316
+ #
317
+ # Time to give our monkey some fruit.
318
+ #
319
+ # ### in monkeys.yml
320
+ #
321
+ # george:
322
+ # id: 1
323
+ # name: George the Monkey
324
+ #
325
+ # ### in fruits.yml
326
+ #
327
+ # apple:
328
+ # id: 1
329
+ # name: apple
330
+ #
331
+ # orange:
332
+ # id: 2
333
+ # name: orange
334
+ #
335
+ # grape:
336
+ # id: 3
337
+ # name: grape
338
+ #
339
+ # ### in fruits_monkeys.yml
340
+ #
341
+ # apple_george:
342
+ # fruit_id: 1
343
+ # monkey_id: 1
344
+ #
345
+ # orange_george:
346
+ # fruit_id: 2
347
+ # monkey_id: 1
348
+ #
349
+ # grape_george:
350
+ # fruit_id: 3
351
+ # monkey_id: 1
352
+ #
353
+ # Let's make the HABTM fixture go away.
354
+ #
355
+ # ### in monkeys.yml
356
+ #
357
+ # george:
358
+ # id: 1
359
+ # name: George the Monkey
360
+ # fruits: apple, orange, grape
361
+ #
362
+ # ### in fruits.yml
363
+ #
364
+ # apple:
365
+ # name: apple
366
+ #
367
+ # orange:
368
+ # name: orange
369
+ #
370
+ # grape:
371
+ # name: grape
372
+ #
373
+ # Zap! No more fruits_monkeys.yml file. We've specified the list of fruits
374
+ # on George's fixture, but we could've just as easily specified a list
375
+ # of monkeys on each fruit. As with +belongs_to+, Active Record reflects on
376
+ # the fixture's model class and discovers the +has_and_belongs_to_many+
377
+ # associations.
378
+ #
379
+ # == Autofilled Timestamp Columns
380
+ #
381
+ # If your table/model specifies any of Active Record's
382
+ # standard timestamp columns (+created_at+, +created_on+, +updated_at+, +updated_on+),
383
+ # they will automatically be set to <tt>Time.now</tt>.
384
+ #
385
+ # If you've set specific values, they'll be left alone.
386
+ #
387
+ # == Fixture label interpolation
388
+ #
389
+ # The label of the current fixture is always available as a column value:
390
+ #
391
+ # geeksomnia:
392
+ # name: Geeksomnia's Account
393
+ # subdomain: $LABEL
394
+ # email: $LABEL@email.com
395
+ #
396
+ # Also, sometimes (like when porting older join table fixtures) you'll need
397
+ # to be able to get a hold of the identifier for a given label. ERB
398
+ # to the rescue:
399
+ #
400
+ # george_reginald:
401
+ # monkey_id: <%= ActiveRecord::FixtureSet.identify(:reginald) %>
402
+ # pirate_id: <%= ActiveRecord::FixtureSet.identify(:george) %>
403
+ #
404
+ # == Support for YAML defaults
405
+ #
406
+ # You can set and reuse defaults in your fixtures YAML file.
407
+ # This is the same technique used in the +database.yml+ file to specify
408
+ # defaults:
409
+ #
410
+ # DEFAULTS: &DEFAULTS
411
+ # created_on: <%= 3.weeks.ago.to_s(:db) %>
412
+ #
413
+ # first:
414
+ # name: Smurf
415
+ # <<: *DEFAULTS
416
+ #
417
+ # second:
418
+ # name: Fraggle
419
+ # <<: *DEFAULTS
420
+ #
421
+ # Any fixture labeled "DEFAULTS" is safely ignored.
422
+ #
423
+ # == Configure the fixture model class
424
+ #
425
+ # It's possible to set the fixture's model class directly in the YAML file.
426
+ # This is helpful when fixtures are loaded outside tests and
427
+ # +set_fixture_class+ is not available (e.g.
428
+ # when running <tt>rails db:fixtures:load</tt>).
429
+ #
430
+ # _fixture:
431
+ # model_class: User
432
+ # david:
433
+ # name: David
434
+ #
435
+ # Any fixtures labeled "_fixture" are safely ignored.
436
+ class FixtureSet
437
+ #--
438
+ # An instance of FixtureSet is normally stored in a single YAML file and
439
+ # possibly in a folder with the same name.
440
+ #++
441
+
442
+ MAX_ID = 2**30 - 1
443
+
444
+ @@all_cached_fixtures = Hash.new { |h, k| h[k] = {} }
445
+
446
+ cattr_accessor :all_loaded_fixtures, default: {}
447
+
448
+ class ClassCache
449
+ def initialize(class_names, config)
450
+ @class_names = class_names.stringify_keys
451
+ @config = config
452
+
453
+ # Remove string values that aren't constants or subclasses of AR
454
+ @class_names.delete_if do |klass_name, klass|
455
+ !insert_class(@class_names, klass_name, klass)
456
+ end
457
+ end
458
+
459
+ def [](fs_name)
460
+ @class_names.fetch(fs_name) do
461
+ klass = default_fixture_model(fs_name, @config).safe_constantize
462
+ insert_class(@class_names, fs_name, klass)
463
+ end
464
+ end
465
+
466
+ private
467
+
468
+ def insert_class(class_names, name, klass)
469
+ # We only want to deal with AR objects.
470
+ if klass && klass < ActiveRecord::Base
471
+ class_names[name] = klass
472
+ else
473
+ class_names[name] = nil
474
+ end
475
+ end
476
+
477
+ def default_fixture_model(fs_name, config)
478
+ ActiveRecord::FixtureSet.default_fixture_model_name(fs_name, config)
479
+ end
480
+ end
481
+
482
+ class << self
483
+ def default_fixture_model_name(fixture_set_name, config = ActiveRecord::Base) # :nodoc:
484
+ config.pluralize_table_names ?
485
+ fixture_set_name.singularize.camelize :
486
+ fixture_set_name.camelize
487
+ end
488
+
489
+ def default_fixture_table_name(fixture_set_name, config = ActiveRecord::Base) # :nodoc:
490
+ "#{ config.table_name_prefix }"\
491
+ "#{ fixture_set_name.tr('/', '_') }"\
492
+ "#{ config.table_name_suffix }".to_sym
493
+ end
494
+
495
+ def reset_cache
496
+ @@all_cached_fixtures.clear
497
+ end
498
+
499
+ def cache_for_connection(connection)
500
+ @@all_cached_fixtures[connection]
501
+ end
502
+
503
+ def fixture_is_cached?(connection, table_name)
504
+ cache_for_connection(connection)[table_name]
505
+ end
506
+
507
+ def cached_fixtures(connection, keys_to_fetch = nil)
508
+ if keys_to_fetch
509
+ cache_for_connection(connection).values_at(*keys_to_fetch)
510
+ else
511
+ cache_for_connection(connection).values
512
+ end
513
+ end
514
+
515
+ def cache_fixtures(connection, fixtures_map)
516
+ cache_for_connection(connection).update(fixtures_map)
517
+ end
518
+
519
+ def instantiate_fixtures(object, fixture_set, load_instances = true)
520
+ return unless load_instances
521
+ fixture_set.each do |fixture_name, fixture|
522
+ object.instance_variable_set "@#{fixture_name}", fixture.find
523
+ rescue FixtureClassNotFound
524
+ nil
525
+ end
526
+ end
527
+
528
+ def instantiate_all_loaded_fixtures(object, load_instances = true)
529
+ all_loaded_fixtures.each_value do |fixture_set|
530
+ instantiate_fixtures(object, fixture_set, load_instances)
531
+ end
532
+ end
533
+
534
+ def create_fixtures(fixtures_directory, fixture_set_names, class_names = {}, config = ActiveRecord::Base, &block)
535
+ fixture_set_names = Array(fixture_set_names).map(&:to_s)
536
+ class_names = ClassCache.new class_names, config
537
+
538
+ # FIXME: Apparently JK uses this.
539
+ connection = block_given? ? block : lambda { ActiveRecord::Base.connection }
540
+
541
+ fixture_files_to_read = fixture_set_names.reject do |fs_name|
542
+ fixture_is_cached?(connection.call, fs_name)
543
+ end
544
+
545
+ if fixture_files_to_read.any?
546
+ fixtures_map = read_and_insert(
547
+ fixtures_directory,
548
+ fixture_files_to_read,
549
+ class_names,
550
+ connection,
551
+ )
552
+ cache_fixtures(connection.call, fixtures_map)
553
+ end
554
+ cached_fixtures(connection.call, fixture_set_names)
555
+ end
556
+
557
+ # Returns a consistent, platform-independent identifier for +label+.
558
+ # Integer identifiers are values less than 2^30. UUIDs are RFC 4122 version 5 SHA-1 hashes.
559
+ def identify(label, column_type = :integer)
560
+ if column_type == :uuid
561
+ Digest::UUID.uuid_v5(Digest::UUID::OID_NAMESPACE, label.to_s)
562
+ else
563
+ Zlib.crc32(label.to_s) % MAX_ID
564
+ end
565
+ end
566
+
567
+ # Superclass for the evaluation contexts used by ERB fixtures.
568
+ def context_class
569
+ @context_class ||= Class.new
570
+ end
571
+
572
+ private
573
+
574
+ def read_and_insert(fixtures_directory, fixture_files, class_names, connection) # :nodoc:
575
+ fixtures_map = {}
576
+ fixture_sets = fixture_files.map do |fixture_set_name|
577
+ klass = class_names[fixture_set_name]
578
+ fixtures_map[fixture_set_name] = new( # ActiveRecord::FixtureSet.new
579
+ nil,
580
+ fixture_set_name,
581
+ klass,
582
+ ::File.join(fixtures_directory, fixture_set_name)
583
+ )
584
+ end
585
+ update_all_loaded_fixtures(fixtures_map)
586
+
587
+ insert(fixture_sets, connection)
588
+
589
+ fixtures_map
590
+ end
591
+
592
+ def insert(fixture_sets, connection) # :nodoc:
593
+ fixture_sets_by_connection = fixture_sets.group_by do |fixture_set|
594
+ if fixture_set.model_class
595
+ fixture_set.model_class.connection
596
+ else
597
+ connection.call
598
+ end
599
+ end
600
+
601
+ fixture_sets_by_connection.each do |conn, set|
602
+ table_rows_for_connection = Hash.new { |h, k| h[k] = [] }
603
+
604
+ set.each do |fixture_set|
605
+ fixture_set.table_rows.each do |table, rows|
606
+ table_rows_for_connection[table].unshift(*rows)
607
+ end
608
+ end
609
+
610
+ conn.insert_fixtures_set(table_rows_for_connection, table_rows_for_connection.keys)
611
+
612
+ # Cap primary key sequences to max(pk).
613
+ if conn.respond_to?(:reset_pk_sequence!)
614
+ set.each { |fs| conn.reset_pk_sequence!(fs.table_name) }
615
+ end
616
+ end
617
+ end
618
+
619
+ def update_all_loaded_fixtures(fixtures_map) # :nodoc:
620
+ all_loaded_fixtures.update(fixtures_map)
621
+ end
622
+ end
623
+
624
+ attr_reader :table_name, :name, :fixtures, :model_class, :config
625
+
626
+ def initialize(_, name, class_name, path, config = ActiveRecord::Base)
627
+ @name = name
628
+ @path = path
629
+ @config = config
630
+
631
+ self.model_class = class_name
632
+
633
+ @fixtures = read_fixture_files(path)
634
+
635
+ @table_name = model_class&.table_name || self.class.default_fixture_table_name(name, config)
636
+ end
637
+
638
+ def [](x)
639
+ fixtures[x]
640
+ end
641
+
642
+ def []=(k, v)
643
+ fixtures[k] = v
644
+ end
645
+
646
+ def each(&block)
647
+ fixtures.each(&block)
648
+ end
649
+
650
+ def size
651
+ fixtures.size
652
+ end
653
+
654
+ # Returns a hash of rows to be inserted. The key is the table, the value is
655
+ # a list of rows to insert to that table.
656
+ def table_rows
657
+ # allow a standard key to be used for doing defaults in YAML
658
+ fixtures.delete("DEFAULTS")
659
+
660
+ TableRows.new(
661
+ table_name,
662
+ model_class: model_class,
663
+ fixtures: fixtures,
664
+ config: config,
665
+ ).to_hash
666
+ end
667
+
668
+ private
669
+
670
+ def model_class=(class_name)
671
+ if class_name.is_a?(Class) # TODO: Should be an AR::Base type class, or any?
672
+ @model_class = class_name
673
+ else
674
+ @model_class = class_name.safe_constantize if class_name
675
+ end
676
+ end
677
+
678
+ # Loads the fixtures from the YAML file at +path+.
679
+ # If the file sets the +model_class+ and current instance value is not set,
680
+ # it uses the file value.
681
+ def read_fixture_files(path)
682
+ yaml_files = Dir["#{path}/{**,*}/*.yml"].select { |f|
683
+ ::File.file?(f)
684
+ } + [yaml_file_path(path)]
685
+
686
+ yaml_files.each_with_object({}) do |file, fixtures|
687
+ FixtureSet::File.open(file) do |fh|
688
+ self.model_class ||= fh.model_class if fh.model_class
689
+ fh.each do |fixture_name, row|
690
+ fixtures[fixture_name] = ActiveRecord::Fixture.new(row, model_class)
691
+ end
692
+ end
693
+ end
694
+ end
695
+
696
+ def yaml_file_path(path)
697
+ "#{path}.yml"
698
+ end
699
+ end
700
+
701
+ class Fixture #:nodoc:
702
+ include Enumerable
703
+
704
+ class FixtureError < StandardError #:nodoc:
705
+ end
706
+
707
+ class FormatError < FixtureError #:nodoc:
708
+ end
709
+
710
+ attr_reader :model_class, :fixture
711
+
712
+ def initialize(fixture, model_class)
713
+ @fixture = fixture
714
+ @model_class = model_class
715
+ end
716
+
717
+ def class_name
718
+ model_class.name if model_class
719
+ end
720
+
721
+ def each
722
+ fixture.each { |item| yield item }
723
+ end
724
+
725
+ def [](key)
726
+ fixture[key]
727
+ end
728
+
729
+ alias :to_hash :fixture
730
+
731
+ def find
732
+ raise FixtureClassNotFound, "No class attached to find." unless model_class
733
+ model_class.unscoped do
734
+ model_class.find(fixture[model_class.primary_key])
735
+ end
736
+ end
737
+ end
738
+ end