activerecord 4.2.0 → 5.2.8.1

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