activerecord 5.0.7.2 → 6.0.6.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (359) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +844 -1944
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +9 -7
  5. data/examples/performance.rb +31 -29
  6. data/examples/simple.rb +5 -3
  7. data/lib/active_record/advisory_lock_base.rb +18 -0
  8. data/lib/active_record/aggregations.rb +249 -247
  9. data/lib/active_record/association_relation.rb +18 -14
  10. data/lib/active_record/associations/alias_tracker.rb +24 -34
  11. data/lib/active_record/associations/association.rb +113 -55
  12. data/lib/active_record/associations/association_scope.rb +102 -96
  13. data/lib/active_record/associations/belongs_to_association.rb +58 -42
  14. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
  15. data/lib/active_record/associations/builder/association.rb +18 -25
  16. data/lib/active_record/associations/builder/belongs_to.rb +43 -54
  17. data/lib/active_record/associations/builder/collection_association.rb +7 -18
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +41 -62
  19. data/lib/active_record/associations/builder/has_many.rb +4 -0
  20. data/lib/active_record/associations/builder/has_one.rb +37 -1
  21. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  22. data/lib/active_record/associations/collection_association.rb +93 -254
  23. data/lib/active_record/associations/collection_proxy.rb +159 -122
  24. data/lib/active_record/associations/foreign_association.rb +9 -0
  25. data/lib/active_record/associations/has_many_association.rb +23 -30
  26. data/lib/active_record/associations/has_many_through_association.rb +58 -44
  27. data/lib/active_record/associations/has_one_association.rb +59 -54
  28. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  29. data/lib/active_record/associations/join_dependency/join_association.rb +43 -85
  30. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  31. data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
  32. data/lib/active_record/associations/join_dependency.rb +152 -177
  33. data/lib/active_record/associations/preloader/association.rb +101 -97
  34. data/lib/active_record/associations/preloader/through_association.rb +77 -76
  35. data/lib/active_record/associations/preloader.rb +94 -103
  36. data/lib/active_record/associations/singular_association.rb +12 -45
  37. data/lib/active_record/associations/through_association.rb +27 -15
  38. data/lib/active_record/associations.rb +1603 -1592
  39. data/lib/active_record/attribute_assignment.rb +54 -61
  40. data/lib/active_record/attribute_decorators.rb +38 -17
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +12 -8
  42. data/lib/active_record/attribute_methods/dirty.rb +179 -109
  43. data/lib/active_record/attribute_methods/primary_key.rb +85 -92
  44. data/lib/active_record/attribute_methods/query.rb +4 -3
  45. data/lib/active_record/attribute_methods/read.rb +20 -49
  46. data/lib/active_record/attribute_methods/serialization.rb +29 -7
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -66
  48. data/lib/active_record/attribute_methods/write.rb +34 -33
  49. data/lib/active_record/attribute_methods.rb +66 -106
  50. data/lib/active_record/attributes.rb +38 -25
  51. data/lib/active_record/autosave_association.rb +58 -39
  52. data/lib/active_record/base.rb +27 -24
  53. data/lib/active_record/callbacks.rb +64 -35
  54. data/lib/active_record/coders/json.rb +2 -0
  55. data/lib/active_record/coders/yaml_column.rb +34 -13
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +558 -323
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +23 -5
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +215 -94
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +59 -35
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +128 -75
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +33 -28
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +233 -147
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +68 -80
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +400 -213
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +169 -79
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +373 -202
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +401 -562
  69. data/lib/active_record/connection_adapters/column.rb +41 -13
  70. data/lib/active_record/connection_adapters/connection_specification.rb +172 -139
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +11 -4
  72. data/lib/active_record/connection_adapters/mysql/column.rb +8 -31
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +137 -49
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +24 -23
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -20
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +49 -45
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +58 -56
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +70 -36
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +268 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +12 -13
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +48 -30
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +19 -31
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +64 -54
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +5 -3
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +22 -11
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +6 -5
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +12 -2
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -4
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +19 -18
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -5
  99. data/lib/active_record/connection_adapters/postgresql/oid/{json.rb → oid.rb} +6 -1
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +30 -9
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -30
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +8 -4
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +24 -21
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +95 -35
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +20 -26
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +147 -105
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +34 -32
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +378 -308
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +26 -25
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -6
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +383 -275
  117. data/lib/active_record/connection_adapters/schema_cache.rb +46 -12
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +13 -8
  119. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +119 -0
  120. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +3 -1
  121. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +72 -18
  122. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +3 -8
  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 +261 -267
  127. data/lib/active_record/connection_adapters/statement_pool.rb +9 -8
  128. data/lib/active_record/connection_handling.rb +143 -40
  129. data/lib/active_record/core.rb +207 -160
  130. data/lib/active_record/counter_cache.rb +60 -28
  131. data/lib/active_record/database_configurations/database_config.rb +37 -0
  132. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  133. data/lib/active_record/database_configurations/url_config.rb +78 -0
  134. data/lib/active_record/database_configurations.rb +233 -0
  135. data/lib/active_record/define_callbacks.rb +22 -0
  136. data/lib/active_record/dynamic_matchers.rb +87 -87
  137. data/lib/active_record/enum.rb +67 -23
  138. data/lib/active_record/errors.rb +114 -18
  139. data/lib/active_record/explain.rb +4 -4
  140. data/lib/active_record/explain_registry.rb +3 -1
  141. data/lib/active_record/explain_subscriber.rb +9 -4
  142. data/lib/active_record/fixture_set/file.rb +13 -8
  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 +152 -0
  146. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  147. data/lib/active_record/fixtures.rb +194 -504
  148. data/lib/active_record/gem_version.rb +5 -3
  149. data/lib/active_record/inheritance.rb +150 -99
  150. data/lib/active_record/insert_all.rb +179 -0
  151. data/lib/active_record/integration.rb +116 -25
  152. data/lib/active_record/internal_metadata.rb +16 -19
  153. data/lib/active_record/legacy_yaml_adapter.rb +4 -2
  154. data/lib/active_record/locking/optimistic.rb +85 -86
  155. data/lib/active_record/locking/pessimistic.rb +18 -6
  156. data/lib/active_record/log_subscriber.rb +48 -29
  157. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  158. data/lib/active_record/middleware/database_selector/resolver.rb +87 -0
  159. data/lib/active_record/middleware/database_selector.rb +74 -0
  160. data/lib/active_record/migration/command_recorder.rb +134 -100
  161. data/lib/active_record/migration/compatibility.rb +174 -56
  162. data/lib/active_record/migration/join_table.rb +8 -7
  163. data/lib/active_record/migration.rb +369 -302
  164. data/lib/active_record/model_schema.rb +160 -127
  165. data/lib/active_record/nested_attributes.rb +213 -202
  166. data/lib/active_record/no_touching.rb +12 -3
  167. data/lib/active_record/null_relation.rb +12 -34
  168. data/lib/active_record/persistence.rb +446 -77
  169. data/lib/active_record/query_cache.rb +13 -12
  170. data/lib/active_record/querying.rb +37 -24
  171. data/lib/active_record/railtie.rb +128 -36
  172. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  173. data/lib/active_record/railties/console_sandbox.rb +2 -0
  174. data/lib/active_record/railties/controller_runtime.rb +34 -33
  175. data/lib/active_record/railties/databases.rake +312 -177
  176. data/lib/active_record/readonly_attributes.rb +5 -4
  177. data/lib/active_record/reflection.rb +214 -254
  178. data/lib/active_record/relation/batches/batch_enumerator.rb +3 -1
  179. data/lib/active_record/relation/batches.rb +98 -52
  180. data/lib/active_record/relation/calculations.rb +212 -173
  181. data/lib/active_record/relation/delegation.rb +73 -69
  182. data/lib/active_record/relation/finder_methods.rb +207 -247
  183. data/lib/active_record/relation/from_clause.rb +6 -8
  184. data/lib/active_record/relation/merger.rb +82 -61
  185. data/lib/active_record/relation/predicate_builder/array_handler.rb +20 -14
  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 +4 -3
  188. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +6 -4
  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 +7 -18
  191. data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
  192. data/lib/active_record/relation/predicate_builder.rb +83 -105
  193. data/lib/active_record/relation/query_attribute.rb +33 -2
  194. data/lib/active_record/relation/query_methods.rb +488 -332
  195. data/lib/active_record/relation/record_fetch_warning.rb +5 -3
  196. data/lib/active_record/relation/spawn_methods.rb +8 -8
  197. data/lib/active_record/relation/where_clause.rb +111 -96
  198. data/lib/active_record/relation/where_clause_factory.rb +6 -11
  199. data/lib/active_record/relation.rb +443 -318
  200. data/lib/active_record/result.rb +69 -40
  201. data/lib/active_record/runtime_registry.rb +5 -3
  202. data/lib/active_record/sanitization.rb +83 -99
  203. data/lib/active_record/schema.rb +7 -14
  204. data/lib/active_record/schema_dumper.rb +71 -69
  205. data/lib/active_record/schema_migration.rb +16 -6
  206. data/lib/active_record/scoping/default.rb +92 -95
  207. data/lib/active_record/scoping/named.rb +51 -26
  208. data/lib/active_record/scoping.rb +20 -20
  209. data/lib/active_record/secure_token.rb +4 -2
  210. data/lib/active_record/serialization.rb +2 -0
  211. data/lib/active_record/statement_cache.rb +63 -28
  212. data/lib/active_record/store.rb +121 -41
  213. data/lib/active_record/suppressor.rb +6 -3
  214. data/lib/active_record/table_metadata.rb +39 -18
  215. data/lib/active_record/tasks/database_tasks.rb +271 -81
  216. data/lib/active_record/tasks/mysql_database_tasks.rb +54 -91
  217. data/lib/active_record/tasks/postgresql_database_tasks.rb +77 -47
  218. data/lib/active_record/tasks/sqlite_database_tasks.rb +33 -16
  219. data/lib/active_record/test_databases.rb +23 -0
  220. data/lib/active_record/test_fixtures.rb +243 -0
  221. data/lib/active_record/timestamp.rb +70 -36
  222. data/lib/active_record/touch_later.rb +8 -6
  223. data/lib/active_record/transactions.rb +141 -157
  224. data/lib/active_record/translation.rb +3 -1
  225. data/lib/active_record/type/adapter_specific_registry.rb +44 -48
  226. data/lib/active_record/type/date.rb +2 -0
  227. data/lib/active_record/type/date_time.rb +2 -0
  228. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  229. data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
  230. data/lib/active_record/type/internal/timezone.rb +2 -0
  231. data/lib/active_record/type/json.rb +30 -0
  232. data/lib/active_record/type/serialized.rb +16 -9
  233. data/lib/active_record/type/text.rb +11 -0
  234. data/lib/active_record/type/time.rb +12 -1
  235. data/lib/active_record/type/type_map.rb +14 -17
  236. data/lib/active_record/type/unsigned_integer.rb +16 -0
  237. data/lib/active_record/type.rb +23 -18
  238. data/lib/active_record/type_caster/connection.rb +17 -12
  239. data/lib/active_record/type_caster/map.rb +5 -4
  240. data/lib/active_record/type_caster.rb +4 -2
  241. data/lib/active_record/validations/absence.rb +2 -0
  242. data/lib/active_record/validations/associated.rb +3 -2
  243. data/lib/active_record/validations/length.rb +2 -0
  244. data/lib/active_record/validations/presence.rb +4 -2
  245. data/lib/active_record/validations/uniqueness.rb +29 -42
  246. data/lib/active_record/validations.rb +7 -5
  247. data/lib/active_record/version.rb +3 -1
  248. data/lib/active_record.rb +37 -22
  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 +256 -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 +203 -0
  315. data/lib/arel/visitors/dot.rb +296 -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 +156 -0
  319. data/lib/arel/visitors/mysql.rb +83 -0
  320. data/lib/arel/visitors/oracle.rb +158 -0
  321. data/lib/arel/visitors/oracle12.rb +65 -0
  322. data/lib/arel/visitors/postgresql.rb +109 -0
  323. data/lib/arel/visitors/sqlite.rb +38 -0
  324. data/lib/arel/visitors/to_sql.rb +888 -0
  325. data/lib/arel/visitors/visitor.rb +45 -0
  326. data/lib/arel/visitors/where_sql.rb +22 -0
  327. data/lib/arel/visitors.rb +20 -0
  328. data/lib/arel/window_predications.rb +9 -0
  329. data/lib/arel.rb +62 -0
  330. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
  331. data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -35
  332. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +1 -1
  333. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +4 -2
  334. data/lib/rails/generators/active_record/migration.rb +17 -3
  335. data/lib/rails/generators/active_record/model/model_generator.rb +9 -30
  336. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +10 -1
  337. data/lib/rails/generators/active_record.rb +7 -5
  338. metadata +138 -52
  339. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  340. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  341. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  342. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  343. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  344. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  345. data/lib/active_record/associations/preloader/singular_association.rb +0 -20
  346. data/lib/active_record/attribute/user_provided_default.rb +0 -28
  347. data/lib/active_record/attribute.rb +0 -213
  348. data/lib/active_record/attribute_mutation_tracker.rb +0 -70
  349. data/lib/active_record/attribute_set/builder.rb +0 -132
  350. data/lib/active_record/attribute_set.rb +0 -110
  351. data/lib/active_record/collection_cache_key.rb +0 -50
  352. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +0 -50
  353. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  354. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  355. data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
  356. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -57
  357. data/lib/active_record/type/internal/abstract_json.rb +0 -33
  358. /data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  359. /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -1,12 +1,14 @@
