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.

Potentially problematic release.


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

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,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Associations
3
5
  # Association proxies in Active Record are middlemen between the object that
@@ -28,12 +30,12 @@ module ActiveRecord
28
30
  # is computed directly through SQL and does not trigger by itself the
29
31
  # instantiation of the actual post records.
30
32
  class CollectionProxy < Relation
31
- delegate(*(ActiveRecord::Calculations.public_instance_methods - [:count]), to: :scope)
32
-
33
33
  def initialize(klass, association) #:nodoc:
34
34
  @association = association
35
- super klass, klass.arel_table
36
- merge! association.scope(nullify: false)
35
+ super klass
36
+
37
+ extensions = association.extensions
38
+ extend(*extensions) if extensions.any?
37
39
  end
38
40
 
39
41
  def target
@@ -53,6 +55,12 @@ module ActiveRecord
53
55
  @association.loaded?
54
56
  end
55
57
 
58
+ ##
59
+ # :method: select
60
+ #
61
+ # :call-seq:
62
+ # select(*fields, &block)
63
+ #
56
64
  # Works in two ways.
57
65
  #
58
66
  # *First:* Specify a subset of fields to be selected from the result set.
@@ -75,7 +83,7 @@ module ActiveRecord
75
83
  # # #<Pet id: nil, name: "Choo-Choo">
76
84
  # # ]
77
85
  #
78
- # person.pets.select(:id, :name )
86
+ # person.pets.select(:id, :name)
79
87
  # # => [
80
88
  # # #<Pet id: 1, name: "Fancy-Fancy">,
81
89
  # # #<Pet id: 2, name: "Spook">,
@@ -100,18 +108,9 @@ module ActiveRecord
100
108
  # # #<Pet id: 2, name: "Spook", person_id: 1>,
101
109
  # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
102
110
  # # ]
103
- #
104
- # person.pets.select(:name) { |pet| pet.name =~ /oo/ }
105
- # # => [
106
- # # #<Pet id: 2, name: "Spook">,
107
- # # #<Pet id: 3, name: "Choo-Choo">
108
- # # ]
109
- def select(*fields, &block)
110
- @association.select(*fields, &block)
111
- end
112
111
 
113
112
  # Finds an object in the collection responding to the +id+. Uses the same
114
- # rules as <tt>ActiveRecord::Base.find</tt>. Returns <tt>ActiveRecord::RecordNotFound</tt>
113
+ # rules as ActiveRecord::Base.find. Returns ActiveRecord::RecordNotFound
115
114
  # error if the object cannot be found.
116
115
  #
117
116
  # class Person < ActiveRecord::Base
@@ -126,7 +125,7 @@ module ActiveRecord
126
125
  # # ]
127
126
  #
128
127
  # person.pets.find(1) # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
129
- # person.pets.find(4) # => ActiveRecord::RecordNotFound: Couldn't find Pet with id=4
128
+ # person.pets.find(4) # => ActiveRecord::RecordNotFound: Couldn't find Pet with 'id'=4
130
129
  #
131
130
  # person.pets.find(2) { |pet| pet.name.downcase! }
132
131
  # # => #<Pet id: 2, name: "fancy-fancy", person_id: 1>
@@ -136,10 +135,17 @@ module ActiveRecord
136
135
  # # #<Pet id: 2, name: "Spook", person_id: 1>,
137
136
  # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
138
137
  # # ]
139
- def find(*args, &block)
140
- @association.find(*args, &block)
138
+ def find(*args)
139
+ return super if block_given?
140
+ @association.find(*args)
141
141
  end
142
142
 
143
+ ##
144
+ # :method: first
145
+ #
146
+ # :call-seq:
147
+ # first(limit = nil)
148
+ #
143
149
  # Returns the first record, or the first +n+ records, from the collection.
144
150
  # If the collection is empty, the first form returns +nil+, and the second
145
151
  # form returns an empty array.
@@ -166,35 +172,63 @@ module ActiveRecord
166
172
  # another_person_without.pets # => []
167
173
  # another_person_without.pets.first # => nil
168
174
  # another_person_without.pets.first(3) # => []
169
- def first(*args)
170
- @association.first(*args)
171
- end
172
175
 
173
- # Same as +first+ except returns only the second record.
174
- def second(*args)
175
- @association.second(*args)
176
- end
176
+ ##
177
+ # :method: second
178
+ #
179
+ # :call-seq:
180
+ # second()
181
+ #
182
+ # Same as #first except returns only the second record.
177
183
 
