activerecord 3.2.6 → 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 (371) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +611 -6417
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +44 -47
  5. data/examples/performance.rb +79 -71
  6. data/examples/simple.rb +6 -5
  7. data/lib/active_record/aggregations.rb +268 -238
  8. data/lib/active_record/association_relation.rb +40 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -42
  10. data/lib/active_record/associations/association.rb +173 -81
  11. data/lib/active_record/associations/association_scope.rb +124 -92
  12. data/lib/active_record/associations/belongs_to_association.rb +83 -38
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +11 -9
  14. data/lib/active_record/associations/builder/association.rb +113 -32
  15. data/lib/active_record/associations/builder/belongs_to.rb +105 -60
  16. data/lib/active_record/associations/builder/collection_association.rb +53 -56
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +98 -41
  18. data/lib/active_record/associations/builder/has_many.rb +11 -63
  19. data/lib/active_record/associations/builder/has_one.rb +47 -45
  20. data/lib/active_record/associations/builder/singular_association.rb +30 -18
  21. data/lib/active_record/associations/collection_association.rb +217 -295
  22. data/lib/active_record/associations/collection_proxy.rb +1074 -77
  23. data/lib/active_record/associations/foreign_association.rb +20 -0
  24. data/lib/active_record/associations/has_many_association.rb +78 -50
  25. data/lib/active_record/associations/has_many_through_association.rb +99 -61
  26. data/lib/active_record/associations/has_one_association.rb +75 -30
  27. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  28. data/lib/active_record/associations/join_dependency/join_association.rb +45 -119
  29. data/lib/active_record/associations/join_dependency/join_base.rb +11 -12
  30. data/lib/active_record/associations/join_dependency/join_part.rb +35 -42
  31. data/lib/active_record/associations/join_dependency.rb +208 -164
  32. data/lib/active_record/associations/preloader/association.rb +93 -87
  33. data/lib/active_record/associations/preloader/through_association.rb +87 -38
  34. data/lib/active_record/associations/preloader.rb +134 -110
  35. data/lib/active_record/associations/singular_association.rb +19 -24
  36. data/lib/active_record/associations/through_association.rb +61 -27
  37. data/lib/active_record/associations.rb +1766 -1505
  38. data/lib/active_record/attribute_assignment.rb +57 -193
  39. data/lib/active_record/attribute_decorators.rb +90 -0
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +58 -8
  41. data/lib/active_record/attribute_methods/dirty.rb +187 -67
  42. data/lib/active_record/attribute_methods/primary_key.rb +100 -78
  43. data/lib/active_record/attribute_methods/query.rb +10 -8
  44. data/lib/active_record/attribute_methods/read.rb +29 -118
  45. data/lib/active_record/attribute_methods/serialization.rb +60 -72
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +69 -42
  47. data/lib/active_record/attribute_methods/write.rb +36 -44
  48. data/lib/active_record/attribute_methods.rb +306 -161
  49. data/lib/active_record/attributes.rb +279 -0
  50. data/lib/active_record/autosave_association.rb +324 -238
  51. data/lib/active_record/base.rb +114 -507
  52. data/lib/active_record/callbacks.rb +147 -83
  53. data/lib/active_record/coders/json.rb +15 -0
  54. data/lib/active_record/coders/yaml_column.rb +32 -23
  55. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +962 -279
  56. data/lib/active_record/connection_adapters/abstract/database_limits.rb +32 -5
  57. data/lib/active_record/connection_adapters/abstract/database_statements.rb +331 -209
  58. data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -23
  59. data/lib/active_record/connection_adapters/abstract/quoting.rb +201 -65
  60. data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
  61. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +510 -289
  63. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +93 -0
  64. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1182 -313
  65. data/lib/active_record/connection_adapters/abstract/transaction.rb +323 -0
  66. data/lib/active_record/connection_adapters/abstract_adapter.rb +585 -120
  67. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +610 -463
  68. data/lib/active_record/connection_adapters/column.rb +58 -233
  69. data/lib/active_record/connection_adapters/connection_specification.rb +297 -0
  70. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
  71. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  72. data/lib/active_record/connection_adapters/mysql/database_statements.rb +200 -0
  73. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  74. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +72 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
  79. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
  80. data/lib/active_record/connection_adapters/mysql2_adapter.rb +75 -207
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -0
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +182 -0
  83. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +53 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +113 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
  107. data/lib/active_record/connection_adapters/postgresql/quoting.rb +205 -0
  108. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
  109. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +222 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +776 -0
  113. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  114. data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
  115. data/lib/active_record/connection_adapters/postgresql_adapter.rb +695 -1052
  116. data/lib/active_record/connection_adapters/schema_cache.rb +115 -24
  117. data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
  118. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +103 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +528 -26
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +267 -0
  128. data/lib/active_record/core.rb +599 -0
  129. data/lib/active_record/counter_cache.rb +177 -103
  130. data/lib/active_record/database_configurations/database_config.rb +37 -0
  131. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  132. data/lib/active_record/database_configurations/url_config.rb +79 -0
  133. data/lib/active_record/database_configurations.rb +233 -0
  134. data/lib/active_record/define_callbacks.rb +22 -0
  135. data/lib/active_record/dynamic_matchers.rb +107 -64
  136. data/lib/active_record/enum.rb +274 -0
  137. data/lib/active_record/errors.rb +254 -61
  138. data/lib/active_record/explain.rb +35 -70
  139. data/lib/active_record/explain_registry.rb +32 -0
  140. data/lib/active_record/explain_subscriber.rb +18 -8
  141. data/lib/active_record/fixture_set/file.rb +82 -0
  142. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  143. data/lib/active_record/fixture_set/render_context.rb +17 -0
  144. data/lib/active_record/fixture_set/table_row.rb +153 -0
  145. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  146. data/lib/active_record/fixtures.rb +291 -475
  147. data/lib/active_record/gem_version.rb +17 -0
  148. data/lib/active_record/inheritance.rb +219 -100
  149. data/lib/active_record/insert_all.rb +179 -0
  150. data/lib/active_record/integration.rb +175 -17
  151. data/lib/active_record/internal_metadata.rb +53 -0
  152. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  153. data/lib/active_record/locale/en.yml +9 -1
  154. data/lib/active_record/locking/optimistic.rb +106 -92
  155. data/lib/active_record/locking/pessimistic.rb +23 -11
  156. data/lib/active_record/log_subscriber.rb +80 -30
  157. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  158. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  159. data/lib/active_record/middleware/database_selector.rb +75 -0
  160. data/lib/active_record/migration/command_recorder.rb +235 -56
  161. data/lib/active_record/migration/compatibility.rb +244 -0
  162. data/lib/active_record/migration/join_table.rb +17 -0
  163. data/lib/active_record/migration.rb +917 -301
  164. data/lib/active_record/model_schema.rb +351 -175
  165. data/lib/active_record/nested_attributes.rb +366 -235
  166. data/lib/active_record/no_touching.rb +65 -0
  167. data/lib/active_record/null_relation.rb +68 -0
  168. data/lib/active_record/persistence.rb +761 -166
  169. data/lib/active_record/query_cache.rb +22 -44
  170. data/lib/active_record/querying.rb +55 -31
  171. data/lib/active_record/railtie.rb +185 -47
  172. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  173. data/lib/active_record/railties/console_sandbox.rb +5 -4
  174. data/lib/active_record/railties/controller_runtime.rb +35 -33
  175. data/lib/active_record/railties/databases.rake +366 -463
  176. data/lib/active_record/readonly_attributes.rb +4 -6
  177. data/lib/active_record/reflection.rb +736 -228
  178. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  179. data/lib/active_record/relation/batches.rb +252 -52
  180. data/lib/active_record/relation/calculations.rb +340 -270
  181. data/lib/active_record/relation/delegation.rb +117 -36
  182. data/lib/active_record/relation/finder_methods.rb +439 -286
  183. data/lib/active_record/relation/from_clause.rb +26 -0
  184. data/lib/active_record/relation/merger.rb +184 -0
  185. data/lib/active_record/relation/predicate_builder/array_handler.rb +49 -0
  186. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  187. data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
  188. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  189. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  190. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  191. data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
  192. data/lib/active_record/relation/predicate_builder.rb +131 -39
  193. data/lib/active_record/relation/query_attribute.rb +50 -0
  194. data/lib/active_record/relation/query_methods.rb +1163 -221
  195. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  196. data/lib/active_record/relation/spawn_methods.rb +49 -120
  197. data/lib/active_record/relation/where_clause.rb +190 -0
  198. data/lib/active_record/relation/where_clause_factory.rb +33 -0
  199. data/lib/active_record/relation.rb +671 -349
  200. data/lib/active_record/result.rb +149 -15
  201. data/lib/active_record/runtime_registry.rb +24 -0
  202. data/lib/active_record/sanitization.rb +153 -133
  203. data/lib/active_record/schema.rb +22 -19
  204. data/lib/active_record/schema_dumper.rb +178 -112
  205. data/lib/active_record/schema_migration.rb +60 -0
  206. data/lib/active_record/scoping/default.rb +107 -98
  207. data/lib/active_record/scoping/named.rb +130 -115
  208. data/lib/active_record/scoping.rb +77 -123
  209. data/lib/active_record/secure_token.rb +40 -0
  210. data/lib/active_record/serialization.rb +10 -6
  211. data/lib/active_record/statement_cache.rb +148 -0
  212. data/lib/active_record/store.rb +256 -16
  213. data/lib/active_record/suppressor.rb +61 -0
  214. data/lib/active_record/table_metadata.rb +75 -0
  215. data/lib/active_record/tasks/database_tasks.rb +506 -0
  216. data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
  217. data/lib/active_record/tasks/postgresql_database_tasks.rb +141 -0
  218. data/lib/active_record/tasks/sqlite_database_tasks.rb +77 -0
  219. data/lib/active_record/test_databases.rb +23 -0
  220. data/lib/active_record/test_fixtures.rb +224 -0
  221. data/lib/active_record/timestamp.rb +93 -39
  222. data/lib/active_record/touch_later.rb +66 -0
  223. data/lib/active_record/transactions.rb +260 -129
  224. data/lib/active_record/translation.rb +3 -1
  225. data/lib/active_record/type/adapter_specific_registry.rb +129 -0
  226. data/lib/active_record/type/date.rb +9 -0
  227. data/lib/active_record/type/date_time.rb +9 -0
  228. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  229. data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
  230. data/lib/active_record/type/internal/timezone.rb +17 -0
  231. data/lib/active_record/type/json.rb +30 -0
  232. data/lib/active_record/type/serialized.rb +71 -0
  233. data/lib/active_record/type/text.rb +11 -0
  234. data/lib/active_record/type/time.rb +21 -0
  235. data/lib/active_record/type/type_map.rb +62 -0
  236. data/lib/active_record/type/unsigned_integer.rb +17 -0
  237. data/lib/active_record/type.rb +78 -0
  238. data/lib/active_record/type_caster/connection.rb +34 -0
  239. data/lib/active_record/type_caster/map.rb +20 -0
  240. data/lib/active_record/type_caster.rb +9 -0
  241. data/lib/active_record/validations/absence.rb +25 -0
  242. data/lib/active_record/validations/associated.rb +35 -18
  243. data/lib/active_record/validations/length.rb +26 -0
  244. data/lib/active_record/validations/presence.rb +68 -0
  245. data/lib/active_record/validations/uniqueness.rb +123 -77
  246. data/lib/active_record/validations.rb +54 -43
  247. data/lib/active_record/version.rb +7 -7
  248. data/lib/active_record.rb +97 -49
  249. data/lib/arel/alias_predication.rb +9 -0
  250. data/lib/arel/attributes/attribute.rb +37 -0
  251. data/lib/arel/attributes.rb +22 -0
  252. data/lib/arel/collectors/bind.rb +24 -0
  253. data/lib/arel/collectors/composite.rb +31 -0
  254. data/lib/arel/collectors/plain_string.rb +20 -0
  255. data/lib/arel/collectors/sql_string.rb +20 -0
  256. data/lib/arel/collectors/substitute_binds.rb +28 -0
  257. data/lib/arel/crud.rb +42 -0
  258. data/lib/arel/delete_manager.rb +18 -0
  259. data/lib/arel/errors.rb +9 -0
  260. data/lib/arel/expressions.rb +29 -0
  261. data/lib/arel/factory_methods.rb +49 -0
  262. data/lib/arel/insert_manager.rb +49 -0
  263. data/lib/arel/math.rb +45 -0
  264. data/lib/arel/nodes/and.rb +32 -0
  265. data/lib/arel/nodes/ascending.rb +23 -0
  266. data/lib/arel/nodes/binary.rb +52 -0
  267. data/lib/arel/nodes/bind_param.rb +36 -0
  268. data/lib/arel/nodes/case.rb +55 -0
  269. data/lib/arel/nodes/casted.rb +50 -0
  270. data/lib/arel/nodes/comment.rb +29 -0
  271. data/lib/arel/nodes/count.rb +12 -0
  272. data/lib/arel/nodes/delete_statement.rb +45 -0
  273. data/lib/arel/nodes/descending.rb +23 -0
  274. data/lib/arel/nodes/equality.rb +18 -0
  275. data/lib/arel/nodes/extract.rb +24 -0
  276. data/lib/arel/nodes/false.rb +16 -0
  277. data/lib/arel/nodes/full_outer_join.rb +8 -0
  278. data/lib/arel/nodes/function.rb +44 -0
  279. data/lib/arel/nodes/grouping.rb +8 -0
  280. data/lib/arel/nodes/in.rb +8 -0
  281. data/lib/arel/nodes/infix_operation.rb +80 -0
  282. data/lib/arel/nodes/inner_join.rb +8 -0
  283. data/lib/arel/nodes/insert_statement.rb +37 -0
  284. data/lib/arel/nodes/join_source.rb +20 -0
  285. data/lib/arel/nodes/matches.rb +18 -0
  286. data/lib/arel/nodes/named_function.rb +23 -0
  287. data/lib/arel/nodes/node.rb +50 -0
  288. data/lib/arel/nodes/node_expression.rb +13 -0
  289. data/lib/arel/nodes/outer_join.rb +8 -0
  290. data/lib/arel/nodes/over.rb +15 -0
  291. data/lib/arel/nodes/regexp.rb +16 -0
  292. data/lib/arel/nodes/right_outer_join.rb +8 -0
  293. data/lib/arel/nodes/select_core.rb +67 -0
  294. data/lib/arel/nodes/select_statement.rb +41 -0
  295. data/lib/arel/nodes/sql_literal.rb +16 -0
  296. data/lib/arel/nodes/string_join.rb +11 -0
  297. data/lib/arel/nodes/table_alias.rb +27 -0
  298. data/lib/arel/nodes/terminal.rb +16 -0
  299. data/lib/arel/nodes/true.rb +16 -0
  300. data/lib/arel/nodes/unary.rb +45 -0
  301. data/lib/arel/nodes/unary_operation.rb +20 -0
  302. data/lib/arel/nodes/unqualified_column.rb +22 -0
  303. data/lib/arel/nodes/update_statement.rb +41 -0
  304. data/lib/arel/nodes/values_list.rb +9 -0
  305. data/lib/arel/nodes/window.rb +126 -0
  306. data/lib/arel/nodes/with.rb +11 -0
  307. data/lib/arel/nodes.rb +68 -0
  308. data/lib/arel/order_predications.rb +13 -0
  309. data/lib/arel/predications.rb +257 -0
  310. data/lib/arel/select_manager.rb +271 -0
  311. data/lib/arel/table.rb +110 -0
  312. data/lib/arel/tree_manager.rb +72 -0
  313. data/lib/arel/update_manager.rb +34 -0
  314. data/lib/arel/visitors/depth_first.rb +204 -0
  315. data/lib/arel/visitors/dot.rb +297 -0
  316. data/lib/arel/visitors/ibm_db.rb +34 -0
  317. data/lib/arel/visitors/informix.rb +62 -0
  318. data/lib/arel/visitors/mssql.rb +157 -0
  319. data/lib/arel/visitors/mysql.rb +83 -0
  320. data/lib/arel/visitors/oracle.rb +159 -0
  321. data/lib/arel/visitors/oracle12.rb +66 -0
  322. data/lib/arel/visitors/postgresql.rb +110 -0
  323. data/lib/arel/visitors/sqlite.rb +39 -0
  324. data/lib/arel/visitors/to_sql.rb +889 -0
  325. data/lib/arel/visitors/visitor.rb +46 -0
  326. data/lib/arel/visitors/where_sql.rb +23 -0
  327. data/lib/arel/visitors.rb +20 -0
  328. data/lib/arel/window_predications.rb +9 -0
  329. data/lib/arel.rb +51 -0
  330. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  331. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  332. data/lib/rails/generators/active_record/migration/migration_generator.rb +59 -9
  333. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  334. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +48 -0
  335. data/lib/rails/generators/active_record/migration.rb +41 -8
  336. data/lib/rails/generators/active_record/model/model_generator.rb +24 -22
  337. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  338. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +1 -1
  339. data/lib/rails/generators/active_record.rb +10 -16
  340. metadata +285 -149
  341. data/examples/associations.png +0 -0
  342. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  343. data/lib/active_record/associations/join_helper.rb +0 -55
  344. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  345. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  346. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  347. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  348. data/lib/active_record/associations/preloader/has_many_through.rb +0 -15
  349. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  350. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  351. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  352. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  353. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -188
  354. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -426
  355. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -579
  356. data/lib/active_record/dynamic_finder_match.rb +0 -68
  357. data/lib/active_record/dynamic_scope_match.rb +0 -23
  358. data/lib/active_record/fixtures/file.rb +0 -65
  359. data/lib/active_record/identity_map.rb +0 -162
  360. data/lib/active_record/observer.rb +0 -121
  361. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  362. data/lib/active_record/serializers/xml_serializer.rb +0 -203
  363. data/lib/active_record/session_store.rb +0 -358
  364. data/lib/active_record/test_case.rb +0 -73
  365. data/lib/rails/generators/active_record/migration/templates/migration.rb +0 -34
  366. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
  367. data/lib/rails/generators/active_record/model/templates/model.rb +0 -12
  368. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  369. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  370. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  371. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,85 +1,29 @@