1
- require 'active_support/core_ext/string/filters'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/string/filters"
2
4
 
3
5
  module ActiveRecord
4
6
  module FinderMethods
5
- ONE_AS_ONE = '1 AS one'
7
+ ONE_AS_ONE = "1 AS one"
6
8
 
7
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]).
8
- # If one or more records can not be found for the requested ids, then RecordNotFound will be raised. If the primary key
9
- # is an integer, find by id coerces its arguments using +to_i+.
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+.
10
12
  #
11
13
  # Person.find(1) # returns the object for ID = 1
12
14
  # Person.find("1") # returns the object for ID = 1
@@ -16,9 +18,10 @@ module ActiveRecord
16
18
  # Person.find([1]) # returns an array for the object with ID = 1
17
19
  # Person.where("administrator = 1").order("created_on DESC").find(1)
18
20
  #
19
- # NOTE: The returned records may not be in the same order as the ids you
20
- # provide since database rows are unordered. You will need to provide an explicit QueryMethods#order
21
- # option if you want the results to be sorted.
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.
22
25
  #
23
26
  # ==== Find with lock
24
27
  #
@@ -76,17 +79,12 @@ module ActiveRecord
76
79
  # Post.find_by "published_at < ?", 2.weeks.ago
77
80
  def find_by(arg, *args)