178
- # Same as +first+ except returns only the third record.
179
- def third(*args)
180
- @association.third(*args)
181
- end
184
+ ##
185
+ # :method: third
186
+ #
187
+ # :call-seq:
188
+ # third()
189
+ #
190
+ # Same as #first except returns only the third record.
182
191
 
183
- # Same as +first+ except returns only the fourth record.
184
- def fourth(*args)
185
- @association.fourth(*args)
186
- end
192
+ ##
193
+ # :method: fourth
194
+ #
195
+ # :call-seq:
196
+ # fourth()
197
+ #
198
+ # Same as #first except returns only the fourth record.
187
199
 
188
- # Same as +first+ except returns only the fifth record.
189
- def fifth(*args)
190
- @association.fifth(*args)
191
- end
200
+ ##
201
+ # :method: fifth
202
+ #
203
+ # :call-seq:
204
+ # fifth()
205
+ #
206
+ # Same as #first except returns only the fifth record.
192
207
 
193
- # Same as +first+ except returns only the forty second record.
208
+ ##
209
+ # :method: forty_two
210
+ #
211
+ # :call-seq:
212
+ # forty_two()
213
+ #
214
+ # Same as #first except returns only the forty second record.
194
215
  # Also known as accessing "the reddit".
195
- def forty_two(*args)
196
- @association.forty_two(*args)
197
- end
216
+
217
+ ##
218
+ # :method: third_to_last
219
+ #
220
+ # :call-seq:
221
+ # third_to_last()
222
+ #
223
+ # Same as #first except returns only the third-to-last record.
224
+
225
+ ##
226
+ # :method: second_to_last
227
+ #
228
+ # :call-seq:
229
+ # second_to_last()
230
+ #
231
+ # Same as #first except returns only the second-to-last record.
198
232
 
199
233
  # Returns the last record, or the last +n+ records, from the collection.
200
234
  # If the collection is empty, the first form returns +nil+, and the second
@@ -222,8 +256,39 @@ module ActiveRecord
222
256
  # another_person_without.pets # => []
223
257
  # another_person_without.pets.last # => nil
224
258
  # another_person_without.pets.last(3) # => []
225
- def last(*args)
226
- @association.last(*args)
259
+ def last(limit = nil)
260
+ load_target if find_from_target?
261
+ super
262
+ end
263
+
264
+ # Gives a record (or N records if a parameter is supplied) from the collection
265
+ # using the same rules as <tt>ActiveRecord::Base.take</tt>.
266
+ #
267
+ # class Person < ActiveRecord::Base
268
+ # has_many :pets
269
+ # end
270
+ #
271
+ # person.pets
272
+ # # => [
273
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
274
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
275
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
276
+ # # ]
277
+ #
278
+ # person.pets.take # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
279
+ #
280
+ # person.pets.take(2)
281
+ # # => [
282
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
283
+ # # #<Pet id: 2, name: "Spook", person_id: 1>
284
+ # # ]
285
+ #
286
+ # another_person_without.pets # => []
287
+ # another_person_without.pets.take # => nil
288
+ # another_person_without.pets.take(2) # => []
289
+ def take(limit = nil)
290
+ load_target if find_from_target?
291
+ super
227
292
  end
228
293
 
229
294
  # Returns a new object of the collection type that has been instantiated
@@ -285,7 +350,7 @@ module ActiveRecord
285
350
  @association.create(attributes, &block)
286
351
  end
287
352
 
288
- # Like +create+, except that if the record is invalid, raises an exception.
353
+ # Like #create, except that if the record is invalid, raises an exception.
289
354
  #
290
355
  # class Person
291
356
  # has_many :pets
@@ -301,34 +366,6 @@ module ActiveRecord
301
366
  @association.create!(attributes, &block)
302
367
  end
303
368
 
