activerecord 4.2.11.3 → 6.0.0

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

Potentially problematic release.


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

Files changed (372) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +613 -1643
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +13 -12
  5. data/examples/performance.rb +33 -32
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record.rb +41 -22
  8. data/lib/active_record/aggregations.rb +267 -251
  9. data/lib/active_record/association_relation.rb +11 -6
  10. data/lib/active_record/associations.rb +1737 -1597
  11. data/lib/active_record/associations/alias_tracker.rb +29 -35
  12. data/lib/active_record/associations/association.rb +125 -58
  13. data/lib/active_record/associations/association_scope.rb +103 -132
  14. data/lib/active_record/associations/belongs_to_association.rb +65 -60
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
  16. data/lib/active_record/associations/builder/association.rb +27 -40
  17. data/lib/active_record/associations/builder/belongs_to.rb +69 -55
  18. data/lib/active_record/associations/builder/collection_association.rb +10 -33
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +52 -66
  20. data/lib/active_record/associations/builder/has_many.rb +8 -4
  21. data/lib/active_record/associations/builder/has_one.rb +46 -5
  22. data/lib/active_record/associations/builder/singular_association.rb +16 -10
  23. data/lib/active_record/associations/collection_association.rb +131 -287
  24. data/lib/active_record/associations/collection_proxy.rb +241 -146
  25. data/lib/active_record/associations/foreign_association.rb +10 -1
  26. data/lib/active_record/associations/has_many_association.rb +34 -97
  27. data/lib/active_record/associations/has_many_through_association.rb +60 -87
  28. data/lib/active_record/associations/has_one_association.rb +61 -49
  29. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  30. data/lib/active_record/associations/join_dependency.rb +137 -167
  31. data/lib/active_record/associations/join_dependency/join_association.rb +38 -86
  32. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  33. data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
  34. data/lib/active_record/associations/preloader.rb +90 -92
  35. data/lib/active_record/associations/preloader/association.rb +90 -123
  36. data/lib/active_record/associations/preloader/through_association.rb +85 -65
  37. data/lib/active_record/associations/singular_association.rb +18 -39
  38. data/lib/active_record/associations/through_association.rb +38 -18
  39. data/lib/active_record/attribute_assignment.rb +56 -183
  40. data/lib/active_record/attribute_decorators.rb +39 -15
  41. data/lib/active_record/attribute_methods.rb +120 -135
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -8
  43. data/lib/active_record/attribute_methods/dirty.rb +174 -144
  44. data/lib/active_record/attribute_methods/primary_key.rb +91 -83
  45. data/lib/active_record/attribute_methods/query.rb +6 -5
  46. data/lib/active_record/attribute_methods/read.rb +20 -76
  47. data/lib/active_record/attribute_methods/serialization.rb +40 -20
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +58 -36
  49. data/lib/active_record/attribute_methods/write.rb +32 -54
  50. data/lib/active_record/attributes.rb +214 -82
  51. data/lib/active_record/autosave_association.rb +91 -37
  52. data/lib/active_record/base.rb +57 -45
  53. data/lib/active_record/callbacks.rb +100 -74
  54. data/lib/active_record/coders/json.rb +3 -1
  55. data/lib/active_record/coders/yaml_column.rb +24 -12
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +796 -296
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +26 -8
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +234 -115
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +82 -23
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +170 -53
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +74 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +356 -227
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +664 -243
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +191 -83
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +460 -204
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +510 -635
  69. data/lib/active_record/connection_adapters/column.rb +56 -43
  70. data/lib/active_record/connection_adapters/connection_specification.rb +174 -152
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +200 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +72 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +58 -180
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +21 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +64 -114
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -58
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -22
  93. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  94. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
  95. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
  96. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  98. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  100. data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
  101. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
  102. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
  103. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
  104. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  105. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +10 -5
  106. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +144 -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 +76 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +470 -290
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +551 -356
  117. data/lib/active_record/connection_adapters/schema_cache.rb +72 -25
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
  119. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  120. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  121. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +103 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
  126. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +290 -345
  127. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  128. data/lib/active_record/connection_handling.rb +176 -41
  129. data/lib/active_record/core.rb +251 -231
  130. data/lib/active_record/counter_cache.rb +67 -49
  131. data/lib/active_record/database_configurations.rb +233 -0
  132. data/lib/active_record/database_configurations/database_config.rb +37 -0
  133. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  134. data/lib/active_record/database_configurations/url_config.rb +79 -0
  135. data/lib/active_record/define_callbacks.rb +22 -0
  136. data/lib/active_record/dynamic_matchers.rb +87 -105
  137. data/lib/active_record/enum.rb +163 -86
  138. data/lib/active_record/errors.rb +188 -53
  139. data/lib/active_record/explain.rb +23 -11
  140. data/lib/active_record/explain_registry.rb +4 -2
  141. data/lib/active_record/explain_subscriber.rb +10 -5
  142. data/lib/active_record/fixture_set/file.rb +35 -9
  143. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  144. data/lib/active_record/fixture_set/render_context.rb +17 -0
  145. data/lib/active_record/fixture_set/table_row.rb +153 -0
  146. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  147. data/lib/active_record/fixtures.rb +228 -499
  148. data/lib/active_record/gem_version.rb +6 -4
  149. data/lib/active_record/inheritance.rb +158 -112
  150. data/lib/active_record/insert_all.rb +179 -0
  151. data/lib/active_record/integration.rb +123 -29
  152. data/lib/active_record/internal_metadata.rb +53 -0
  153. data/lib/active_record/legacy_yaml_adapter.rb +21 -3
  154. data/lib/active_record/locale/en.yml +3 -2
  155. data/lib/active_record/locking/optimistic.rb +87 -96
  156. data/lib/active_record/locking/pessimistic.rb +18 -6
  157. data/lib/active_record/log_subscriber.rb +76 -33
  158. data/lib/active_record/middleware/database_selector.rb +75 -0
  159. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  160. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  161. data/lib/active_record/migration.rb +621 -303
  162. data/lib/active_record/migration/command_recorder.rb +177 -90
  163. data/lib/active_record/migration/compatibility.rb +244 -0
  164. data/lib/active_record/migration/join_table.rb +8 -6
  165. data/lib/active_record/model_schema.rb +312 -112
  166. data/lib/active_record/nested_attributes.rb +264 -222
  167. data/lib/active_record/no_touching.rb +14 -1
  168. data/lib/active_record/null_relation.rb +24 -37
  169. data/lib/active_record/persistence.rb +557 -125
  170. data/lib/active_record/query_cache.rb +19 -23
  171. data/lib/active_record/querying.rb +43 -29
  172. data/lib/active_record/railtie.rb +143 -44
  173. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  174. data/lib/active_record/railties/console_sandbox.rb +2 -0
  175. data/lib/active_record/railties/controller_runtime.rb +34 -33
  176. data/lib/active_record/railties/databases.rake +328 -185
  177. data/lib/active_record/readonly_attributes.rb +5 -4
  178. data/lib/active_record/reflection.rb +428 -279
  179. data/lib/active_record/relation.rb +518 -341
  180. data/lib/active_record/relation/batches.rb +207 -55
  181. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  182. data/lib/active_record/relation/calculations.rb +267 -253
  183. data/lib/active_record/relation/delegation.rb +70 -80
  184. data/lib/active_record/relation/finder_methods.rb +277 -241
  185. data/lib/active_record/relation/from_clause.rb +26 -0
  186. data/lib/active_record/relation/merger.rb +78 -87
  187. data/lib/active_record/relation/predicate_builder.rb +114 -119
  188. data/lib/active_record/relation/predicate_builder/array_handler.rb +27 -26
  189. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  190. data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
  191. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  192. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  193. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  194. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  195. data/lib/active_record/relation/query_attribute.rb +50 -0
  196. data/lib/active_record/relation/query_methods.rb +575 -394
  197. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  198. data/lib/active_record/relation/spawn_methods.rb +11 -13
  199. data/lib/active_record/relation/where_clause.rb +190 -0
  200. data/lib/active_record/relation/where_clause_factory.rb +33 -0
  201. data/lib/active_record/result.rb +79 -42
  202. data/lib/active_record/runtime_registry.rb +6 -4
  203. data/lib/active_record/sanitization.rb +144 -121
  204. data/lib/active_record/schema.rb +21 -24
  205. data/lib/active_record/schema_dumper.rb +112 -93
  206. data/lib/active_record/schema_migration.rb +24 -17
  207. data/lib/active_record/scoping.rb +45 -26
  208. data/lib/active_record/scoping/default.rb +101 -85
  209. data/lib/active_record/scoping/named.rb +86 -33
  210. data/lib/active_record/secure_token.rb +40 -0
  211. data/lib/active_record/serialization.rb +5 -5
  212. data/lib/active_record/statement_cache.rb +73 -36
  213. data/lib/active_record/store.rb +127 -42
  214. data/lib/active_record/suppressor.rb +61 -0
  215. data/lib/active_record/table_metadata.rb +75 -0
  216. data/lib/active_record/tasks/database_tasks.rb +307 -100
  217. data/lib/active_record/tasks/mysql_database_tasks.rb +55 -99
  218. data/lib/active_record/tasks/postgresql_database_tasks.rb +81 -41
  219. data/lib/active_record/tasks/sqlite_database_tasks.rb +38 -16
  220. data/lib/active_record/test_databases.rb +23 -0
  221. data/lib/active_record/test_fixtures.rb +224 -0
  222. data/lib/active_record/timestamp.rb +86 -40
  223. data/lib/active_record/touch_later.rb +66 -0
  224. data/lib/active_record/transactions.rb +216 -150
  225. data/lib/active_record/translation.rb +3 -1
  226. data/lib/active_record/type.rb +78 -23
  227. data/lib/active_record/type/adapter_specific_registry.rb +129 -0
  228. data/lib/active_record/type/date.rb +4 -45
  229. data/lib/active_record/type/date_time.rb +4 -49
  230. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  231. data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
  232. data/lib/active_record/type/internal/timezone.rb +17 -0
  233. data/lib/active_record/type/json.rb +30 -0
  234. data/lib/active_record/type/serialized.rb +24 -15
  235. data/lib/active_record/type/text.rb +2 -2
  236. data/lib/active_record/type/time.rb +11 -16
  237. data/lib/active_record/type/type_map.rb +15 -17
  238. data/lib/active_record/type/unsigned_integer.rb +9 -7
  239. data/lib/active_record/type_caster.rb +9 -0
  240. data/lib/active_record/type_caster/connection.rb +34 -0
  241. data/lib/active_record/type_caster/map.rb +20 -0
  242. data/lib/active_record/validations.rb +39 -35
  243. data/lib/active_record/validations/absence.rb +25 -0
  244. data/lib/active_record/validations/associated.rb +13 -4
  245. data/lib/active_record/validations/length.rb +26 -0
  246. data/lib/active_record/validations/presence.rb +14 -13
  247. data/lib/active_record/validations/uniqueness.rb +42 -55
  248. data/lib/active_record/version.rb +3 -1
  249. data/lib/arel.rb +51 -0
  250. data/lib/arel/alias_predication.rb +9 -0
  251. data/lib/arel/attributes.rb +22 -0
  252. data/lib/arel/attributes/attribute.rb +37 -0
  253. data/lib/arel/collectors/bind.rb +24 -0
  254. data/lib/arel/collectors/composite.rb +31 -0
  255. data/lib/arel/collectors/plain_string.rb +20 -0
  256. data/lib/arel/collectors/sql_string.rb +20 -0
  257. data/lib/arel/collectors/substitute_binds.rb +28 -0
  258. data/lib/arel/crud.rb +42 -0
  259. data/lib/arel/delete_manager.rb +18 -0
  260. data/lib/arel/errors.rb +9 -0
  261. data/lib/arel/expressions.rb +29 -0
  262. data/lib/arel/factory_methods.rb +49 -0
  263. data/lib/arel/insert_manager.rb +49 -0
  264. data/lib/arel/math.rb +45 -0
  265. data/lib/arel/nodes.rb +68 -0
  266. data/lib/arel/nodes/and.rb +32 -0
  267. data/lib/arel/nodes/ascending.rb +23 -0
  268. data/lib/arel/nodes/binary.rb +52 -0
  269. data/lib/arel/nodes/bind_param.rb +36 -0
  270. data/lib/arel/nodes/case.rb +55 -0
  271. data/lib/arel/nodes/casted.rb +50 -0
  272. data/lib/arel/nodes/comment.rb +29 -0
  273. data/lib/arel/nodes/count.rb +12 -0
  274. data/lib/arel/nodes/delete_statement.rb +45 -0
  275. data/lib/arel/nodes/descending.rb +23 -0
  276. data/lib/arel/nodes/equality.rb +18 -0
  277. data/lib/arel/nodes/extract.rb +24 -0
  278. data/lib/arel/nodes/false.rb +16 -0
  279. data/lib/arel/nodes/full_outer_join.rb +8 -0
  280. data/lib/arel/nodes/function.rb +44 -0
  281. data/lib/arel/nodes/grouping.rb +8 -0
  282. data/lib/arel/nodes/in.rb +8 -0
  283. data/lib/arel/nodes/infix_operation.rb +80 -0
  284. data/lib/arel/nodes/inner_join.rb +8 -0
  285. data/lib/arel/nodes/insert_statement.rb +37 -0
  286. data/lib/arel/nodes/join_source.rb +20 -0
  287. data/lib/arel/nodes/matches.rb +18 -0
  288. data/lib/arel/nodes/named_function.rb +23 -0
  289. data/lib/arel/nodes/node.rb +50 -0
  290. data/lib/arel/nodes/node_expression.rb +13 -0
  291. data/lib/arel/nodes/outer_join.rb +8 -0
  292. data/lib/arel/nodes/over.rb +15 -0
  293. data/lib/arel/nodes/regexp.rb +16 -0
  294. data/lib/arel/nodes/right_outer_join.rb +8 -0
  295. data/lib/arel/nodes/select_core.rb +67 -0
  296. data/lib/arel/nodes/select_statement.rb +41 -0
  297. data/lib/arel/nodes/sql_literal.rb +16 -0
  298. data/lib/arel/nodes/string_join.rb +11 -0
  299. data/lib/arel/nodes/table_alias.rb +27 -0
  300. data/lib/arel/nodes/terminal.rb +16 -0
  301. data/lib/arel/nodes/true.rb +16 -0
  302. data/lib/arel/nodes/unary.rb +45 -0
  303. data/lib/arel/nodes/unary_operation.rb +20 -0
  304. data/lib/arel/nodes/unqualified_column.rb +22 -0
  305. data/lib/arel/nodes/update_statement.rb +41 -0
  306. data/lib/arel/nodes/values_list.rb +9 -0
  307. data/lib/arel/nodes/window.rb +126 -0
  308. data/lib/arel/nodes/with.rb +11 -0
  309. data/lib/arel/order_predications.rb +13 -0
  310. data/lib/arel/predications.rb +257 -0
  311. data/lib/arel/select_manager.rb +271 -0
  312. data/lib/arel/table.rb +110 -0
  313. data/lib/arel/tree_manager.rb +72 -0
  314. data/lib/arel/update_manager.rb +34 -0
  315. data/lib/arel/visitors.rb +20 -0
  316. data/lib/arel/visitors/depth_first.rb +204 -0
  317. data/lib/arel/visitors/dot.rb +297 -0
  318. data/lib/arel/visitors/ibm_db.rb +34 -0
  319. data/lib/arel/visitors/informix.rb +62 -0
  320. data/lib/arel/visitors/mssql.rb +157 -0
  321. data/lib/arel/visitors/mysql.rb +83 -0
  322. data/lib/arel/visitors/oracle.rb +159 -0
  323. data/lib/arel/visitors/oracle12.rb +66 -0
  324. data/lib/arel/visitors/postgresql.rb +110 -0
  325. data/lib/arel/visitors/sqlite.rb +39 -0
  326. data/lib/arel/visitors/to_sql.rb +889 -0
  327. data/lib/arel/visitors/visitor.rb +46 -0
  328. data/lib/arel/visitors/where_sql.rb +23 -0
  329. data/lib/arel/window_predications.rb +9 -0
  330. data/lib/rails/generators/active_record.rb +7 -5
  331. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  332. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  333. data/lib/rails/generators/active_record/migration.rb +31 -1
  334. data/lib/rails/generators/active_record/migration/migration_generator.rb +42 -37
  335. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  336. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +11 -2
  337. data/lib/rails/generators/active_record/model/model_generator.rb +19 -22
  338. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  339. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  340. metadata +164 -59
  341. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  342. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  343. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  344. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  345. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  346. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  347. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  348. data/lib/active_record/attribute.rb +0 -163
  349. data/lib/active_record/attribute_set.rb +0 -81
  350. data/lib/active_record/attribute_set/builder.rb +0 -106
  351. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  352. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  353. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  354. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  355. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  356. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  357. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  358. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  359. data/lib/active_record/type/big_integer.rb +0 -13
  360. data/lib/active_record/type/binary.rb +0 -50
  361. data/lib/active_record/type/boolean.rb +0 -31
  362. data/lib/active_record/type/decimal.rb +0 -64
  363. data/lib/active_record/type/decorator.rb +0 -14
  364. data/lib/active_record/type/float.rb +0 -19
  365. data/lib/active_record/type/integer.rb +0 -59
  366. data/lib/active_record/type/mutable.rb +0 -16
  367. data/lib/active_record/type/numeric.rb +0 -36
  368. data/lib/active_record/type/string.rb +0 -40
  369. data/lib/active_record/type/time_value.rb +0 -38
  370. data/lib/active_record/type/value.rb +0 -110
  371. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -19
  372. data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