78
81
  where(arg, *args).take
79
- rescue RangeError
80
- nil
81
82
  end
82
83
 
83
84
  # Like #find_by, except that if no record is found, raises
84
85
  # an ActiveRecord::RecordNotFound error.
85
86
  def find_by!(arg, *args)
86
87
  where(arg, *args).take!
87
- rescue RangeError
88
- raise RecordNotFound.new("Couldn't find #{@klass.name} with an out of range value",
89
- @klass.name)
90
88
  end
91
89
 
92
90
  # Gives a record (or N records if a parameter is supplied) without any implied
@@ -97,13 +95,13 @@ module ActiveRecord
97
95
  # Person.take(5) # returns 5 objects fetched by SELECT * FROM people LIMIT 5
98
96
  # Person.where(["name LIKE '%?'", name]).take
99
97
  def take(limit = nil)
100
- limit ? limit(limit).to_a : find_take
98
+ limit ? find_take_with_limit(limit) : find_take
101
99
  end
102
100
 
103
101
  # Same as #take but raises ActiveRecord::RecordNotFound if no record
104
102
  # is found. Note that #take! accepts no arguments.
105
103
  def take!
106
- take or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]")
104
+ take || raise_record_not_found_exception!
107
105
  end
108
106
 
109
107
  # Find the first record (or first N records if a parameter is supplied).