1
- require 'active_support/core_ext/object/blank'
2
- require 'active_support/core_ext/hash/indifferent_access'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/string/filters"
3
4
 
4
5
  module ActiveRecord
5
6
  module FinderMethods
6
- # Find operates with four different retrieval approaches:
7
- #
8
- # * Find by id - This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]).
9
- # If no record can be found for all of the listed ids, then RecordNotFound will be raised.
10
- # * Find first - This will return the first record matched by the options used. These options can either be specific
11
- # conditions or merely an order. If no record can be matched, +nil+ is returned. Use
12
- # <tt>Model.find(:first, *args)</tt> or its shortcut <tt>Model.first(*args)</tt>.
13
- # * Find last - This will return the last record matched by the options used. These options can either be specific
14
- # conditions or merely an order. If no record can be matched, +nil+ is returned. Use
15
- # <tt>Model.find(:last, *args)</tt> or its shortcut <tt>Model.last(*args)</tt>.
16
- # * Find all - This will return all the records matched by the options used.
17
- # If no records are found, an empty array is returned. Use
18
- # <tt>Model.find(:all, *args)</tt> or its shortcut <tt>Model.all(*args)</tt>.
19
- #
20
- # All approaches accept an options hash as their last parameter.
21
- #
22
- # ==== Options
23
- #
24
- # * <tt>:conditions</tt> - An SQL fragment like "administrator = 1", <tt>["user_name = ?", username]</tt>,
25
- # or <tt>["user_name = :user_name", { :user_name => user_name }]</tt>. See conditions in the intro.
26
- # * <tt>:order</tt> - An SQL fragment like "created_at DESC, name".
27
- # * <tt>:group</tt> - An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
28
- # * <tt>:having</tt> - Combined with +:group+ this can be used to filter the records that a
29
- # <tt>GROUP BY</tt> returns. Uses the <tt>HAVING</tt> SQL-clause.
30
- # * <tt>:limit</tt> - An integer determining the limit on the number of rows that should be returned.
31
- # * <tt>:offset</tt> - An integer determining the offset from where the rows should be fetched. So at 5,
32
- # it would skip rows 0 through 4.
33
- # * <tt>:joins</tt> - Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id" (rarely needed),
34
- # named associations in the same form used for the <tt>:include</tt> option, which will perform an
35
- # <tt>INNER JOIN</tt> on the associated table(s),
36
- # or an array containing a mixture of both strings and named associations.
37
- # If the value is a string, then the records will be returned read-only since they will
38
- # have attributes that do not correspond to the table's columns.
39
- # Pass <tt>:readonly => false</tt> to override.
40
- # * <tt>:include</tt> - Names associations that should be loaded alongside. The symbols named refer
41
- # to already defined associations. See eager loading under Associations.
42
- # * <tt>:select</tt> - By default, this is "*" as in "SELECT * FROM", but can be changed if you,
43
- # for example, want to do a join but not include the joined columns. Takes a string with the SELECT SQL fragment (e.g. "id, name").
44
- # * <tt>:from</tt> - By default, this is the table name of the class, but can be changed
45
- # to an alternate table name (or even the name of a database view).
46
- # * <tt>:readonly</tt> - Mark the returned records read-only so they cannot be saved or updated.
47
- # * <tt>:lock</tt> - An SQL fragment like "FOR UPDATE" or "LOCK IN SHARE MODE".
48
- # <tt>:lock => true</tt> gives connection's default exclusive lock, usually "FOR UPDATE".
49
- #
50
- # ==== Examples
51
- #
52
- # # find by id
53
- # Person.find(1) # returns the object for ID = 1
54
- # Person.find(1, 2, 6) # returns an array for objects with IDs in (1, 2, 6)
55
- # Person.find([7, 17]) # returns an array for objects with IDs in (7, 17)
56
- # Person.find([1]) # returns an array for the object with ID = 1
57
- # Person.where("administrator = 1").order("created_on DESC").find(1)
58
- #
59
- # Note that returned records may not be in the same order as the ids you
60
- # provide since database rows are unordered. Give an explicit <tt>:order</tt>
61
- # to ensure the results are sorted.
62
- #
63
- # ==== Examples
7
+ ONE_AS_ONE = "1 AS one"
8
+
9
+ # Find by id - This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]).
10
+ # If one or more records cannot be found for the requested ids, then ActiveRecord::RecordNotFound will be raised.
11
+ # If the primary key is an integer, find by id coerces its arguments by using +to_i+.
64
12
  #