@@ -1,15 +1,15 @@
1
- require 'set'
2
- require 'active_support/concern'
3
- require 'active_support/deprecation'
1
+ # frozen_string_literal: true
2
+
3
+ require "mutex_m"
4
4
 
5
5
  module ActiveRecord
6
6
  module Delegation # :nodoc:
7
- module DelegateCache
8
- def relation_delegate_class(klass) # :nodoc:
7
+ module DelegateCache # :nodoc:
8
+ def relation_delegate_class(klass)
9
9
  @relation_delegate_cache[klass]
10
10
  end
11
11
 
12
- def initialize_relation_delegate_cache # :nodoc:
12
+ def initialize_relation_delegate_cache
13
13
  @relation_delegate_cache = cache = {}
14
14
  [
15
15
  ActiveRecord::Relation,
@@ -19,7 +19,11 @@ module ActiveRecord
19
19
  delegate = Class.new(klass) {
20
20
  include ClassSpecificRelation
21
21
  }
22
- const_set klass.name.gsub('::', '_'), delegate
22
+ include_relation_methods(delegate)
23
+ mangled_name = klass.name.gsub("::", "_")
24
+ const_set mangled_name, delegate
25
+ private_constant mangled_name
26
+
23
27
  cache[klass] = delegate
24
28
  end
25
29
  end
@@ -28,7 +32,48 @@ module ActiveRecord
28
32
  child_class.initialize_relation_delegate_cache
29
33
  super
30
34
  end
35
+
36
+ def generate_relation_method(method)
37
+ generated_relation_methods.generate_method(method)
38
+ end
39
+
40
+ protected
41
+ def include_relation_methods(delegate)
42
+ superclass.include_relation_methods(delegate) unless base_class?
43
+ delegate.include generated_relation_methods
44
+ end
45
+
46
+ private
47
+ def generated_relation_methods
48
+ @generated_relation_methods ||= GeneratedRelationMethods.new.tap do |mod|
49
+ const_set(:GeneratedRelationMethods, mod)
50
+ private_constant :GeneratedRelationMethods
51
+ end
52
+ end
53
+ end
54
+
55
+ class GeneratedRelationMethods < Module # :nodoc:
56
+ include Mutex_m
57
+
58
+ def generate_method(method)
59
+ synchronize do
60
+ return if method_defined?(method)
61
+
62
+ if /\A[a-zA-Z_]\w*[!?]?\z/.match?(method)
63
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
64
+ def #{method}(*args, &block)
65
+ scoping { klass.#{method}(*args, &block) }
66
+ end
67
+ RUBY
68
+ else
69
+ define_method(method) do |*args, &block|
70
+ scoping { klass.public_send(method, *args, &block) }
71
+ end
72
+ end
73
+ end
74
+ end
31
75
  end
76
+ private_constant :GeneratedRelationMethods
32
77
 
33
78
  extend ActiveSupport::Concern
34
79
 
@@ -37,68 +82,32 @@ module ActiveRecord
37
82
  # may vary depending on the klass of a relation, so we create a subclass of Relation
38
83
  # for each different klass, and the delegations are compiled into that subclass only.
39
84
 
40
- BLACKLISTED_ARRAY_METHODS = [
41
- :compact!, :flatten!, :reject!, :reverse!, :rotate!, :map!,
42
- :shuffle!, :slice!, :sort!, :sort_by!, :delete_if,
43
- :keep_if, :pop, :shift, :delete_at, :select!
44
- ].to_set # :nodoc:
85
+ delegate :to_xml, :encode_with, :length, :each, :join,
86
+ :[], :&, :|, :+, :-, :sample, :reverse, :rotate, :compact, :in_groups, :in_groups_of,
87
+ :to_sentence, :to_formatted_s, :as_json,
88
+ :shuffle, :split, :slice, :index, :rindex, to: :records
45
89
 
46
- delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :join, to: :to_a
47
-
48
- delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
49
- :connection, :columns_hash, :to => :klass
90
+ delegate :primary_key, :connection, to: :klass
50
91
 
51
92
  module ClassSpecificRelation # :nodoc:
52
93
  extend ActiveSupport::Concern
53
94
 
54
- included do
55
- @delegation_mutex = Mutex.new
56
- end
57
-
58
95
  module ClassMethods # :nodoc:
59
96
  def name
60
97
  superclass.name
61
98
  end
99
+ end
62
100
 
63
- def delegate_to_scoped_klass(method)
64
- @delegation_mutex.synchronize do
65
- return if method_defined?(method)
66
-
67
- if method.to_s =~ /\A[a-zA-Z_]\w*[!?]?\z/
68
- module_eval <<-RUBY, __FILE__, __LINE__ + 1
69
- def #{method}(*args, &block)
70
- scoping { @klass.#{method}(*args, &block) }
71
- end
72
- RUBY
73
- else
74
- define_method method do |*args, &block|
75
- scoping { @klass.public_send(method, *args, &block) }
76
- end
77
- end
78
- end
79
- end
101
+ private
80
102
 
81
- def delegate(method, opts = {})
82
- @delegation_mutex.synchronize do
83
- return if method_defined?(method)
103
+ def method_missing(method, *args, &block)
104
+ if @klass.respond_to?(method)
105
+ @klass.generate_relation_method(method)
106
+ scoping { @klass.public_send(method, *args, &block) }
107
+ else
84
108
  super
85
109
  end
86
110
  end
87
- end
88
-
89
- protected
90
-
91
- def method_missing(method, *args, &block)
92
- if @klass.respond_to?(method)
93
- self.class.delegate_to_scoped_klass(method)
94
- scoping { @klass.public_send(method, *args, &block) }
95
- elsif arel.respond_to?(method)
96
- self.class.delegate method, :to => :arel
97
- arel.public_send(method, *args, &block)
98
- else
99
- super
100
- end
101
- end
102
111
  end
103
112
 
104
113
  module ClassMethods # :nodoc:
@@ -108,33 +117,14 @@ module ActiveRecord
108
117
 
109
118
  private
110
119
 
111
- def relation_class_for(klass)
112
- klass.relation_delegate_class(self)
113
- end
114
- end
115
-
116
- def respond_to?(method, include_private = false)
117
- super || @klass.respond_to?(method, include_private) ||
118
- array_delegable?(method) ||
119
- arel.respond_to?(method, include_private)
120
- end
121
-
122
- protected
123
-
124
- def array_delegable?(method)
125
- Array.method_defined?(method) && BLACKLISTED_ARRAY_METHODS.exclude?(method)
120
+ def relation_class_for(klass)
121
+ klass.relation_delegate_class(self)
122
+ end
126
123
  end
127
124
 
128
- def method_missing(method, *args, &block)
129
- if @klass.respond_to?(method)
130
- scoping { @klass.public_send(method, *args, &block) }
131
- elsif array_delegable?(method)
132
- to_a.public_send(method, *args, &block)
133
- elsif arel.respond_to?(method)
134
- arel.public_send(method, *args, &block)
135
- else
136
- super
125
+ private
126
+ def respond_to_missing?(method, _)
127
+ super || @klass.respond_to?(method)
137
128
  end
138
- end
139
129
  end
140
130
  end
@@ -1,13 +1,14 @@
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
- # 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+.
11
12
  #
12
13
  # Person.find(1) # returns the object for ID = 1
13
14
  # 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,14 @@ 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
86
- nil
80
+ def find_by(arg, *args)
81
+ where(arg, *args).take
87
82
  end
88
83
 
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"
84
+ # Like #find_by, except that if no record is found, raises
85
+ # an ActiveRecord::RecordNotFound error.
86
+ def find_by!(arg, *args)
87
+ where(arg, *args).take!
95
88
  end
96
89
 
97
90
  # Gives a record (or N records if a parameter is supplied) without any implied
@@ -102,13 +95,13 @@ module ActiveRecord
102
95
  # Person.take(5) # returns 5 objects fetched by SELECT * FROM people LIMIT 5
103
96
  # Person.where(["name LIKE '%?'", name]).take
104
97
  def take(limit = nil)
105
- limit ? limit(limit).to_a : find_take
98
+ limit ? find_take_with_limit(limit) : find_take
106
99
  end
107
100
 
108
- # Same as +take+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
109
- # is found. Note that <tt>take!</tt> accepts no arguments.
101
+ # Same as #take but raises ActiveRecord::RecordNotFound if no record
102
+ # is found. Note that #take! accepts no arguments.
110
103
  def take!
111
- take or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
104
+ take || raise_record_not_found_exception!
112
105
  end
113
106
 
114
107
  # Find the first record (or first N records if a parameter is supplied).
@@ -122,16 +115,16 @@ module ActiveRecord
122
115
  #
123
116
  def first(limit = nil)
124
117
  if limit
125
- find_nth_with_limit(offset_index, limit)
118
+ find_nth_with_limit(0, limit)
126
119
  else
127
- find_nth(0, offset_index)
120
+ find_nth 0
128
121
  end
129
122
  end
130
123
 
131
- # Same as +first+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
132
- # is found. Note that <tt>first!</tt> accepts no arguments.
124
+ # Same as #first but raises ActiveRecord::RecordNotFound if no record
125
+ # is found. Note that #first! accepts no arguments.
133
126
  def first!
134
- find_nth! 0
127
+ first || raise_record_not_found_exception!
135
128
  end
136
129
 
137
130
  # Find the last record (or last N records if a parameter is supplied).
@@ -150,21 +143,18 @@ module ActiveRecord
150
143
  #
151
144
  # [#<Person id:4>, #<Person id:3>, #<Person id:2>]
152
145
  def last(limit = nil)
153
- if limit
154
- if order_values.empty? && primary_key
155
- order(arel_table[primary_key].desc).limit(limit).reverse
156
- else
157
- to_a.last(limit)
158
- end
159
- else
160
- find_last
161
- end
146
+ return find_last(limit) if loaded? || has_limit_or_offset?
147
+
148
+ result = ordered_relation.limit(limit)
149
+ result = result.reverse_order!
150
+
151
+ limit ? result.reverse : result.first
162
152
  end
163
153
 
164
- # Same as +last+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
165
- # is found. Note that <tt>last!</tt> accepts no arguments.
154
+ # Same as #last but raises ActiveRecord::RecordNotFound if no record
155
+ # is found. Note that #last! accepts no arguments.
166
156
  def last!
167
- last or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
157
+ last || raise_record_not_found_exception!
168
158
  end
169
159
 
170
160
  # Find the second record.
@@ -174,13 +164,13 @@ module ActiveRecord
174
164
  # Person.offset(3).second # returns the second object from OFFSET 3 (which is OFFSET 4)
175
165
  # Person.where(["user_name = :u", { u: user_name }]).second
176
166
  def second
177
- find_nth(1, offset_index)
167
+ find_nth 1
178
168
  end
179
169
 
180
- # Same as +second+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
170
+ # Same as #second but raises ActiveRecord::RecordNotFound if no record
181
171
  # is found.
182
172
  def second!
183
- find_nth! 1
173
+ second || raise_record_not_found_exception!
184
174
  end
185
175
 
186
176
  # Find the third record.
@@ -190,13 +180,13 @@ module ActiveRecord
190
180
  # Person.offset(3).third # returns the third object from OFFSET 3 (which is OFFSET 5)
191
181
  # Person.where(["user_name = :u", { u: user_name }]).third
192
182
  def third
193
- find_nth(2, offset_index)
183
+ find_nth 2
194
184
  end
195
185
 
196
- # Same as +third+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
186
+ # Same as #third but raises ActiveRecord::RecordNotFound if no record
197
187
  # is found.
198
188
  def third!
199
- find_nth! 2
189
+ third || raise_record_not_found_exception!
200
190
  end
201
191
 
202
192
  # Find the fourth record.
@@ -206,13 +196,13 @@ module ActiveRecord
206
196
  # Person.offset(3).fourth # returns the fourth object from OFFSET 3 (which is OFFSET 6)
207
197
  # Person.where(["user_name = :u", { u: user_name }]).fourth
208
198
  def fourth
209
- find_nth(3, offset_index)
199
+ find_nth 3
210
200
  end
211
201
 
212
- # Same as +fourth+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
202
+ # Same as #fourth but raises ActiveRecord::RecordNotFound if no record
213
203
  # is found.
214
204
  def fourth!
215
- find_nth! 3
205
+ fourth || raise_record_not_found_exception!
216
206
  end
217
207
 
218
208
  # Find the fifth record.
@@ -222,13 +212,13 @@ module ActiveRecord
222
212
  # Person.offset(3).fifth # returns the fifth object from OFFSET 3 (which is OFFSET 7)
223
213
  # Person.where(["user_name = :u", { u: user_name }]).fifth
224
214
  def fifth
225
- find_nth(4, offset_index)
215
+ find_nth 4
226
216
  end
227
217
 
228
- # Same as +fifth+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
218
+ # Same as #fifth but raises ActiveRecord::RecordNotFound if no record
229
219
  # is found.
230
220
  def fifth!
231
- find_nth! 4
221
+ fifth || raise_record_not_found_exception!
232
222
  end
233
223
 
234
224
  # Find the forty-second record. Also known as accessing "the reddit".
@@ -238,17 +228,49 @@ module ActiveRecord
238
228
  # Person.offset(3).forty_two # returns the forty-second object from OFFSET 3 (which is OFFSET 44)
239
229
  # Person.where(["user_name = :u", { u: user_name }]).forty_two
240
230
  def forty_two
241
- find_nth(41, offset_index)
231
+ find_nth 41
242
232
  end
243
233
 
244
- # Same as +forty_two+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
234
+ # Same as #forty_two but raises ActiveRecord::RecordNotFound if no record
245
235
  # is found.
246
236
  def forty_two!
247
- find_nth! 41
237
+ forty_two || raise_record_not_found_exception!
248
238
  end
249
239
 
250
- # Returns +true+ if a record exists in the table that matches the +id+ or
251
- # conditions given, or +false+ otherwise. The argument can take six forms:
240
+ # Find the third-to-last record.
241
+ # If no order is defined it will order by primary key.
242
+ #
243
+ # Person.third_to_last # returns the third-to-last object fetched by SELECT * FROM people
244
+ # Person.offset(3).third_to_last # returns the third-to-last object from OFFSET 3
245
+ # Person.where(["user_name = :u", { u: user_name }]).third_to_last
246
+ def third_to_last
247
+ find_nth_from_last 3
248
+ end
249
+
250
+ # Same as #third_to_last but raises ActiveRecord::RecordNotFound if no record
251
+ # is found.
252
+ def third_to_last!
253
+ third_to_last || raise_record_not_found_exception!
254
+ end
255
+
256
+ # Find the second-to-last record.
257
+ # If no order is defined it will order by primary key.
258
+ #
259
+ # Person.second_to_last # returns the second-to-last object fetched by SELECT * FROM people
260
+ # Person.offset(3).second_to_last # returns the second-to-last object from OFFSET 3
261
+ # Person.where(["user_name = :u", { u: user_name }]).second_to_last
262
+ def second_to_last
263
+ find_nth_from_last 2
264
+ end
265
+
266
+ # Same as #second_to_last but raises ActiveRecord::RecordNotFound if no record
267
+ # is found.
268
+ def second_to_last!
269
+ second_to_last || raise_record_not_found_exception!
270
+ end
271
+
272
+ # Returns true if a record exists in the table that matches the +id+ or
273
+ # conditions given, or false otherwise. The argument can take six forms:
252
274
  #
253
275
  # * Integer - Finds the record with this primary key.
254
276
  # * String - Finds the record with a primary key corresponding to this
@@ -258,10 +280,10 @@ module ActiveRecord
258
280
  # * Hash - Finds the record that matches these +find+-style conditions
259
281
  # (such as <tt>{name: 'David'}</tt>).
260
282
  # * +false+ - Returns always +false+.
261
- # * No args - Returns +false+ if the table is empty, +true+ otherwise.
283
+ # * No args - Returns +false+ if the relation is empty, +true+ otherwise.
262
284
  #
263
285
  # For more information about specifying conditions as a hash or array,
264
- # see the Conditions section in the introduction to <tt>ActiveRecord::Base</tt>.
286
+ # see the Conditions section in the introduction to ActiveRecord::Base.
265
287
  #
266
288
  # Note: You can't pass in a condition as a string (like <tt>name =
267
289
  # 'Jamie'</tt>), since it would be sanitized and then queried against
@@ -274,243 +296,257 @@ module ActiveRecord
274
296
  # Person.exists?(name: 'David')
275
297
  # Person.exists?(false)
276
298
  # Person.exists?
299
+ # Person.where(name: 'Spartacus', rating: 4).exists?
277
300
  def exists?(conditions = :none)
278
301
  if Base === conditions
279
- conditions = conditions.id
280
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
302
+ raise ArgumentError, <<-MSG.squish
281
303
  You are passing an instance of ActiveRecord::Base to `exists?`.
282
- Please pass the id of the object by calling `.id`
304
+ Please pass the id of the object by calling `.id`.
283
305
  MSG
284
306
  end
285
307
 
286
- return false if !conditions
287
-
288
- relation = apply_join_dependency(self, construct_join_dependency)
289
- return false if ActiveRecord::NullRelation === relation
290
-
291
- relation = relation.except(:select, :order).select(ONE_AS_ONE).limit(1)
308
+ return false if !conditions || limit_value == 0
292
309
 
293
- case conditions
294
- when Array, Hash
295
- relation = relation.where(conditions)
296
- else
297
- unless conditions == :none
298
- relation = relation.where(primary_key => conditions)
299
- end
310
+ if eager_loading?
311
+ relation = apply_join_dependency(eager_loading: false)
312
+ return relation.exists?(conditions)
300
313
  end
301
314
 
302
- connection.select_value(relation, "#{name} Exists", relation.arel.bind_values + relation.bind_values) ? 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
303
318
  end
304
319
 
305
320
  # This method is called whenever no records are found with either a single
306
- # id or multiple ids and raises a +ActiveRecord::RecordNotFound+ exception.
321
+ # id or multiple ids and raises an ActiveRecord::RecordNotFound exception.
307
322
  #
308
323
  # The error message is different depending on whether a single id or
309
324
  # multiple ids are provided. If multiple ids are provided, then the number
310
325
  # of results obtained should be provided in the +result_size+ argument and
311
326
  # the expected number of results should be provided in the +expected_size+
312
327
  # argument.
313
- def raise_record_not_found_exception!(ids, result_size, expected_size) #:nodoc:
314
- conditions = arel.where_sql
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)
315
330
  conditions = " [#{conditions}]" if conditions
316
-
317
- if Array(ids).size == 1
318
- error = "Couldn't find #{@klass.name} with '#{primary_key}'=#{ids}#{conditions}"
331
+ name = @klass.name
332
+
333
+ if ids.nil?
334
+ error = +"Couldn't find #{name}"
335
+ error << " with#{conditions}" if conditions
336
+ raise RecordNotFound.new(error, name, key)
337
+ elsif Array(ids).size == 1
338
+ error = "Couldn't find #{name} with '#{key}'=#{ids}#{conditions}"
339
+ raise RecordNotFound.new(error, name, key, ids)
319
340
  else
320
- error = "Couldn't find all #{@klass.name.pluralize} with '#{primary_key}': "
321
- error << "(#{ids.join(", ")})#{conditions} (found #{result_size} results, but was looking for #{expected_size})"
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)
322
345
  end
323
-
324
- raise RecordNotFound, error
325
346
  end
326
347
 
327
348
  private
328
349
 
329
- def offset_index
330
- offset_value || 0
331
- end
350
+ def offset_index
351
+ offset_value || 0
352
+ end
332
353
 
333
- def find_with_associations
334
- # NOTE: the JoinDependency constructed here needs to know about
335
- # any joins already present in `self`, so pass them in
336
- #
337
- # failing to do so means that in cases like activerecord/test/cases/associations/inner_join_association_test.rb:136
338
- # incorrect SQL is generated. In that case, the join dependency for
339
- # SpecialCategorizations is constructed without knowledge of the
340
- # preexisting join in joins_values to categorizations (by way of
341
- # the `has_many :through` for categories).
342
- #
343
- join_dependency = construct_join_dependency(joins_values)
344
-
345
- aliases = join_dependency.aliases
346
- relation = select aliases.columns
347
- relation = apply_join_dependency(relation, join_dependency)
348
-
349
- if block_given?
350
- yield relation
351
- else
352
- if ActiveRecord::NullRelation === relation
353
- []
354
+ def construct_relation_for_exists(conditions)
355
+ conditions = sanitize_forbidden_attributes(conditions)
356
+
357
+ if distinct_value && offset_value
358
+ relation = except(:order).limit!(1)
354
359
  else
355
- arel = relation.arel
356
- rows = connection.select_all(arel, 'SQL', arel.bind_values + relation.bind_values)
357
- join_dependency.instantiate(rows, aliases)
360
+ relation = except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
358
361
  end
359
- end
360
- end
361
362
 
362
- def construct_join_dependency(joins = [])
363
- including = eager_load_values + includes_values
364
- ActiveRecord::Associations::JoinDependency.new(@klass, including, joins)
365
- end
363
+ case conditions
364
+ when Array, Hash
365
+ relation.where!(conditions) unless conditions.empty?
366
+ else
367
+ relation.where!(primary_key => conditions) unless conditions == :none
368
+ end
366
369
 
367
- def construct_relation_for_association_calculations
368
- from = arel.froms.first
369
- if Arel::Table === from
370
- apply_join_dependency(self, construct_join_dependency(joins_values))
371
- else
372
- # FIXME: as far as I can tell, `from` will always be an Arel::Table.
373
- # There are no tests that test this branch, but presumably it's
374
- # possible for `from` to be a list?
375
- apply_join_dependency(self, construct_join_dependency(from))
370
+ relation
376
371
  end
377
- end
378
372
 
379
- def apply_join_dependency(relation, join_dependency)
380
- relation = relation.except(:includes, :eager_load, :preload)
381
- relation = relation.joins join_dependency
373
+ def apply_join_dependency(eager_loading: group_values.empty?)
374
+ join_dependency = construct_join_dependency(
375
+ eager_load_values + includes_values, Arel::Nodes::OuterJoin
376
+ )
377
+ relation = except(:includes, :eager_load, :preload).joins!(join_dependency)
382
378
 
383
- if using_limitable_reflections?(join_dependency.reflections)
384
- relation
385
- else
386
- if relation.limit_value
387
- limited_ids = limited_ids_for(relation)
388
- limited_ids.empty? ? relation.none! : relation.where!(table[primary_key].in(limited_ids))
379
+ if eager_loading && !using_limitable_reflections?(join_dependency.reflections)
380
+ if has_limit_or_offset?
381
+ limited_ids = limited_ids_for(relation)
382
+ limited_ids.empty? ? relation.none! : relation.where!(primary_key => limited_ids)
383
+ end
384
+ relation.limit_value = relation.offset_value = nil
385
+ end
386
+
387
+ if block_given?
388
+ yield relation, join_dependency
389
+ else
390
+ relation
389
391
  end
390
- relation.except(:limit, :offset)
391
392
  end
392
- end
393
393
 
394
- def limited_ids_for(relation)
395
- values = @klass.connection.columns_for_distinct(
396
- "#{quoted_table_name}.#{quoted_primary_key}", relation.order_values)
394
+ def limited_ids_for(relation)
395
+ values = @klass.connection.columns_for_distinct(
396
+ connection.visitor.compile(arel_attribute(primary_key)),
397
+ relation.order_values
398
+ )
397
399
 
398
- relation = relation.except(:select).select(values).distinct!
399
- arel = relation.arel
400
+ relation = relation.except(:select).select(values).distinct!
400
401
 
401
- id_rows = @klass.connection.select_all(arel, 'SQL', arel.bind_values + relation.bind_values)
402
- id_rows.map {|row| row[primary_key]}
403
- end
402
+ id_rows = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, "SQL") }
403
+ id_rows.map { |row| row[primary_key] }
404
+ end
404
405
 