@@ -117,7 +115,7 @@ module ActiveRecord
117
115
  #
118
116
  def first(limit = nil)
119
117
  if limit
120
- find_nth_with_limit_and_offset(0, limit, offset: offset_index)
118
+ find_nth_with_limit(0, limit)
121
119
  else
122
120
  find_nth 0
123
121
  end
@@ -126,7 +124,7 @@ module ActiveRecord
126
124
  # Same as #first but raises ActiveRecord::RecordNotFound if no record
127
125
  # is found. Note that #first! accepts no arguments.
128
126
  def first!
129
- find_nth! 0
127
+ first || raise_record_not_found_exception!
130
128
  end
131
129
 
132
130
  # Find the last record (or last N records if a parameter is supplied).
@@ -145,27 +143,18 @@ module ActiveRecord
145
143
  #
146
144
  # [#<Person id:4>, #<Person id:3>, #<Person id:2>]
147
145
  def last(limit = nil)
148
- return find_last(limit) if loaded? || limit_value
146
+ return find_last(limit) if loaded? || has_limit_or_offset?
149
147
 
150
- result = limit(limit || 1)
151
- result.order!(arel_attribute(primary_key)) if order_values.empty? && primary_key
148
+ result = ordered_relation.limit(limit)
152
149
  result = result.reverse_order!
153
150
 
154
151
  limit ? result.reverse : result.first
155
- rescue ActiveRecord::IrreversibleOrderError
156
- ActiveSupport::Deprecation.warn(<<-WARNING.squish)
157
- Finding a last element by loading the relation when SQL ORDER
158
- can not be reversed is deprecated.
159
- Rails 5.1 will raise ActiveRecord::IrreversibleOrderError in this case.
160
- Please call `to_a.last` if you still want to load the relation.
161
- WARNING
162
- find_last(limit)
163
152
  end
164
153
 
165
154
  # Same as #last but raises ActiveRecord::RecordNotFound if no record
166
155
  # is found. Note that #last! accepts no arguments.
167
156
  def last!
168
- last or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]")
157
+ last || raise_record_not_found_exception!
169
158
  end
170
159
 
171
160
  # Find the second record.
@@ -181,7 +170,7 @@ module ActiveRecord
181
170
  # Same as #second but raises ActiveRecord::RecordNotFound if no record
182
171
  # is found.
183
172
  def second!
184
- find_nth! 1
173
+ second || raise_record_not_found_exception!
185
174
  end
186
175
 
187
176
  # Find the third record.
@@ -197,7 +186,7 @@ module ActiveRecord
197
186
  # Same as #third but raises ActiveRecord::RecordNotFound if no record
198
187
  # is found.
199
188
  def third!