65
- # # find first
66
- # Person.first # returns the first object fetched by SELECT * FROM people
67
- # Person.where(["user_name = ?", user_name]).first
68
- # Person.where(["user_name = :u", { :u => user_name }]).first
69
- # Person.order("created_on DESC").offset(5).first
13
+ # Person.find(1) # returns the object for ID = 1
14
+ # Person.find("1") # returns the object for ID = 1
15
+ # Person.find("31-sarah") # returns the object for ID = 31
16
+ # Person.find(1, 2, 6) # returns an array for objects with IDs in (1, 2, 6)
17
+ # Person.find([7, 17]) # returns an array for objects with IDs in (7, 17)
18
+ # Person.find([1]) # returns an array for the object with ID = 1
19
+ # Person.where("administrator = 1").order("created_on DESC").find(1)
70
20
  #
71
- # # find last
72
- # Person.last # returns the last object fetched by SELECT * FROM people
73
- # Person.where(["user_name = ?", user_name]).last
74
- # Person.order("created_on DESC").offset(5).last
21
+ # NOTE: The returned records are in the same order as the ids you provide.
22
+ # If you want the results to be sorted by database, you can use ActiveRecord::QueryMethods#where
23
+ # method and provide an explicit ActiveRecord::QueryMethods#order option.
24
+ # But ActiveRecord::QueryMethods#where method doesn't raise ActiveRecord::RecordNotFound.
75
25
  #