405
- def using_limitable_reflections?(reflections)
406
- reflections.none? { |r| r.collection? }
407
- end
406
+ def using_limitable_reflections?(reflections)
407
+ reflections.none?(&:collection?)
408
+ end
408
409
 
409
- protected
410
+ def find_with_ids(*ids)
411
+ raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
410
412
 
411
- def find_with_ids(*ids)
412
- raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
413
+ expects_array = ids.first.kind_of?(Array)
414
+ return [] if expects_array && ids.first.empty?
413
415
 
414
- expects_array = ids.first.kind_of?(Array)
415
- return ids.first if expects_array && ids.first.empty?
416
+ ids = ids.flatten.compact.uniq
416
417
 
417
- ids = ids.flatten.compact.uniq
418
+ model_name = @klass.name
418
419
 
419
- case ids.size
420
- when 0
421
- raise RecordNotFound, "Couldn't find #{@klass.name} without an ID"
422
- when 1
423
- result = find_one(ids.first)
424
- expects_array ? [ result ] : result
425
- else
426
- find_some(ids)
420
+ case ids.size
421
+ when 0
422
+ error_message = "Couldn't find #{model_name} without an ID"
423
+ raise RecordNotFound.new(error_message, model_name, primary_key)
424
+ when 1
425
+ result = find_one(ids.first)
426
+ expects_array ? [ result ] : result
427
+ else
428
+ find_some(ids)
429
+ end
427
430
  end