200
- find_nth! 2
189
+ third || raise_record_not_found_exception!
201
190
  end
202
191
 
203
192
  # Find the fourth record.
@@ -213,7 +202,7 @@ module ActiveRecord
213
202
  # Same as #fourth but raises ActiveRecord::RecordNotFound if no record
214
203
  # is found.
215
204
  def fourth!
216
- find_nth! 3
205
+ fourth || raise_record_not_found_exception!
217
206
  end
218
207
 
219
208
  # Find the fifth record.
@@ -229,7 +218,7 @@ module ActiveRecord
229
218
  # Same as #fifth but raises ActiveRecord::RecordNotFound if no record
230
219
  # is found.
231
220
  def fifth!
232
- find_nth! 4
221
+ fifth || raise_record_not_found_exception!
233
222
  end
234
223
 
235
224
  # Find the forty-second record. Also known as accessing "the reddit".
@@ -245,7 +234,7 @@ module ActiveRecord
245
234
  # Same as #forty_two but raises ActiveRecord::RecordNotFound if no record
246
235
  # is found.
247
236
  def forty_two!
248
- find_nth! 41
237
+ forty_two || raise_record_not_found_exception!
249
238
  end
250
239
 
251
240
  # Find the third-to-last record.
@@ -261,7 +250,7 @@ module ActiveRecord
261
250
  # Same as #third_to_last but raises ActiveRecord::RecordNotFound if no record
262
251
  # is found.
263
252
  def third_to_last!
264
- find_nth_from_last 3 or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]")
253
+ third_to_last || raise_record_not_found_exception!
265
254
  end
266
255
 
267
256
  # Find the second-to-last record.
@@ -277,7 +266,7 @@ module ActiveRecord
277
266
  # Same as #second_to_last but raises ActiveRecord::RecordNotFound if no record
278
267
  # is found.
279
268
  def second_to_last!
280
- find_nth_from_last 2 or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]")
269
+ second_to_last || raise_record_not_found_exception!
281
270
  end
282
271
 
283
272
  # Returns true if a record exists in the table that matches the +id+ or
@@ -291,7 +280,7 @@ module ActiveRecord
291
280
  # * Hash - Finds the record that matches these +find+-style conditions
292
281
  # (such as <tt>{name: 'David'}</tt>).
293
282
  # * +false+ - Returns always +false+.
294
- # * No args - Returns +false+ if the table is empty, +true+ otherwise.
283
+ # * No args - Returns +false+ if the relation is empty, +true+ otherwise.
295
284
  #
296
285
  # For more information about specifying conditions as a hash or array,
297
286
  # see the Conditions section in the introduction to ActiveRecord::Base.
@@ -307,32 +296,25 @@ module ActiveRecord
307
296
  # Person.exists?(name: 'David')
308
297
  # Person.exists?(false)
309
298
  # Person.exists?
299
+ # Person.where(name: 'Spartacus', rating: 4).exists?
310
300
  def exists?(conditions = :none)
311
301
  if Base === conditions
312
- conditions = conditions.id
313
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
302
+ raise ArgumentError, <<-MSG.squish
314
303
  You are passing an instance of ActiveRecord::Base to `exists?`.
315
304
  Please pass the id of the object by calling `.id`.
316
305
  MSG
317
306
  end
318
307
 
319
- return false if !conditions
320
-
321
- relation = apply_join_dependency(self, construct_join_dependency)
322
- return false if ActiveRecord::NullRelation === relation
323
-
324
- relation = relation.except(:select, :order).select(ONE_AS_ONE).limit(1)
308
+ return false if !conditions || limit_value == 0
325
309
 
326
- case conditions
327
- when Array, Hash
328
- relation = relation.where(conditions)
329
- else
330
- unless conditions == :none
331
- relation = relation.where(primary_key => conditions)
332
- end
310
+ if eager_loading?
311
+ relation = apply_join_dependency(eager_loading: false)
312
+ return relation.exists?(conditions)
333
313
  end
334
314
 
335
- connection.select_value(relation, "#{name} Exists", relation.bound_attributes) ? true : false
315
+ relation = construct_relation_for_exists(conditions)
316
+
317
+ skip_query_cache_if_necessary { connection.select_one(relation.arel, "#{name} Exists?") } ? true : false
336
318
  end
337
319
 
338
320
  # This method is called whenever no records are found with either a single
@@ -343,258 +325,236 @@ module ActiveRecord
343
325
  # of results obtained should be provided in the +result_size+ argument and
344
326
  # the expected number of results should be provided in the +expected_size+
345
327
  # argument.
346
- def raise_record_not_found_exception!(ids, result_size, expected_size, key = primary_key) #:nodoc:
347
- conditions = arel.where_sql(@klass.arel_engine)
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)
348
330
  conditions = " [#{conditions}]" if conditions