76
- # # find all
77
- # Person.all # returns an array of objects for all the rows fetched by SELECT * FROM people
78
- # Person.where(["category IN (?)", categories]).limit(50).all
79
- # Person.where({ :friends => ["Bob", "Steve", "Fred"] }).all
80
- # Person.offset(10).limit(10).all
81
- # Person.includes([:account, :friends]).all
82
- # Person.group("category").all
26
+ # ==== Find with lock
83
27
  #
84
28
  # Example for find with a lock: Imagine two concurrent transactions:
85
29
  # each will read <tt>person.visits == 2</tt>, add 1 to it, and save, resulting
@@ -92,308 +36,517 @@ module ActiveRecord
92
36
  # person.visits += 1
93
37
  # person.save!
94
38
  # end
39
+ #
40
+ # ==== Variations of #find
41
+ #
42
+ # Person.where(name: 'Spartacus', rating: 4)
43
+ # # returns a chainable list (which can be empty).
44
+ #
45
+ # Person.find_by(name: 'Spartacus', rating: 4)
46
+ # # returns the first item or nil.
47
+ #
48
+ # Person.find_or_initialize_by(name: 'Spartacus', rating: 4)
49
+ # # returns the first item or returns a new instance (requires you call .save to persist against the database).
50
+ #
51
+ # Person.find_or_create_by(name: 'Spartacus', rating: 4)
52
+ # # returns the first item or creates it and returns it.
53
+ #
54
+ # ==== Alternatives for #find
55
+ #
56
+ # Person.where(name: 'Spartacus', rating: 4).exists?(conditions = :none)
57
+ # # returns a boolean indicating if any record with the given conditions exist.
58
+ #
59
+ # Person.where(name: 'Spartacus', rating: 4).select("field1, field2, field3")
60
+ # # returns a chainable list of instances with only the mentioned fields.
61
+ #
62
+ # Person.where(name: 'Spartacus', rating: 4).ids
63
+ # # returns an Array of ids.
64
+ #
65
+ # Person.where(name: 'Spartacus', rating: 4).pluck(:field1, :field2)
66
+ # # returns an Array of the required fields.
95
67
  def find(*args)
96
- return to_a.find { |*block_args| yield(*block_args) } if block_given?
68
+ return super if block_given?
69
+ find_with_ids(*args)
70
+ end
97
71
 
98
- options = args.extract_options!
72
+ # Finds the first record matching the specified conditions. There
73
+ # is no implied ordering so if order matters, you should specify it
74
+ # yourself.
75
+ #
76
+ # If no record is found, returns <tt>nil</tt>.
77
+ #
78
+ # Post.find_by name: 'Spartacus', rating: 4
79
+ # Post.find_by "published_at < ?", 2.weeks.ago
80
+ def find_by(arg, *args)
81
+ where(arg, *args).take
82
+ end
99
83
 