428
- rescue RangeError
429
- raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID"
430
- end
431
431
 
432
- def find_one(id)
433
- if ActiveRecord::Base === id
434
- id = id.id
435
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
436
- You are passing an instance of ActiveRecord::Base to `find`.
437
- Please pass the id of the object by calling `.id`
438
- MSG
432
+ def find_one(id)
433
+ if ActiveRecord::Base === id
434
+ raise ArgumentError, <<-MSG.squish
435
+ You are passing an instance of ActiveRecord::Base to `find`.
436
+ Please pass the id of the object by calling `.id`.
437
+ MSG
438
+ end
439
+
440
+ relation = where(primary_key => id)
441
+ record = relation.take
442
+
443
+ raise_record_not_found_exception!(id, 0, 1) unless record
444
+
445
+ record
439
446
  end
440
447
 
441
- relation = where(primary_key => id)
442
- record = relation.take
448
+ def find_some(ids)
449
+ return find_some_ordered(ids) unless order_values.present?
443
450
 
444
- raise_record_not_found_exception!(id, 0, 1) unless record
451
+ result = where(primary_key => ids).to_a
445
452
 
446
- record
447
- end
453
+ expected_size =
454
+ if limit_value && ids.size > limit_value
455
+ limit_value
456
+ else
457
+ ids.size
458
+ end
448
459
 