349
331
  name = @klass.name
350
332
 
351
- if Array(ids).size == 1
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
352
338
  error = "Couldn't find #{name} with '#{key}'=#{ids}#{conditions}"
353
339
  raise RecordNotFound.new(error, name, key, ids)
354
340
  else
355
- error = "Couldn't find all #{name.pluralize} with '#{key}': "
356
- error << "(#{ids.join(", ")})#{conditions} (found #{result_size} results, but was looking for #{expected_size})"
357
-
358
- raise RecordNotFound, error
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)
359
345
  end
360
346
  end
361
347
 
362
348
  private
349
+ def offset_index
350
+ offset_value || 0
351
+ end
363
352
 
364
- def offset_index
365
- offset_value || 0
366
- end
353
+ def construct_relation_for_exists(conditions)
354
+ conditions = sanitize_forbidden_attributes(conditions)
367
355
 
368
- def find_with_associations
369
- # NOTE: the JoinDependency constructed here needs to know about
370
- # any joins already present in `self`, so pass them in
371
- #
372
- # failing to do so means that in cases like activerecord/test/cases/associations/inner_join_association_test.rb:136
373
- # incorrect SQL is generated. In that case, the join dependency for
374
- # SpecialCategorizations is constructed without knowledge of the
375
- # preexisting join in joins_values to categorizations (by way of
376
- # the `has_many :through` for categories).
377
- #
378
- join_dependency = construct_join_dependency(joins_values)
379
-
380
- aliases = join_dependency.aliases
381
- relation = select aliases.columns
382
- relation = apply_join_dependency(relation, join_dependency)
383
-
384
- if block_given?
385
- yield relation
386
- else
387
- if ActiveRecord::NullRelation === relation
388
- []
356
+ if distinct_value && offset_value
357
+ relation = except(:order).limit!(1)
389
358
  else
390
- arel = relation.arel
391
- rows = connection.select_all(arel, 'SQL', relation.bound_attributes)
392
- join_dependency.instantiate(rows, aliases)
359
+ relation = except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
393
360
  end
394
- end
395
- end
396
361
 
397
- def construct_join_dependency(joins = [])
398
- including = eager_load_values + includes_values
399
- ActiveRecord::Associations::JoinDependency.new(@klass, including, joins)
400
- end
362
+ case conditions
363
+ when Array, Hash
364
+ relation.where!(conditions) unless conditions.empty?
365
+ else
366
+ relation.where!(primary_key => conditions) unless conditions == :none
367
+ end
401
368
 
402
- def construct_relation_for_association_calculations
403
- from = arel.froms.first
404
- if Arel::Table === from
405
- apply_join_dependency(self, construct_join_dependency(joins_values))
406
- else
407
- # FIXME: as far as I can tell, `from` will always be an Arel::Table.
408
- # There are no tests that test this branch, but presumably it's
409
- # possible for `from` to be a list?
410
- apply_join_dependency(self, construct_join_dependency(from))
369
+ relation
411
370
  end
412
- end
413
371
 
414
- def apply_join_dependency(relation, join_dependency)
415
- relation = relation.except(:includes, :eager_load, :preload)
416
- relation = relation.joins join_dependency
372
+ def apply_join_dependency(eager_loading: group_values.empty?)
373
+ join_dependency = construct_join_dependency(
374
+ eager_load_values | includes_values, Arel::Nodes::OuterJoin
375
+ )
376
+ relation = except(:includes, :eager_load, :preload).joins!(join_dependency)
377
+
378
+ if eager_loading && !(
379
+ using_limitable_reflections?(join_dependency.reflections) &&
380
+ using_limitable_reflections?(
381
+ construct_join_dependency(
382
+ select_association_list(joins_values).concat(
383
+ select_association_list(left_outer_joins_values)
384
+ ), nil
385
+ ).reflections
386
+ )
387
+ )
388
+ if has_limit_or_offset?
389
+ limited_ids = limited_ids_for(relation)
390
+ limited_ids.empty? ? relation.none! : relation.where!(primary_key => limited_ids)
391
+ end
392
+ relation.limit_value = relation.offset_value = nil
393
+ end
417
394
 
418
- if using_limitable_reflections?(join_dependency.reflections)
419
- relation
420
- else
421
- if relation.limit_value
422
- limited_ids = limited_ids_for(relation)
423
- limited_ids.empty? ? relation.none! : relation.where!(primary_key => limited_ids)
395
+ if block_given?
396
+ yield relation, join_dependency
397
+ else
398
+ relation
424
399
  end
425
- relation.except(:limit, :offset)
426
400
  end
427
- end
428
401
 