100
- if options.present?
101
- apply_finder_options(options).find(*args)
102
- else
103
- case args.first
104
- when :first, :last, :all
105
- send(args.first)
106
- else
107
- find_with_ids(*args)
108
- end
109
- end
84
+ # Like #find_by, except that if no record is found, raises
85
+ # an ActiveRecord::RecordNotFound error.
86
+ def find_by!(arg, *args)
87
+ where(arg, *args).take!
110
88
  end
111
89
 
112
- # A convenience wrapper for <tt>find(:first, *args)</tt>. You can pass in all the
113
- # same arguments to this method as you can to <tt>find(:first)</tt>.
114
- def first(*args)
115
- if args.any?
116
- if args.first.kind_of?(Integer) || (loaded? && !args.first.kind_of?(Hash))
117
- limit(*args).to_a
118
- else
119
- apply_finder_options(args.first).first
120
- end
90
+ # Gives a record (or N records if a parameter is supplied) without any implied
91
+ # order. The order will depend on the database implementation.
92
+ # If an order is supplied it will be respected.
93
+ #
94
+ # Person.take # returns an object fetched by SELECT * FROM people LIMIT 1
95
+ # Person.take(5) # returns 5 objects fetched by SELECT * FROM people LIMIT 5
96
+ # Person.where(["name LIKE '%?'", name]).take
97
+ def take(limit = nil)
98
+ limit ? find_take_with_limit(limit) : find_take
99
+ end
100
+
101
+ # Same as #take but raises ActiveRecord::RecordNotFound if no record
102
+ # is found. Note that #take! accepts no arguments.
103
+ def take!
104
+ take || raise_record_not_found_exception!
105
+ end
106
+
107
+ # Find the first record (or first N records if a parameter is supplied).
108
+ # If no order is defined it will order by primary key.
109
+ #
110
+ # Person.first # returns the first object fetched by SELECT * FROM people ORDER BY people.id LIMIT 1
111
+ # Person.where(["user_name = ?", user_name]).first
112
+ # Person.where(["user_name = :u", { u: user_name }]).first
113
+ # Person.order("created_on DESC").offset(5).first
114
+ # Person.first(3) # returns the first three objects fetched by SELECT * FROM people ORDER BY people.id LIMIT 3
115
+ #
116
+ def first(limit = nil)
117
+ if limit
118
+ find_nth_with_limit(0, limit)
121
119
  else
122
- find_first
120
+ find_nth 0
123
121
  end
124
122
  end
125
123
 
126
- # Same as +first+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
127
- # is found. Note that <tt>first!</tt> accepts no arguments.
124
+ # Same as #first but raises ActiveRecord::RecordNotFound if no record
125
+ # is found. Note that #first! accepts no arguments.
128
126
  def first!
129
- first or raise RecordNotFound
127
+ first || raise_record_not_found_exception!
130
128
  end
131
129
 
132
- # A convenience wrapper for <tt>find(:last, *args)</tt>. You can pass in all the
133
- # same arguments to this method as you can to <tt>find(:last)</tt>.
134
- def last(*args)
135
- if args.any?
136
- if args.first.kind_of?(Integer) || (loaded? && !args.first.kind_of?(Hash))
137
- if order_values.empty?
138
- order("#{primary_key} DESC").limit(*args).reverse
139
- else
140
- to_a.last(*args)
141
- end
142
- else
143
- apply_finder_options(args.first).last
144
- end
145
- else
146
- find_last
147
- end
130
+ # Find the last record (or last N records if a parameter is supplied).
131
+ # If no order is defined it will order by primary key.
132
+ #
133
+ # Person.last # returns the last object fetched by SELECT * FROM people
134
+ # Person.where(["user_name = ?", user_name]).last
135
+ # Person.order("created_on DESC").offset(5).last
136
+ # Person.last(3) # returns the last three objects fetched by SELECT * FROM people.
137
+ #
138
+ # Take note that in that last case, the results are sorted in ascending order:
139
+ #
140
+ # [#<Person id:2>, #<Person id:3>, #<Person id:4>]
141
+ #
142
+ # and not:
143
+ #
144
+ # [#<Person id:4>, #<Person id:3>, #<Person id:2>]
145
+ def last(limit = nil)
146
+ return find_last(limit) if loaded? || has_limit_or_offset?
147
+
148
+ result = ordered_relation.limit(limit)
149
+ result = result.reverse_order!
150
+
151
+ limit ? result.reverse : result.first
148
152
  end
149
153
 
150
- # Same as +last+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
151
- # is found. Note that <tt>last!</tt> accepts no arguments.
154
+ # Same as #last but raises ActiveRecord::RecordNotFound if no record
155
+ # is found. Note that #last! accepts no arguments.
152
156
  def last!
153
- last or raise RecordNotFound
157
+ last || raise_record_not_found_exception!
158
+ end
159
+
160
+ # Find the second record.
161
+ # If no order is defined it will order by primary key.
162
+ #
163
+ # Person.second # returns the second object fetched by SELECT * FROM people
164
+ # Person.offset(3).second # returns the second object from OFFSET 3 (which is OFFSET 4)
165
+ # Person.where(["user_name = :u", { u: user_name }]).second
166
+ def second
167
+ find_nth 1
168
+ end
169
+
170
+ # Same as #second but raises ActiveRecord::RecordNotFound if no record
171
+ # is found.
172
+ def second!
173
+ second || raise_record_not_found_exception!
154
174
  end
155
175
 