449
- def find_some(ids)
450
- result = where(primary_key => ids).to_a
460
+ # 11 ids with limit 3, offset 9 should give 2 results.
461
+ if offset_value && (ids.size - offset_value < expected_size)
462
+ expected_size = ids.size - offset_value
463
+ end
451
464
 
452
- expected_size =
453
- if limit_value && ids.size > limit_value
454
- limit_value
465
+ if result.size == expected_size
466
+ result
455
467
  else
456
- ids.size
468
+ raise_record_not_found_exception!(ids, result.size, expected_size)
457
469
  end
470
+ end
471
+
472
+ def find_some_ordered(ids)
473
+ ids = ids.slice(offset_value || 0, limit_value || ids.size) || []
458
474
 
459
- # 11 ids with limit 3, offset 9 should give 2 results.
460
- if offset_value && (ids.size - offset_value < expected_size)
461
- expected_size = ids.size - offset_value
475
+ result = except(:limit, :offset).where(primary_key => ids).records
476
+
477
+ if result.size == ids.size
478
+ pk_type = @klass.type_for_attribute(primary_key)
479
+
480
+ records_by_id = result.index_by(&:id)
481
+ ids.map { |id| records_by_id.fetch(pk_type.cast(id)) }
482
+ else
483
+ raise_record_not_found_exception!(ids, result.size, ids.size)
484
+ end
462
485
  end