429
- def limited_ids_for(relation)
430
- values = @klass.connection.columns_for_distinct(
431
- "#{quoted_table_name}.#{quoted_primary_key}", relation.order_values)
402
+ def limited_ids_for(relation)
403
+ values = @klass.connection.columns_for_distinct(
404
+ connection.visitor.compile(arel_attribute(primary_key)),
405
+ relation.order_values
406
+ )
432
407
 
433
- relation = relation.except(:select).select(values).distinct!
434
- arel = relation.arel
408
+ relation = relation.except(:select).select(values).distinct!
435
409
 
436
- id_rows = @klass.connection.select_all(arel, 'SQL', relation.bound_attributes)
437
- id_rows.map {|row| row[primary_key]}
438
- end
410
+ id_rows = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, "SQL") }
411
+ id_rows.map { |row| row[primary_key] }
412
+ end
439
413
 
440
- def using_limitable_reflections?(reflections)
441
- reflections.none?(&:collection?)
442
- end
414
+ def using_limitable_reflections?(reflections)
415
+ reflections.none?(&:collection?)
416
+ end
443
417
 
444
- protected
418
+ def find_with_ids(*ids)
419
+ raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
445
420
 
446
- def find_with_ids(*ids)
447
- raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
421
+ expects_array = ids.first.kind_of?(Array)
422
+ return [] if expects_array && ids.first.empty?
448
423
 
449
- expects_array = ids.first.kind_of?(Array)
450
- return ids.first if expects_array && ids.first.empty?
424
+ ids = ids.flatten.compact.uniq
451
425
 
452
- ids = ids.flatten.compact.uniq
426
+ model_name = @klass.name
453
427
 
454
- case ids.size
455
- when 0
456
- raise RecordNotFound, "Couldn't find #{@klass.name} without an ID"
457
- when 1
458
- result = find_one(ids.first)
459
- expects_array ? [ result ] : result
460
- else
461
- find_some(ids)
428
+ case ids.size
429
+ when 0
430
+ error_message = "Couldn't find #{model_name} without an ID"
431
+ raise RecordNotFound.new(error_message, model_name, primary_key)
432
+ when 1
433
+ result = find_one(ids.first)
434
+ expects_array ? [ result ] : result
435
+ else
436
+ find_some(ids)
437
+ end
462
438
  end
463
- rescue RangeError
464
- raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID"
465
- end
466
439
 
467
- def find_one(id)
468
- if ActiveRecord::Base === id
469
- id = id.id
470
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
471
- You are passing an instance of ActiveRecord::Base to `find`.
472
- Please pass the id of the object by calling `.id`.
473
- MSG
474
- end
440
+ def find_one(id)
441
+ if ActiveRecord::Base === id
442
+ raise ArgumentError, <<-MSG.squish
443
+ You are passing an instance of ActiveRecord::Base to `find`.
444
+ Please pass the id of the object by calling `.id`.
445
+ MSG
446
+ end
475
447
 
476
- relation = where(primary_key => id)
477
- record = relation.take
448
+ relation = where(primary_key => id)
449
+ record = relation.take
478
450
 
479
- raise_record_not_found_exception!(id, 0, 1) unless record
451
+ raise_record_not_found_exception!(id, 0, 1) unless record
480
452
 
481
- record
482
- end
453
+ record
454
+ end
483
455
 
484
- def find_some(ids)
485
- return find_some_ordered(ids) unless order_values.present?
456
+ def find_some(ids)
457
+ return find_some_ordered(ids) unless order_values.present?
486
458
 
487
- result = where(primary_key => ids).to_a
459
+ result = where(primary_key => ids).to_a
488
460
 
489
- expected_size =
490
- if limit_value && ids.size > limit_value
491
- limit_value
492
- else
493
- ids.size
494
- end
461
+ expected_size =
462
+ if limit_value && ids.size > limit_value
463
+ limit_value
464
+ else
465
+ ids.size
466
+ end
495
467
 
496
- # 11 ids with limit 3, offset 9 should give 2 results.
497
- if offset_value && (ids.size - offset_value < expected_size)
498
- expected_size = ids.size - offset_value
499
- end
468
+ # 11 ids with limit 3, offset 9 should give 2 results.
469
+ if offset_value && (ids.size - offset_value < expected_size)
470
+ expected_size = ids.size - offset_value
471
+ end
500
472
 
501
- if result.size == expected_size
502
- result
503
- else
504
- raise_record_not_found_exception!(ids, result.size, expected_size)
473
+ if result.size == expected_size
474
+ result
475
+ else
476
+ raise_record_not_found_exception!(ids, result.size, expected_size)
477
+ end
505
478
  end
506
- end
507
479
 
508
- def find_some_ordered(ids)
509
- ids = ids.slice(offset_value || 0, limit_value || ids.size) || []
480
+ def find_some_ordered(ids)
481
+ ids = ids.slice(offset_value || 0, limit_value || ids.size) || []
510
482
 