156
- # A convenience wrapper for <tt>find(:all, *args)</tt>. You can pass in all the
157
- # same arguments to this method as you can to <tt>find(:all)</tt>.
158
- def all(*args)
159
- args.any? ? apply_finder_options(args.first).to_a : to_a
176
+ # Find the third record.
177
+ # If no order is defined it will order by primary key.
178
+ #
179
+ # Person.third # returns the third object fetched by SELECT * FROM people
180
+ # Person.offset(3).third # returns the third object from OFFSET 3 (which is OFFSET 5)
181
+ # Person.where(["user_name = :u", { u: user_name }]).third
182
+ def third
183
+ find_nth 2
184
+ end
185
+
186
+ # Same as #third but raises ActiveRecord::RecordNotFound if no record
187
+ # is found.
188
+ def third!
189
+ third || raise_record_not_found_exception!
190
+ end
191
+
192
+ # Find the fourth record.
193
+ # If no order is defined it will order by primary key.
194
+ #
195
+ # Person.fourth # returns the fourth object fetched by SELECT * FROM people
196
+ # Person.offset(3).fourth # returns the fourth object from OFFSET 3 (which is OFFSET 6)
197
+ # Person.where(["user_name = :u", { u: user_name }]).fourth
198
+ def fourth
199
+ find_nth 3
200
+ end
201
+
202
+ # Same as #fourth but raises ActiveRecord::RecordNotFound if no record
203
+ # is found.
204
+ def fourth!
205
+ fourth || raise_record_not_found_exception!
206
+ end
207
+
208
+ # Find the fifth record.
209
+ # If no order is defined it will order by primary key.
210
+ #
211
+ # Person.fifth # returns the fifth object fetched by SELECT * FROM people
212
+ # Person.offset(3).fifth # returns the fifth object from OFFSET 3 (which is OFFSET 7)
213
+ # Person.where(["user_name = :u", { u: user_name }]).fifth
214
+ def fifth
215
+ find_nth 4
216
+ end
217
+
218
+ # Same as #fifth but raises ActiveRecord::RecordNotFound if no record
219
+ # is found.
220
+ def fifth!
221
+ fifth || raise_record_not_found_exception!
222
+ end
223
+
224
+ # Find the forty-second record. Also known as accessing "the reddit".
225
+ # If no order is defined it will order by primary key.
226
+ #
227
+ # Person.forty_two # returns the forty-second object fetched by SELECT * FROM people
228
+ # Person.offset(3).forty_two # returns the forty-second object from OFFSET 3 (which is OFFSET 44)
229
+ # Person.where(["user_name = :u", { u: user_name }]).forty_two
230
+ def forty_two
231
+ find_nth 41
232
+ end
233
+
234
+ # Same as #forty_two but raises ActiveRecord::RecordNotFound if no record
235
+ # is found.
236
+ def forty_two!
237
+ forty_two || raise_record_not_found_exception!
238
+ end
239
+
240
+ # Find the third-to-last record.
241
+ # If no order is defined it will order by primary key.
242
+ #
243
+ # Person.third_to_last # returns the third-to-last object fetched by SELECT * FROM people
244
+ # Person.offset(3).third_to_last # returns the third-to-last object from OFFSET 3
245
+ # Person.where(["user_name = :u", { u: user_name }]).third_to_last
246
+ def third_to_last
247
+ find_nth_from_last 3
248
+ end
249
+
250
+ # Same as #third_to_last but raises ActiveRecord::RecordNotFound if no record
251
+ # is found.
252
+ def third_to_last!
253
+ third_to_last || raise_record_not_found_exception!
254
+ end
255
+
256
+ # Find the second-to-last record.
257
+ # If no order is defined it will order by primary key.
258
+ #
259
+ # Person.second_to_last # returns the second-to-last object fetched by SELECT * FROM people
260
+ # Person.offset(3).second_to_last # returns the second-to-last object from OFFSET 3
261
+ # Person.where(["user_name = :u", { u: user_name }]).second_to_last
262
+ def second_to_last
263
+ find_nth_from_last 2
264
+ end
265
+
266
+ # Same as #second_to_last but raises ActiveRecord::RecordNotFound if no record
267
+ # is found.
268
+ def second_to_last!
269
+ second_to_last || raise_record_not_found_exception!
160
270
  end
161
271
 
162
272
  # Returns true if a record exists in the table that matches the +id+ or
163
- # conditions given, or false otherwise. The argument can take five forms:
273
+ # conditions given, or false otherwise. The argument can take six forms:
164
274
  #
165
275
  # * Integer - Finds the record with this primary key.
166
276
  # * String - Finds the record with a primary key corresponding to this
167
277
  # string (such as <tt>'5'</tt>).
168
278
  # * Array - Finds the record that matches these +find+-style conditions
169
- # (such as <tt>['color = ?', 'red']</tt>).
279
+ # (such as <tt>['name LIKE ?', "%#{query}%"]</tt>).
170
280
  # * Hash - Finds the record that matches these +find+-style conditions
171
- # (such as <tt>{:color => 'red'}</tt>).
172
- # * No args - Returns false if the table is empty, true otherwise.
281
+ # (such as <tt>{name: 'David'}</tt>).
282
+ # * +false+ - Returns always +false+.
283
+ # * No args - Returns +false+ if the relation is empty, +true+ otherwise.
173
284
  #
174
- # For more information about specifying conditions as a Hash or Array,
285
+ # For more information about specifying conditions as a hash or array,
175
286
  # see the Conditions section in the introduction to ActiveRecord::Base.
176
287
  #
177
288
  # Note: You can't pass in a condition as a string (like <tt>name =
178
289
  # 'Jamie'</tt>), since it would be sanitized and then queried against
179
290
  # the primary key column, like <tt>id = 'name = \'Jamie\''</tt>.
180
291
  #
181
- # ==== Examples
182
292
  # Person.exists?(5)
183
293
  # Person.exists?('5')
184
- # Person.exists?(:name => "David")
185
294
  # Person.exists?(['name LIKE ?', "%#{query}%"])
295
+ # Person.exists?(id: [1, 4, 8])
296
+ # Person.exists?(name: 'David')
297
+ # Person.exists?(false)
186
298
  # Person.exists?
187
- def exists?(id = false)
188
- id = id.id if ActiveRecord::Base === id
189
- return false if id.nil?
190
-
191
- join_dependency = construct_join_dependency_for_association_find
192
- relation = construct_relation_for_association_find(join_dependency)
193
- relation = relation.except(:select, :order).select("1 AS one").limit(1)
194
-
195
- case id
196
- when Array, Hash
197
- relation = relation.where(id)
198
- else
199
- relation = relation.where(table[primary_key].eq(id)) if id
299
+ # Person.where(name: 'Spartacus', rating: 4).exists?
300
+ def exists?(conditions = :none)
301
+ if Base === conditions
302
+ raise ArgumentError, <<-MSG.squish
303
+ You are passing an instance of ActiveRecord::Base to `exists?`.
304
+ Please pass the id of the object by calling `.id`.
305
+ MSG
200
306
  end
201
307
 
202
- connection.select_value(relation, "#{name} Exists") ? true : false
203
- rescue ThrowResult
204
- false
205
- end
308
+ return false if !conditions || limit_value == 0
206
309
 
207
- protected
310
+ if eager_loading?
311
+ relation = apply_join_dependency(eager_loading: false)
312
+ return relation.exists?(conditions)
313
+ end
208
314
 
209
- def find_with_associations
210
- join_dependency = construct_join_dependency_for_association_find
211
- relation = construct_relation_for_association_find(join_dependency)
212
- rows = connection.select_all(relation, 'SQL', relation.bind_values.dup)
213
- join_dependency.instantiate(rows)
214
- rescue ThrowResult
215
- []
216
- end
315
+ relation = construct_relation_for_exists(conditions)
217
316
 
218
- def construct_join_dependency_for_association_find
219
- including = (@eager_load_values + @includes_values).uniq
220
- ActiveRecord::Associations::JoinDependency.new(@klass, including, [])
317
+ skip_query_cache_if_necessary { connection.select_one(relation.arel, "#{name} Exists?") } ? true : false
221
318
  end
222
319
 