463
486
 
464
- if result.size == expected_size
465
- result
466
- else
467
- raise_record_not_found_exception!(ids, result.size, expected_size)
487
+ def find_take
488
+ if loaded?
489
+ records.first
490
+ else
491
+ @take ||= limit(1).records.first
492
+ end
468
493
  end
469
- end
470
494
 
471
- def find_take
472
- if loaded?
473
- @records.first
474
- else
475
- @take ||= limit(1).to_a.first
495
+ def find_take_with_limit(limit)
496
+ if loaded?
497
+ records.take(limit)
498
+ else
499
+ limit(limit).to_a
500
+ end
476
501
  end
477
- end
478
502
 
479
- def find_nth(index, offset)
480
- if loaded?
481
- @records[index]
482
- else
483
- offset += index
484
- @offsets[offset] ||= find_nth_with_limit(offset, 1).first
503
+ def find_nth(index)
504
+ @offsets[offset_index + index] ||= find_nth_with_limit(index, 1).first
485
505
  end
486
- end
487
506
 
488
- def find_nth!(index)
489
- find_nth(index, offset_index) or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
490
- end
507
+ def find_nth_with_limit(index, limit)
508
+ if loaded?
509
+ records[index, limit] || []
510
+ else
511
+ relation = ordered_relation
491
512
 