511
- result = except(:limit, :offset).where(primary_key => ids).records
483
+ result = except(:limit, :offset).where(primary_key => ids).records
512
484
 
513
- if result.size == ids.size
514
- pk_type = @klass.type_for_attribute(primary_key)
485
+ if result.size == ids.size
486
+ pk_type = @klass.type_for_attribute(primary_key)
515
487
 
516
- records_by_id = result.index_by(&:id)
517
- ids.map { |id| records_by_id.fetch(pk_type.cast(id)) }
518
- else
519
- raise_record_not_found_exception!(ids, result.size, ids.size)
488
+ records_by_id = result.index_by(&:id)
489
+ ids.map { |id| records_by_id.fetch(pk_type.cast(id)) }
490
+ else
491
+ raise_record_not_found_exception!(ids, result.size, ids.size)
492
+ end
520
493
  end
521
- end
522
494
 
523
- def find_take
524
- if loaded?
525
- records.first
526
- else
527
- @take ||= limit(1).records.first
495
+ def find_take
496
+ if loaded?
497
+ records.first
498
+ else
499
+ @take ||= limit(1).records.first
500
+ end
528
501
  end
529
- end
530
502
 
531
- def find_nth(index, offset = nil)
532
- # TODO: once the offset argument is removed we rely on offset_index
533
- # within find_nth_with_limit, rather than pass it in via
534
- # find_nth_with_limit_and_offset
535
- if offset
536
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
537
- Passing an offset argument to find_nth is deprecated,
538
- please use Relation#offset instead.
539
- MSG
503
+ def find_take_with_limit(limit)
504
+ if loaded?
505
+ records.take(limit)
506
+ else
507
+ limit(limit).to_a
508
+ end
540
509
  end
541
- if loaded?
542
- records[index]
543
- else
544
- offset ||= offset_index
545
- @offsets[offset + index] ||= find_nth_with_limit_and_offset(index, 1, offset: offset).first
510
+
511
+ def find_nth(index)
512
+ @offsets[offset_index + index] ||= find_nth_with_limit(index, 1).first
546
513
  end
547
- end
548
514
 
549
- def find_nth!(index)
550
- find_nth(index) or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]")
551
- end
515
+ def find_nth_with_limit(index, limit)
516
+ if loaded?
517
+ records[index, limit] || []
518
+ else
519
+ relation = ordered_relation
520
+
521
+ if limit_value
522
+ limit = [limit_value - index, limit].min
523
+ end
524
+
525
+ if limit > 0
526
+ relation = relation.offset(offset_index + index) unless index.zero?
527
+ relation.limit(limit).to_a
528
+ else
529
+ []
530
+ end
531
+ end
532
+ end
552
533
 
553
- def find_nth_with_limit(index, limit)
554
- # TODO: once the offset argument is removed from find_nth,
555
- # find_nth_with_limit_and_offset can be merged into this method
556
- relation = if order_values.empty? && primary_key
557
- order(arel_attribute(primary_key).asc)
558
- else
559
- self
560
- end
561
-
562
- relation = relation.offset(index) unless index.zero?
563
- relation.limit(limit).to_a
564
- end
534
+ def find_nth_from_last(index)
535
+ if loaded?
536
+ records[-index]
537
+ else
538
+ relation = ordered_relation
565
539
 
566
- def find_nth_from_last(index)
567
- if loaded?
568
- records[-index]
569
- else
570
- relation = if order_values.empty? && primary_key
571
- order(arel_attribute(primary_key).asc)
572
- else
573
- self
574
- end
575
-
576
- relation.to_a[-index]
577
- # TODO: can be made more performant on large result sets by
578
- # for instance, last(index)[-index] (which would require
579
- # refactoring the last(n) finder method to make test suite pass),
580
- # or by using a combination of reverse_order, limit, and offset,
581
- # e.g., reverse_order.offset(index-1).first
540
+ if equal?(relation) || has_limit_or_offset?
541
+ relation.records[-index]
542
+ else
543
+ relation.last(index)[-index]
544
+ end
545
+ end
582
546
  end
583
- end
584
-
585
- private
586
547
 
587
- def find_nth_with_limit_and_offset(index, limit, offset:) # :nodoc:
588
- if loaded?
589
- records[index, limit]
590
- else
591
- index += offset
592
- find_nth_with_limit(index, limit)
548
+ def find_last(limit)
549
+ limit ? records.last(limit) : records.last
593
550
  end
594
- end
595
551
 
596
- def find_last(limit)
597
- limit ? records.last(limit) : records.last
598
- end
552
+ def ordered_relation
553
+ if order_values.empty? && (implicit_order_column || primary_key)
554
+ order(arel_attribute(implicit_order_column || primary_key).asc)
555
+ else
556
+ self
557
+ end
558
+ end
599
559
  end
600
560
  end