304
- # Add one or more records to the collection by setting their foreign keys
305
- # to the association's primary key. Since << flattens its argument list and
306
- # inserts each record, +push+ and +concat+ behave identically. Returns +self+
307
- # so method calls may be chained.
308
- #
309
- # class Person < ActiveRecord::Base
310
- # has_many :pets
311
- # end
312
- #
313
- # person.pets.size # => 0
314
- # person.pets.concat(Pet.new(name: 'Fancy-Fancy'))
315
- # person.pets.concat(Pet.new(name: 'Spook'), Pet.new(name: 'Choo-Choo'))
316
- # person.pets.size # => 3
317
- #
318
- # person.id # => 1
319
- # person.pets
320
- # # => [
321
- # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
322
- # # #<Pet id: 2, name: "Spook", person_id: 1>,
323
- # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
324
- # # ]
325
- #
326
- # person.pets.concat([Pet.new(name: 'Brain'), Pet.new(name: 'Benny')])
327
- # person.pets.size # => 5
328
- def concat(*records)
329
- @association.concat(*records)
330
- end
331
-
332
369
  # Replaces this collection with +other_array+. This will perform a diff
333
370
  # and delete/add only records that have changed.
334
371
  #
@@ -359,7 +396,7 @@ module ActiveRecord
359
396
  # specified by the +:dependent+ option. If no +:dependent+ option is given,
360
397
  # then it will follow the default strategy.
361
398
  #
362
- # For +has_many :through+ associations, the default deletion strategy is
399
+ # For <tt>has_many :through</tt> associations, the default deletion strategy is
363
400
  # +:delete_all+.
364
401
  #
365
402
  # For +has_many+ associations, the default deletion strategy is +:nullify+.
@@ -394,7 +431,7 @@ module ActiveRecord
394
431
  # # #<Pet id: 3, name: "Choo-Choo", person_id: nil>
395
432
  # # ]
396
433
  #
397
- # Both +has_many+ and +has_many :through+ dependencies default to the
434
+ # Both +has_many+ and <tt>has_many :through</tt> dependencies default to the
398
435
  # +:delete_all+ strategy if the +:dependent+ option is set to +:destroy+.
399
436
  # Records are not instantiated and callbacks will not be fired.
400
437
  #
@@ -413,7 +450,7 @@ module ActiveRecord
413
450
  # person.pets.delete_all
414
451
  #
415
452
  # Pet.find(1, 2, 3)
416
- # # => ActiveRecord::RecordNotFound
453
+ # # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
417
454
  #
418
455
  # If it is set to <tt>:delete_all</tt>, all the objects are deleted
419
456
  # *without* calling their +destroy+ method.
@@ -433,9 +470,9 @@ module ActiveRecord
433
470
  # person.pets.delete_all
434
471
  #
435
472
  # Pet.find(1, 2, 3)
436
- # # => ActiveRecord::RecordNotFound
473
+ # # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
437
474
  def delete_all(dependent = nil)
438
- @association.delete_all(dependent)
475
+ @association.delete_all(dependent).tap { reset_scope }
439
476
  end
440
477
 
441
478
  # Deletes the records of the collection directly from the database
@@ -462,18 +499,19 @@ module ActiveRecord
462
499
  #
463
500
  # Pet.find(1) # => Couldn't find Pet with id=1
464
501
  def destroy_all
465
- @association.destroy_all
502
+ @association.destroy_all.tap { reset_scope }
466
503
  end
467
504
 
468
- # Deletes the +records+ supplied and removes them from the collection. For
469
- # +has_many+ associations, the deletion is done according to the strategy
470
- # specified by the <tt>:dependent</tt> option. Returns an array with the
505
+ # Deletes the +records+ supplied from the collection according to the strategy
506
+ # specified by the +:dependent+ option. If no +:dependent+ option is given,
507
+ # then it will follow the default strategy. Returns an array with the
471
508
  # deleted records.
472
509
  #
473
- # If no <tt>:dependent</tt> option is given, then it will follow the default
474
- # strategy. The default strategy is <tt>:nullify</tt>. This sets the foreign
475
- # keys to <tt>NULL</tt>. For, +has_many+ <tt>:through</tt>, the default
476
- # strategy is +delete_all+.
510
+ # For <tt>has_many :through</tt> associations, the default deletion strategy is
511
+ # +:delete_all+.
512
+ #
513
+ # For +has_many+ associations, the default deletion strategy is +:nullify+.
514
+ # This sets the foreign keys to +NULL+.
477
515
  #
478
516
  # class Person < ActiveRecord::Base
479
517
  # has_many :pets # dependent: :nullify option by default
@@ -526,7 +564,7 @@ module ActiveRecord
526
564
  # # => [#<Pet id: 2, name: "Spook", person_id: 1>]
527
565
  #
528
566
  # Pet.find(1, 3)