492
- def find_nth_with_limit(offset, limit)
493
- relation = if order_values.empty? && primary_key
494
- order(arel_table[primary_key].asc)
495
- else
496
- self
497
- end
513
+ if limit_value
514
+ limit = [limit_value - index, limit].min
515
+ end
498
516
 
499
- relation = relation.offset(offset) unless offset.zero?
500
- relation.limit(limit).to_a
501
- end
517
+ if limit > 0
518
+ relation = relation.offset(offset_index + index) unless index.zero?
519
+ relation.limit(limit).to_a
520
+ else
521
+ []
522
+ end
523
+ end
524
+ end
502
525
 
503
- def find_last
504
- if loaded?
505
- @records.last
506
- else
507
- @last ||=
508
- if limit_value
509
- to_a.last
526
+ def find_nth_from_last(index)
527
+ if loaded?
528
+ records[-index]
529
+ else
530
+ relation = ordered_relation
531
+
532
+ if equal?(relation) || has_limit_or_offset?
533
+ relation.records[-index]
510
534
  else
511
- reverse_order.limit(1).to_a.first
535
+ relation.last(index)[-index]
512
536
  end
537
+ end
538
+ end
539
+
540
+ def find_last(limit)
541
+ limit ? records.last(limit) : records.last
542
+ end
543
+
544
+ def ordered_relation
545
+ if order_values.empty? && (implicit_order_column || primary_key)
546
+ order(arel_attribute(implicit_order_column || primary_key).asc)
547
+ else
548
+ self
549
+ end
513
550
  end
514
- end
515
551
  end
516
552
  end