223
- def construct_relation_for_association_calculations
224
- including = (@eager_load_values + @includes_values).uniq
225
- join_dependency = ActiveRecord::Associations::JoinDependency.new(@klass, including, arel.froms.first)
226
- relation = except(:includes, :eager_load, :preload)
227
- apply_join_dependency(relation, join_dependency)
320
+ # This method is called whenever no records are found with either a single
321
+ # id or multiple ids and raises an ActiveRecord::RecordNotFound exception.
322
+ #
323
+ # The error message is different depending on whether a single id or
324
+ # multiple ids are provided. If multiple ids are provided, then the number
325
+ # of results obtained should be provided in the +result_size+ argument and
326
+ # the expected number of results should be provided in the +expected_size+
327
+ # argument.
328
+ def raise_record_not_found_exception!(ids = nil, result_size = nil, expected_size = nil, key = primary_key, not_found_ids = nil) # :nodoc:
329
+ conditions = arel.where_sql(@klass)
330
+ conditions = " [#{conditions}]" if conditions
331
+ name = @klass.name
332
+
333
+ if ids.nil?
334
+ error = +"Couldn't find #{name}"
335
+ error << " with#{conditions}" if conditions
336
+ raise RecordNotFound.new(error, name, key)
337
+ elsif Array(ids).size == 1
338
+ error = "Couldn't find #{name} with '#{key}'=#{ids}#{conditions}"
339
+ raise RecordNotFound.new(error, name, key, ids)
340
+ else
341
+ error = +"Couldn't find all #{name.pluralize} with '#{key}': "
342
+ error << "(#{ids.join(", ")})#{conditions} (found #{result_size} results, but was looking for #{expected_size})."
343
+ error << " Couldn't find #{name.pluralize(not_found_ids.size)} with #{key.to_s.pluralize(not_found_ids.size)} #{not_found_ids.join(', ')}." if not_found_ids
344
+ raise RecordNotFound.new(error, name, key, ids)
345
+ end
228
346
  end
229
347
 
230
- def construct_relation_for_association_find(join_dependency)
231
- relation = except(:includes, :eager_load, :preload, :select).select(join_dependency.columns)
232
- apply_join_dependency(relation, join_dependency)
233
- end
348
+ private
234
349
 
235
- def apply_join_dependency(relation, join_dependency)
236
- join_dependency.join_associations.each do |association|
237
- relation = association.join_relation(relation)
350
+ def offset_index
351
+ offset_value || 0
238
352
  end
239
353
 
240
- limitable_reflections = using_limitable_reflections?(join_dependency.reflections)
354
+ def construct_relation_for_exists(conditions)
355
+ conditions = sanitize_forbidden_attributes(conditions)
241
356
 
242
- if !limitable_reflections && relation.limit_value
243
- limited_id_condition = construct_limited_ids_condition(relation.except(:select))
244
- relation = relation.where(limited_id_condition)
245
- end
357
+ if distinct_value && offset_value
358
+ relation = except(:order).limit!(1)
359
+ else
360
+ relation = except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
361
+ end
246
362
 
247
- relation = relation.except(:limit, :offset) unless limitable_reflections
363
+ case conditions
364
+ when Array, Hash
365
+ relation.where!(conditions) unless conditions.empty?
366
+ else
367
+ relation.where!(primary_key => conditions) unless conditions == :none
368
+ end
248
369
 
249
- relation
250
- end
370
+ relation
371
+ end
251
372
 
252
- def construct_limited_ids_condition(relation)
253
- orders = relation.order_values.map { |val| val.presence }.compact
254
- values = @klass.connection.distinct("#{@klass.connection.quote_table_name table_name}.#{primary_key}", orders)
373
+ def apply_join_dependency(eager_loading: group_values.empty?)
374
+ join_dependency = construct_join_dependency(
375
+ eager_load_values + includes_values, Arel::Nodes::OuterJoin
376
+ )
377
+ relation = except(:includes, :eager_load, :preload).joins!(join_dependency)
255
378
 
256
- relation = relation.dup
379
+ if eager_loading && !using_limitable_reflections?(join_dependency.reflections)
380
+ if has_limit_or_offset?
381
+ limited_ids = limited_ids_for(relation)
382
+ limited_ids.empty? ? relation.none! : relation.where!(primary_key => limited_ids)
383
+ end
384
+ relation.limit_value = relation.offset_value = nil
385
+ end
257
386
 
258
- ids_array = relation.select(values).collect {|row| row[primary_key]}
259
- ids_array.empty? ? raise(ThrowResult) : table[primary_key].in(ids_array)
260
- end
387
+ if block_given?
388
+ yield relation, join_dependency
389
+ else
390
+ relation
391
+ end
392
+ end
261
393
 
262
- def find_by_attributes(match, attributes, *args)
263
- conditions = Hash[attributes.map {|a| [a, args[attributes.index(a)]]}]
264
- result = where(conditions).send(match.finder)
394
+ def limited_ids_for(relation)
395
+ values = @klass.connection.columns_for_distinct(
396
+ connection.visitor.compile(arel_attribute(primary_key)),
397
+ relation.order_values
398
+ )
265
399
 
266
- if match.bang? && result.blank?
267
- raise RecordNotFound, "Couldn't find #{@klass.name} with #{conditions.to_a.collect {|p| p.join(' = ')}.join(', ')}"
268
- else
269
- yield(result) if block_given?
270
- result
400
+ relation = relation.except(:select).select(values).distinct!
401
+
402
+ id_rows = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, "SQL") }
403
+ id_rows.map { |row| row[primary_key] }
271
404
  end
272
- end
273
405
 
274
- def find_or_instantiator_by_attributes(match, attributes, *args)
275
- options = args.size > 1 && args.last(2).all?{ |a| a.is_a?(Hash) } ? args.extract_options! : {}
276
- protected_attributes_for_create, unprotected_attributes_for_create = {}, {}
277
- args.each_with_index do |arg, i|
278
- if arg.is_a?(Hash)
279
- protected_attributes_for_create = args[i].with_indifferent_access
280
- else
281
- unprotected_attributes_for_create[attributes[i]] = args[i]
282
- end
406
+ def using_limitable_reflections?(reflections)
407
+ reflections.none?(&:collection?)
283
408
  end
284
409
 
285
- conditions = (protected_attributes_for_create.merge(unprotected_attributes_for_create)).slice(*attributes).symbolize_keys
410
+ def find_with_ids(*ids)
411
+ raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
412
+
413
+ expects_array = ids.first.kind_of?(Array)
414
+ return [] if expects_array && ids.first.empty?
415
+
416
+ ids = ids.flatten.compact.uniq
286
417
 
287
- record = where(conditions).first
418
+ model_name = @klass.name
288
419
 