529
- # # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (1, 3)
567
+ # # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 3)
530
568
  #
531
569
  # If it is set to <tt>:delete_all</tt>, all the +records+ are deleted
532
570
  # *without* calling their +destroy+ method.
@@ -554,9 +592,9 @@ module ActiveRecord
554
592
  # # ]
555
593
  #
556
594
  # Pet.find(1)
557
- # # => ActiveRecord::RecordNotFound: Couldn't find Pet with id=1
595
+ # # => ActiveRecord::RecordNotFound: Couldn't find Pet with 'id'=1
558
596
  #
559
- # You can pass +Fixnum+ or +String+ values, it finds the records
597
+ # You can pass +Integer+ or +String+ values, it finds the records
560
598
  # responding to the +id+ and executes delete on them.
561
599
  #
562
600
  # class Person < ActiveRecord::Base
@@ -580,7 +618,7 @@ module ActiveRecord
580
618
  # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
581
619
  # # ]
582
620
  def delete(*records)
583
- @association.delete(*records)
621
+ @association.delete(*records).tap { reset_scope }
584
622
  end
585
623
 
586
624
  # Destroys the +records+ supplied and removes them from the collection.
@@ -618,9 +656,9 @@ module ActiveRecord
618
656
  # person.pets.size # => 0
619
657
  # person.pets # => []
620
658
  #
621
- # Pet.find(1, 2, 3) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (1, 2, 3)
659
+ # Pet.find(1, 2, 3) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
622
660
  #
623
- # You can pass +Fixnum+ or +String+ values, it finds the records
661
+ # You can pass +Integer+ or +String+ values, it finds the records
624
662
  # responding to the +id+ and then deletes them from the database.
625
663
  #
626
664
  # person.pets.size # => 3
@@ -650,11 +688,17 @@ module ActiveRecord
650
688
  # person.pets.size # => 0
651
689
  # person.pets # => []
652
690
  #
653
- # Pet.find(4, 5, 6) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (4, 5, 6)
691
+ # Pet.find(4, 5, 6) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (4, 5, 6)
654
692
  def destroy(*records)
655
- @association.destroy(*records)
693
+ @association.destroy(*records).tap { reset_scope }
656
694
  end
657
695
 
696
+ ##
697
+ # :method: distinct
698
+ #
699
+ # :call-seq:
700
+ # distinct(value = true)
701
+ #
658
702
  # Specifies whether the records should be unique or not.
659
703
  #
660
704
  # class Person < ActiveRecord::Base
@@ -669,17 +713,35 @@ module ActiveRecord
669
713
  #
670
714
  # person.pets.select(:name).distinct
671
715
  # # => [#<Pet name: "Fancy-Fancy">]
672
- def distinct
673
- @association.distinct
716
+ #
717
+ # person.pets.select(:name).distinct.distinct(false)
718
+ # # => [
719
+ # # #<Pet name: "Fancy-Fancy">,
720
+ # # #<Pet name: "Fancy-Fancy">
721
+ # # ]
722
+
723
+ #--
724
+ def calculate(operation, column_name)
725
+ null_scope? ? scope.calculate(operation, column_name) : super
674
726
  end
675
- alias uniq distinct
676
727
 
677
- # Count all records using SQL.
728
+ def pluck(*column_names)
729
+ null_scope? ? scope.pluck(*column_names) : super
730
+ end
731
+
732
+ ##
733
+ # :method: count
734
+ #
735
+ # :call-seq:
736
+ # count(column_name = nil, &block)
737
+ #
738
+ # Count all records.
678
739
  #
679
740
  # class Person < ActiveRecord::Base
680
741
  # has_many :pets
681
742
  # end
682
743
  #
744
+ # # This will perform the count using SQL.
683
745
  # person.pets.count # => 3
684
746
  # person.pets
685
747
  # # => [
@@ -687,11 +749,11 @@ module ActiveRecord
687
749
  # # #<Pet id: 2, name: "Spook", person_id: 1>,
688
750
  # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
689
751
  # # ]
690
- def count(column_name = nil, options = {})
691
- # TODO: Remove options argument as soon we remove support to
692
- # activerecord-deprecated_finders.
693
- @association.count(column_name, options)
694
- end
752
+ #
753
+ # Passing a block will select all of a person's pets in SQL and then
754
+ # perform the count using Ruby.
755
+ #
756
+ # person.pets.count { |pet| pet.name.include?('-') } # => 2
695
757
 