289
- unless record
290
- record = @klass.new(protected_attributes_for_create, options) do |r|
291
- r.assign_attributes(unprotected_attributes_for_create, :without_protection => true)
420
+ case ids.size
421
+ when 0
422
+ error_message = "Couldn't find #{model_name} without an ID"
423
+ raise RecordNotFound.new(error_message, model_name, primary_key)
424
+ when 1
425
+ result = find_one(ids.first)
426
+ expects_array ? [ result ] : result
427
+ else
428
+ find_some(ids)
292
429
  end
293
- yield(record) if block_given?
294
- record.send(match.save_method) if match.save_record?
295
430
  end
296
431
 
297
- record
298
- end
299
-
300
- def find_with_ids(*ids)
301
- return to_a.find { |*block_args| yield(*block_args) } if block_given?
432
+ def find_one(id)
433
+ if ActiveRecord::Base === id
434
+ raise ArgumentError, <<-MSG.squish
435
+ You are passing an instance of ActiveRecord::Base to `find`.
436
+ Please pass the id of the object by calling `.id`.
437
+ MSG
438
+ end
302
439
 
303
- expects_array = ids.first.kind_of?(Array)
304
- return ids.first if expects_array && ids.first.empty?
440
+ relation = where(primary_key => id)
441
+ record = relation.take
305
442
 
306
- ids = ids.flatten.compact.uniq
443
+ raise_record_not_found_exception!(id, 0, 1) unless record
307
444
 
308
- case ids.size
309
- when 0
310
- raise RecordNotFound, "Couldn't find #{@klass.name} without an ID"
311
- when 1
312
- result = find_one(ids.first)
313
- expects_array ? [ result ] : result
314
- else
315
- find_some(ids)
445
+ record
316
446
  end
317
- end
318
447
 
319
- def find_one(id)
320
- id = id.id if ActiveRecord::Base === id
448
+ def find_some(ids)
449
+ return find_some_ordered(ids) unless order_values.present?
321
450
 
322
- if IdentityMap.enabled? && where_values.blank? &&
323
- limit_value.blank? && order_values.blank? &&
324
- includes_values.blank? && preload_values.blank? &&
325
- readonly_value.nil? && joins_values.blank? &&
326
- !@klass.locking_enabled? &&
327
- record = IdentityMap.get(@klass, id)
328
- return record
329
- end
451
+ result = where(primary_key => ids).to_a
330
452
 
331
- column = columns_hash[primary_key]
453
+ expected_size =
454
+ if limit_value && ids.size > limit_value
455
+ limit_value
456
+ else
457
+ ids.size
458
+ end
332
459
 
333
- substitute = connection.substitute_at(column, @bind_values.length)
334
- relation = where(table[primary_key].eq(substitute))
335
- relation.bind_values = [[column, id]]
336
- record = relation.first
460
+ # 11 ids with limit 3, offset 9 should give 2 results.
461
+ if offset_value && (ids.size - offset_value < expected_size)
462
+ expected_size = ids.size - offset_value
463
+ end
337
464
 
338
- unless record
339
- conditions = arel.where_sql
340
- conditions = " [#{conditions}]" if conditions
341
- raise RecordNotFound, "Couldn't find #{@klass.name} with #{primary_key}=#{id}#{conditions}"
465
+ if result.size == expected_size
466
+ result
467
+ else
468
+ raise_record_not_found_exception!(ids, result.size, expected_size)
469
+ end
342
470
  end
343
471
 
344
- record
345
- end
472
+ def find_some_ordered(ids)
473
+ ids = ids.slice(offset_value || 0, limit_value || ids.size) || []
474
+
475
+ result = except(:limit, :offset).where(primary_key => ids).records
346
476
 
347
- def find_some(ids)
348
- result = where(table[primary_key].in(ids)).all
477
+ if result.size == ids.size
478
+ pk_type = @klass.type_for_attribute(primary_key)
349
479
 
350
- expected_size =
351
- if @limit_value && ids.size > @limit_value
352
- @limit_value
480
+ records_by_id = result.index_by(&:id)
481
+ ids.map { |id| records_by_id.fetch(pk_type.cast(id)) }
353
482
  else
354
- ids.size
483
+ raise_record_not_found_exception!(ids, result.size, ids.size)
355
484
  end
485
+ end
356
486
 
357
- # 11 ids with limit 3, offset 9 should give 2 results.
358
- if @offset_value && (ids.size - @offset_value < expected_size)
359
- expected_size = ids.size - @offset_value
487
+ def find_take
488
+ if loaded?
489
+ records.first
490
+ else
491
+ @take ||= limit(1).records.first
492
+ end
360
493
  end
361
494
 
362
- if result.size == expected_size
363
- result
364
- else
365
- conditions = arel.where_sql
366
- conditions = " [#{conditions}]" if conditions
495
+ def find_take_with_limit(limit)
496
+ if loaded?
497
+ records.take(limit)
498
+ else
499
+ limit(limit).to_a
500
+ end
501
+ end
367
502
 
368
- error = "Couldn't find all #{@klass.name.pluralize} with IDs "
369
- error << "(#{ids.join(", ")})#{conditions} (found #{result.size} results, but was looking for #{expected_size})"
370
- raise RecordNotFound, error
503
+ def find_nth(index)
504
+ @offsets[offset_index + index] ||= find_nth_with_limit(index, 1).first
371
505
  end
372
- end
373
506
 
374
- def find_first
375
- if loaded?
376
- @records.first
377
- else
378
- @first ||= limit(1).to_a[0]
507
+ def find_nth_with_limit(index, limit)
508
+ if loaded?
509
+ records[index, limit] || []
510
+ else
511
+ relation = ordered_relation
512
+
513
+ if limit_value
514
+ limit = [limit_value - index, limit].min
515
+ end
516
+
517
+ if limit > 0
518
+ relation = relation.offset(offset_index + index) unless index.zero?
519
+ relation.limit(limit).to_a
520
+ else
521
+ []
522
+ end
523
+ end
379
524
  end
380
- end
381
525
 
382
- def find_last
383
- if loaded?
384
- @records.last
385
- else
386
- @last ||=
387
- if offset_value || limit_value
388
- to_a.last
526
+ def find_nth_from_last(index)
527
+ if loaded?
528
+ records[-index]
529
+ else
530
+ relation = ordered_relation
531
+
532
+ if equal?(relation) || has_limit_or_offset?
533
+ relation.records[-index]
389
534
  else
390
- reverse_order.limit(1).to_a[0]
535
+ relation.last(index)[-index]
391
536
  end
537
+ end
392
538
  end
393
- end
394
539
 
395
- def using_limitable_reflections?(reflections)
396
- reflections.none? { |r| r.collection? }
397
- end
540
+ def find_last(limit)
541
+ limit ? records.last(limit) : records.last
542
+ end
543
+
544
+ def ordered_relation
545
+ if order_values.empty? && (implicit_order_column || primary_key)
546
+ order(arel_attribute(implicit_order_column || primary_key).asc)
547
+ else
548
+ self
549
+ end
550
+ end
398
551
  end
399
552
  end