696
758
  # Returns the size of the collection. If the collection hasn't been loaded,
697
759
  # it executes a <tt>SELECT COUNT(*)</tt> query. Else it calls <tt>collection.size</tt>.
@@ -721,6 +783,12 @@ module ActiveRecord
721
783
  @association.size
722
784
  end
723
785
 
786
+ ##
787
+ # :method: length
788
+ #
789
+ # :call-seq:
790
+ # length()
791
+ #
724
792
  # Returns the size of the collection calling +size+ on the target.
725
793
  # If the collection has been already loaded, +length+ and +size+ are
726
794
  # equivalent. If not and you are going to need the records anyway this
@@ -741,14 +809,11 @@ module ActiveRecord
741
809
  # # #<Pet id: 2, name: "Spook", person_id: 1>,
742
810
  # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
743
811
  # # ]
744
- def length
745
- @association.length
746
- end
747
812
 
748
813
  # Returns +true+ if the collection is empty. If the collection has been
749
814
  # loaded it is equivalent
750
815
  # to <tt>collection.size.zero?</tt>. If the collection has not been loaded,
751
- # it is equivalent to <tt>collection.exists?</tt>. If the collection has
816
+ # it is equivalent to <tt>!collection.exists?</tt>. If the collection has
752
817
  # not already been loaded and you are going to fetch the records anyway it
753
818
  # is better to check <tt>collection.length.zero?</tt>.
754
819
  #
@@ -767,6 +832,12 @@ module ActiveRecord
767
832
  @association.empty?
768
833
  end
769
834
 
835
+ ##
836
+ # :method: any?
837
+ #
838
+ # :call-seq:
839
+ # any?()
840
+ #
770
841
  # Returns +true+ if the collection is not empty.
771
842
  #
772
843
  # class Person < ActiveRecord::Base
@@ -777,7 +848,7 @@ module ActiveRecord
777
848
  # person.pets.any? # => false
778
849
  #
779
850
  # person.pets << Pet.new(name: 'Snoop')
780
- # person.pets.count # => 0
851
+ # person.pets.count # => 1
781
852
  # person.pets.any? # => true
782
853
  #
783
854
  # You can also pass a +block+ to define criteria. The behavior
@@ -796,10 +867,13 @@ module ActiveRecord
796
867
  # pet.group == 'dogs'
797
868
  # end
798
869
  # # => true
799
- def any?(&block)
800
- @association.any?(&block)
801
- end
802
870
 
871
+ ##
872
+ # :method: many?
873
+ #
874
+ # :call-seq:
875
+ # many?()
876
+ #
803
877
  # Returns true if the collection has more than one record.
804
878
  # Equivalent to <tt>collection.size > 1</tt>.
805
879
  #
@@ -834,9 +908,6 @@ module ActiveRecord
834
908
  # pet.group == 'cats'
835
909
  # end
836
910
  # # => true
837
- def many?(&block)
838
- @association.many?(&block)
839
- end
840
911
 
841
912
  # Returns +true+ if the given +record+ is present in the collection.
842
913
  #
@@ -852,27 +923,14 @@ module ActiveRecord
852
923
  !!@association.include?(record)
853
924
  end
854
925
 
855
- def arel
856
- scope.arel
857
- end
858
-
859
926
  def proxy_association
860
927
  @association
861
928
  end
862
929
 
863
- # We don't want this object to be put on the scoping stack, because
864
- # that could create an infinite loop where we call an @association
865
- # method, which gets the current scope, which is this object, which
866
- # delegates to @association, and so on.
867
- def scoping
868
- @association.scope.scoping { yield }
869
- end
870
-
871
930
  # Returns a <tt>Relation</tt> object for the records in this association
872
931
  def scope
873
- @association.scope
932
+ @scope ||= @association.scope
874
933
  end
875
- alias spawn scope
876
934
 
877
935
  # Equivalent to <tt>Array#==</tt>. Returns +true+ if the two arrays
878
936
  # contain the same number of elements and if each element is equal
@@ -902,6 +960,12 @@ module ActiveRecord
902
960
  load_target == other
903
961
  end
904
962
 
963
+ ##
964
+ # :method: to_ary
965
+ #
966
+ # :call-seq:
967
+ # to_ary()
968
+ #
905
969
  # Returns a new array of objects from the collection. If the collection
906
970
  # hasn't been loaded, it fetches the records from the database.
907
971
  #
@@ -935,14 +999,15 @@ module ActiveRecord
935
999
  # # #<Pet id: 5, name: "Brain", person_id: 1>,
936
1000
  # # #<Pet id: 6, name: "Boss", person_id: 1>
937
1001
  # # ]
938
- def to_ary
939
- load_target.dup
1002
+
1003
+ def records # :nodoc:
1004
+ load_target
940
1005
  end
941
- alias_method :to_a, :to_ary
942
1006
 
943
1007
  # Adds one or more +records+ to the collection by setting their foreign keys
944
- # to the association's primary key. Returns +self+, so several appends may be
945
- # chained together.
1008
+ # to the association's primary key. Since +<<+ flattens its argument list and
1009
+ # inserts each record, +push+ and +concat+ behave identically. Returns +self+
1010
+ # so several appends may be chained together.
946
1011
  #
947
1012
  # class Person < ActiveRecord::Base
948
1013
  # has_many :pets
@@ -965,21 +1030,24 @@ module ActiveRecord
965
1030
  end
966
1031
  alias_method :push, :<<
967
1032
  alias_method :append, :<<
1033
+ alias_method :concat, :<<
968
1034
 
969
1035
  def prepend(*args)
970
- raise NoMethodError, "prepend on association is not defined. Please use << or append"
1036
+ raise NoMethodError, "prepend on association is not defined. Please use <<, push or append"
971
1037
  end
972
1038
 
973
1039
  # Equivalent to +delete_all+. The difference is that returns +self+, instead
974
1040
  # of an array with the deleted objects, so methods can be chained. See
975
1041
  # +delete_all+ for more information.
1042
+ # Note that because +delete_all+ removes records by directly
1043
+ # running an SQL query into the database, the +updated_at+ column of
1044
+ # the object is not changed.
976
1045
  def clear
977
1046
  delete_all
978
1047
  self
979
1048
  end
980
1049
 
981
1050
  # Reloads the collection from the database. Returns +self+.
982
- # Equivalent to <tt>collection(true)</tt>.
983
1051
  #
984
1052
  # class Person < ActiveRecord::Base
985
1053
  # has_many :pets
@@ -993,12 +1061,9 @@ module ActiveRecord
993
1061
  #
994
1062
  # person.pets.reload # fetches pets from the database
995
1063
  # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
996
- #
997
- # person.pets(true) # fetches pets from the database
998
- # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
999
1064
  def reload
1000
1065
  proxy_association.reload
1001
- self
1066
+ reset_scope
1002
1067
  end
1003
1068
 
1004
1069
  # Unloads the association. Returns +self+.
@@ -1020,8 +1085,47 @@ module ActiveRecord
1020
1085
  def reset
1021
1086
  proxy_association.reset
1022
1087
  proxy_association.reset_scope
1088
+ reset_scope
1089
+ end
1090
+
1091
+ def reset_scope # :nodoc:
1092
+ @offsets = {}
1093
+ @scope = nil
1023
1094
  self
1024
1095
  end
1096
+
1097
+ delegate_methods = [
1098
+ QueryMethods,
1099
+ SpawnMethods,
1100
+ ].flat_map { |klass|
1101
+ klass.public_instance_methods(false)
1102
+ } - self.public_instance_methods(false) - [:select] + [:scoping]
1103
+
1104
+ delegate(*delegate_methods, to: :scope)
1105
+
1106
+ private
1107
+
1108
+ def find_nth_with_limit(index, limit)
1109
+ load_target if find_from_target?
1110
+ super
1111
+ end
1112
+
1113
+ def find_nth_from_last(index)
1114
+ load_target if find_from_target?
1115
+ super
1116
+ end
1117
+
1118
+ def null_scope?
1119
+ @association.null_scope?
1120
+ end
1121
+
1122
+ def find_from_target?
1123
+ @association.find_from_target?
1124
+ end
1125
+
1126
+ def exec_queries
1127
+ load_target
1128
+ end
1025
1129
  end
1026
1130
  end
1027
1131
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord::Associations
4
+ module ForeignAssociation # :nodoc:
5
+ def foreign_key_present?
6
+ if reflection.klass.primary_key
7
+ owner.attribute_present?(reflection.active_record_primary_key)
8
+ else
9
+ false
10
+ end
11
+ end
12
+ end
13
+ end