activerecord 4.2.9 → 5.2.8

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 +614 -1572
  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 +263 -249
  8. data/lib/active_record/association_relation.rb +11 -6
  9. data/lib/active_record/associations/alias_tracker.rb +29 -35
  10. data/lib/active_record/associations/association.rb +77 -43
  11. data/lib/active_record/associations/association_scope.rb +106 -133
  12. data/lib/active_record/associations/belongs_to_association.rb +52 -41
  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 +9 -22
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +42 -35
  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 +139 -280
  22. data/lib/active_record/associations/collection_proxy.rb +231 -133
  23. data/lib/active_record/associations/foreign_association.rb +3 -1
  24. data/lib/active_record/associations/has_many_association.rb +34 -89
  25. data/lib/active_record/associations/has_many_through_association.rb +49 -76
  26. data/lib/active_record/associations/has_one_association.rb +38 -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 -89
  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 +133 -159
  32. data/lib/active_record/associations/preloader/association.rb +85 -120
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -74
  34. data/lib/active_record/associations/preloader.rb +81 -91
  35. data/lib/active_record/associations/singular_association.rb +27 -34
  36. data/lib/active_record/associations/through_association.rb +38 -18
  37. data/lib/active_record/associations.rb +1732 -1597
  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 +10 -8
  41. data/lib/active_record/attribute_methods/dirty.rb +94 -135
  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 +58 -36
  47. data/lib/active_record/attribute_methods/write.rb +30 -45
  48. data/lib/active_record/attribute_methods.rb +166 -109
  49. data/lib/active_record/attributes.rb +201 -82
  50. data/lib/active_record/autosave_association.rb +94 -36
  51. data/lib/active_record/base.rb +57 -44
  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 +24 -12
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -290
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +237 -90
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +71 -21
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +118 -52
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +318 -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 +570 -228
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +138 -70
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +325 -202
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +542 -593
  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 +41 -188
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +45 -114
  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 -58
  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 +4 -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 -22
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -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 -5
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +55 -53
  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 +462 -284
  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 +432 -323
  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 -308
  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 +178 -198
  129. data/lib/active_record/counter_cache.rb +79 -36
  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 +135 -88
  133. data/lib/active_record/errors.rb +179 -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 +10 -5
  137. data/lib/active_record/fixture_set/file.rb +35 -9
  138. data/lib/active_record/fixtures.rb +188 -132
  139. data/lib/active_record/gem_version.rb +4 -2
  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 +21 -3
  144. data/lib/active_record/locale/en.yml +3 -2
  145. data/lib/active_record/locking/optimistic.rb +88 -96
  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 +581 -282
  152. data/lib/active_record/model_schema.rb +290 -111
  153. data/lib/active_record/nested_attributes.rb +264 -222
  154. data/lib/active_record/no_touching.rb +7 -1
  155. data/lib/active_record/null_relation.rb +24 -37
  156. data/lib/active_record/persistence.rb +347 -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 +94 -32
  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 +149 -156
  163. data/lib/active_record/readonly_attributes.rb +5 -4
  164. data/lib/active_record/reflection.rb +414 -267
  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 +256 -248
  168. data/lib/active_record/relation/delegation.rb +67 -60
  169. data/lib/active_record/relation/finder_methods.rb +288 -239
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +86 -86
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -24
  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 +116 -119
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +448 -393
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +11 -13
  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 -340
  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 -16
  193. data/lib/active_record/scoping/default.rb +102 -85
  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 +134 -96
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +56 -100
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +83 -41
  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 +199 -124
  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 -45
  212. data/lib/active_record/type/date_time.rb +4 -49
  213. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  214. data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
  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 +24 -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 +40 -41
  231. data/lib/active_record/validations.rb +38 -35
  232. data/lib/active_record/version.rb +3 -1
  233. data/lib/active_record.rb +34 -22
  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 -3
  238. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -1
  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/model/templates/{module.rb → module.rb.tt} +0 -0
  243. data/lib/rails/generators/active_record.rb +7 -5
  244. metadata +72 -50
  245. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  246. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  247. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  248. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  249. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  250. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  251. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  252. data/lib/active_record/attribute.rb +0 -163
  253. data/lib/active_record/attribute_set/builder.rb +0 -106
  254. data/lib/active_record/attribute_set.rb +0 -81
  255. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  256. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  257. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  258. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  259. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  260. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  261. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  262. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  263. data/lib/active_record/type/big_integer.rb +0 -13
  264. data/lib/active_record/type/binary.rb +0 -50
  265. data/lib/active_record/type/boolean.rb +0 -31
  266. data/lib/active_record/type/decimal.rb +0 -64
  267. data/lib/active_record/type/decorator.rb +0 -14
  268. data/lib/active_record/type/float.rb +0 -19
  269. data/lib/active_record/type/integer.rb +0 -59
  270. data/lib/active_record/type/mutable.rb +0 -16
  271. data/lib/active_record/type/numeric.rb +0 -36
  272. data/lib/active_record/type/string.rb +0 -40
  273. data/lib/active_record/type/time_value.rb +0 -38
  274. data/lib/active_record/type/value.rb +0 -110
@@ -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,13 +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
- delegate :find_nth, to: :scope
33
-
34
33
  def initialize(klass, association) #:nodoc:
35
34
  @association = association
36
- super klass, klass.arel_table
37
- merge! association.scope(nullify: false)
35
+ super klass
36
+
37
+ extensions = association.extensions
38
+ extend(*extensions) if extensions.any?
38
39
  end
39
40
 
40
41
  def target
@@ -54,6 +55,12 @@ module ActiveRecord
54
55
  @association.loaded?
55
56
  end
56
57
 
58
+ ##
59
+ # :method: select
60
+ #
61
+ # :call-seq:
62
+ # select(*fields, &block)
63
+ #
57
64
  # Works in two ways.
58
65
  #
59
66
  # *First:* Specify a subset of fields to be selected from the result set.
@@ -76,7 +83,7 @@ module ActiveRecord
76
83
  # # #<Pet id: nil, name: "Choo-Choo">
77
84
  # # ]
78
85
  #
79
- # person.pets.select(:id, :name )
86
+ # person.pets.select(:id, :name)
80
87
  # # => [
81
88
  # # #<Pet id: 1, name: "Fancy-Fancy">,
82
89
  # # #<Pet id: 2, name: "Spook">,
@@ -101,18 +108,9 @@ module ActiveRecord
101
108
  # # #<Pet id: 2, name: "Spook", person_id: 1>,
102
109
  # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
103
110
  # # ]
104
- #
105
- # person.pets.select(:name) { |pet| pet.name =~ /oo/ }
106
- # # => [
107
- # # #<Pet id: 2, name: "Spook">,
108
- # # #<Pet id: 3, name: "Choo-Choo">
109
- # # ]
110
- def select(*fields, &block)
111
- @association.select(*fields, &block)
112
- end
113
111
 
114
112
  # Finds an object in the collection responding to the +id+. Uses the same
115
- # rules as <tt>ActiveRecord::Base.find</tt>. Returns <tt>ActiveRecord::RecordNotFound</tt>
113
+ # rules as ActiveRecord::Base.find. Returns ActiveRecord::RecordNotFound
116
114
  # error if the object cannot be found.
117
115
  #
118
116
  # class Person < ActiveRecord::Base
@@ -127,7 +125,7 @@ module ActiveRecord
127
125
  # # ]
128
126
  #
129
127
  # person.pets.find(1) # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
130
- # 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
131
129
  #
132
130
  # person.pets.find(2) { |pet| pet.name.downcase! }
133
131
  # # => #<Pet id: 2, name: "fancy-fancy", person_id: 1>
@@ -137,10 +135,17 @@ module ActiveRecord
137
135
  # # #<Pet id: 2, name: "Spook", person_id: 1>,
138
136
  # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
139
137
  # # ]
140
- def find(*args, &block)
141
- @association.find(*args, &block)
138
+ def find(*args)
139
+ return super if block_given?
140
+ @association.find(*args)
142
141
  end
143
142
 
143
+ ##
144
+ # :method: first
145
+ #
146
+ # :call-seq:
147
+ # first(limit = nil)
148
+ #
144
149
  # Returns the first record, or the first +n+ records, from the collection.
145
150
  # If the collection is empty, the first form returns +nil+, and the second
146
151
  # form returns an empty array.
@@ -167,35 +172,63 @@ module ActiveRecord
167
172
  # another_person_without.pets # => []
168
173
  # another_person_without.pets.first # => nil
169
174
  # another_person_without.pets.first(3) # => []
170
- def first(*args)
171
- @association.first(*args)
172
- end
173
175
 
174
- # Same as +first+ except returns only the second record.
175
- def second(*args)
176
- @association.second(*args)
177
- end
176
+ ##
177
+ # :method: second
178
+ #
179
+ # :call-seq:
180
+ # second()
181
+ #
182
+ # Same as #first except returns only the second record.
178
183
 
179
- # Same as +first+ except returns only the third record.
180
- def third(*args)
181
- @association.third(*args)
182
- end
184
+ ##
185
+ # :method: third
186
+ #
187
+ # :call-seq:
188
+ # third()
189
+ #
190
+ # Same as #first except returns only the third record.
183
191
 
184
- # Same as +first+ except returns only the fourth record.
185
- def fourth(*args)
186
- @association.fourth(*args)
187
- end
192
+ ##
193
+ # :method: fourth
194
+ #
195
+ # :call-seq:
196
+ # fourth()
197
+ #
198
+ # Same as #first except returns only the fourth record.
188
199
 
189
- # Same as +first+ except returns only the fifth record.
190
- def fifth(*args)
191
- @association.fifth(*args)
192
- end
200
+ ##
201
+ # :method: fifth
202
+ #
203
+ # :call-seq:
204
+ # fifth()
205
+ #
206
+ # Same as #first except returns only the fifth record.
193
207
 
194
- # 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.
195
215
  # Also known as accessing "the reddit".
196
- def forty_two(*args)
197
- @association.forty_two(*args)
198
- 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.
199
232
 
200
233
  # Returns the last record, or the last +n+ records, from the collection.
201
234
  # If the collection is empty, the first form returns +nil+, and the second
@@ -223,12 +256,39 @@ module ActiveRecord
223
256
  # another_person_without.pets # => []
224
257
  # another_person_without.pets.last # => nil
225
258
  # another_person_without.pets.last(3) # => []
226
- def last(*args)
227
- @association.last(*args)
259
+ def last(limit = nil)
260
+ load_target if find_from_target?
261
+ super
228
262
  end
229
263
 
230
- def take(n = nil)
231
- @association.take(n)
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
232
292
  end
233
293
 
234
294
  # Returns a new object of the collection type that has been instantiated
@@ -290,7 +350,7 @@ module ActiveRecord
290
350
  @association.create(attributes, &block)
291
351
  end
292
352
 
293
- # 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.
294
354
  #
295
355
  # class Person
296
356
  # has_many :pets
@@ -306,34 +366,6 @@ module ActiveRecord
306
366
  @association.create!(attributes, &block)
307
367
  end
308
368
 
309
- # Add one or more records to the collection by setting their foreign keys
310
- # to the association's primary key. Since << flattens its argument list and
311
- # inserts each record, +push+ and +concat+ behave identically. Returns +self+
312
- # so method calls may be chained.
313
- #
314
- # class Person < ActiveRecord::Base
315
- # has_many :pets
316
- # end
317
- #
318
- # person.pets.size # => 0
319
- # person.pets.concat(Pet.new(name: 'Fancy-Fancy'))
320
- # person.pets.concat(Pet.new(name: 'Spook'), Pet.new(name: 'Choo-Choo'))
321
- # person.pets.size # => 3
322
- #
323
- # person.id # => 1
324
- # person.pets
325
- # # => [
326
- # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
327
- # # #<Pet id: 2, name: "Spook", person_id: 1>,
328
- # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
329
- # # ]
330
- #
331
- # person.pets.concat([Pet.new(name: 'Brain'), Pet.new(name: 'Benny')])
332
- # person.pets.size # => 5
333
- def concat(*records)
334
- @association.concat(*records)
335
- end
336
-
337
369
  # Replaces this collection with +other_array+. This will perform a diff
338
370
  # and delete/add only records that have changed.
339
371
  #
@@ -364,7 +396,7 @@ module ActiveRecord
364
396
  # specified by the +:dependent+ option. If no +:dependent+ option is given,
365
397
  # then it will follow the default strategy.
366
398
  #
367
- # For +has_many :through+ associations, the default deletion strategy is
399
+ # For <tt>has_many :through</tt> associations, the default deletion strategy is
368
400
  # +:delete_all+.
369
401
  #
370
402
  # For +has_many+ associations, the default deletion strategy is +:nullify+.
@@ -399,7 +431,7 @@ module ActiveRecord
399
431
  # # #<Pet id: 3, name: "Choo-Choo", person_id: nil>
400
432
  # # ]
401
433
  #
402
- # 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
403
435
  # +:delete_all+ strategy if the +:dependent+ option is set to +:destroy+.
404
436
  # Records are not instantiated and callbacks will not be fired.
405
437
  #
@@ -418,7 +450,7 @@ module ActiveRecord
418
450
  # person.pets.delete_all
419
451
  #
420
452
  # Pet.find(1, 2, 3)
421
- # # => ActiveRecord::RecordNotFound
453
+ # # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
422
454
  #
423
455
  # If it is set to <tt>:delete_all</tt>, all the objects are deleted
424
456
  # *without* calling their +destroy+ method.
@@ -438,9 +470,9 @@ module ActiveRecord
438
470
  # person.pets.delete_all
439
471
  #
440
472
  # Pet.find(1, 2, 3)
441
- # # => ActiveRecord::RecordNotFound
473
+ # # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
442
474
  def delete_all(dependent = nil)
443
- @association.delete_all(dependent)
475
+ @association.delete_all(dependent).tap { reset_scope }
444
476
  end
445
477
 
446
478
  # Deletes the records of the collection directly from the database
@@ -467,7 +499,7 @@ module ActiveRecord
467
499
  #
468
500
  # Pet.find(1) # => Couldn't find Pet with id=1
469
501
  def destroy_all
470
- @association.destroy_all
502
+ @association.destroy_all.tap { reset_scope }
471
503
  end
472
504
 
473
505
  # Deletes the +records+ supplied from the collection according to the strategy
@@ -475,7 +507,7 @@ module ActiveRecord
475
507
  # then it will follow the default strategy. Returns an array with the
476
508
  # deleted records.
477
509
  #
478
- # For +has_many :through+ associations, the default deletion strategy is
510
+ # For <tt>has_many :through</tt> associations, the default deletion strategy is
479
511
  # +:delete_all+.
480
512
  #
481
513
  # For +has_many+ associations, the default deletion strategy is +:nullify+.
@@ -532,7 +564,7 @@ module ActiveRecord
532
564
  # # => [#<Pet id: 2, name: "Spook", person_id: 1>]
533
565
  #
534
566
  # Pet.find(1, 3)
535
- # # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (1, 3)
567
+ # # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 3)
536
568
  #
537
569
  # If it is set to <tt>:delete_all</tt>, all the +records+ are deleted
538
570
  # *without* calling their +destroy+ method.
@@ -560,7 +592,7 @@ module ActiveRecord
560
592
  # # ]
561
593
  #
562
594
  # Pet.find(1)
563
- # # => ActiveRecord::RecordNotFound: Couldn't find Pet with id=1
595
+ # # => ActiveRecord::RecordNotFound: Couldn't find Pet with 'id'=1
564
596
  #
565
597
  # You can pass +Integer+ or +String+ values, it finds the records
566
598
  # responding to the +id+ and executes delete on them.
@@ -586,7 +618,7 @@ module ActiveRecord
586
618
  # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
587
619
  # # ]
588
620
  def delete(*records)
589
- @association.delete(*records)
621
+ @association.delete(*records).tap { reset_scope }
590
622
  end
591
623
 
592
624
  # Destroys the +records+ supplied and removes them from the collection.
@@ -624,7 +656,7 @@ module ActiveRecord
624
656
  # person.pets.size # => 0
625
657
  # person.pets # => []
626
658
  #
627
- # 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)
628
660
  #
629
661
  # You can pass +Integer+ or +String+ values, it finds the records
630
662
  # responding to the +id+ and then deletes them from the database.
@@ -656,11 +688,17 @@ module ActiveRecord
656
688
  # person.pets.size # => 0
657
689
  # person.pets # => []
658
690
  #
659
- # 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)
660
692
  def destroy(*records)
661
- @association.destroy(*records)
693
+ @association.destroy(*records).tap { reset_scope }
662
694
  end
663
695
 
696
+ ##
697
+ # :method: distinct
698
+ #
699
+ # :call-seq:
700
+ # distinct(value = true)
701
+ #
664
702
  # Specifies whether the records should be unique or not.
665
703
  #
666
704
  # class Person < ActiveRecord::Base
@@ -675,17 +713,35 @@ module ActiveRecord
675
713
  #
676
714
  # person.pets.select(:name).distinct
677
715
  # # => [#<Pet name: "Fancy-Fancy">]
678
- def distinct
679
- @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
726
+ end
727
+
728
+ def pluck(*column_names)
729
+ null_scope? ? scope.pluck(*column_names) : super
680
730
  end
681
- alias uniq distinct
682
731
 
683
- # Count all records using SQL.
732
+ ##
733
+ # :method: count
734
+ #
735
+ # :call-seq:
736
+ # count(column_name = nil, &block)
737
+ #
738
+ # Count all records.
684
739
  #
685
740
  # class Person < ActiveRecord::Base
686
741
  # has_many :pets
687
742
  # end
688
743
  #
744
+ # # This will perform the count using SQL.
689
745
  # person.pets.count # => 3
690
746
  # person.pets
691
747
  # # => [
@@ -693,11 +749,11 @@ module ActiveRecord
693
749
  # # #<Pet id: 2, name: "Spook", person_id: 1>,
694
750
  # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
695
751
  # # ]
696
- def count(column_name = nil, options = {})
697
- # TODO: Remove options argument as soon we remove support to
698
- # activerecord-deprecated_finders.
699
- @association.count(column_name, options)
700
- 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
701
757
 
702
758
  # Returns the size of the collection. If the collection hasn't been loaded,
703
759
  # it executes a <tt>SELECT COUNT(*)</tt> query. Else it calls <tt>collection.size</tt>.
@@ -727,6 +783,12 @@ module ActiveRecord
727
783
  @association.size
728
784
  end
729
785
 
786
+ ##
787
+ # :method: length
788
+ #
789
+ # :call-seq:
790
+ # length()
791
+ #
730
792
  # Returns the size of the collection calling +size+ on the target.
731
793
  # If the collection has been already loaded, +length+ and +size+ are
732
794
  # equivalent. If not and you are going to need the records anyway this
@@ -747,14 +809,11 @@ module ActiveRecord
747
809
  # # #<Pet id: 2, name: "Spook", person_id: 1>,
748
810
  # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
749
811
  # # ]
750
- def length
751
- @association.length
752
- end
753
812
 
754
813
  # Returns +true+ if the collection is empty. If the collection has been
755
814
  # loaded it is equivalent
756
815
  # to <tt>collection.size.zero?</tt>. If the collection has not been loaded,
757
- # 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
758
817
  # not already been loaded and you are going to fetch the records anyway it
759
818
  # is better to check <tt>collection.length.zero?</tt>.
760
819
  #
@@ -773,6 +832,12 @@ module ActiveRecord
773
832
  @association.empty?
774
833
  end
775
834
 
835
+ ##
836
+ # :method: any?
837
+ #
838
+ # :call-seq:
839
+ # any?()
840
+ #
776
841
  # Returns +true+ if the collection is not empty.
777
842
  #
778
843
  # class Person < ActiveRecord::Base
@@ -783,7 +848,7 @@ module ActiveRecord
783
848
  # person.pets.any? # => false
784
849
  #
785
850
  # person.pets << Pet.new(name: 'Snoop')
786
- # person.pets.count # => 0
851
+ # person.pets.count # => 1
787
852
  # person.pets.any? # => true
788
853
  #
789
854
  # You can also pass a +block+ to define criteria. The behavior
@@ -802,10 +867,13 @@ module ActiveRecord
802
867
  # pet.group == 'dogs'
803
868
  # end
804
869
  # # => true
805
- def any?(&block)
806
- @association.any?(&block)
807
- end
808
870
 
871
+ ##
872
+ # :method: many?
873
+ #
874
+ # :call-seq:
875
+ # many?()
876
+ #
809
877
  # Returns true if the collection has more than one record.
810
878
  # Equivalent to <tt>collection.size > 1</tt>.
811
879
  #
@@ -840,9 +908,6 @@ module ActiveRecord
840
908
  # pet.group == 'cats'
841
909
  # end
842
910
  # # => true
843
- def many?(&block)
844
- @association.many?(&block)
845
- end
846
911
 
847
912
  # Returns +true+ if the given +record+ is present in the collection.
848
913
  #
@@ -858,27 +923,14 @@ module ActiveRecord
858
923
  !!@association.include?(record)
859
924
  end
860
925
 
861
- def arel
862
- scope.arel
863
- end
864
-
865
926
  def proxy_association
866
927
  @association
867
928
  end
868
929
 
869
- # We don't want this object to be put on the scoping stack, because
870
- # that could create an infinite loop where we call an @association
871
- # method, which gets the current scope, which is this object, which
872
- # delegates to @association, and so on.
873
- def scoping
874
- @association.scope.scoping { yield }
875
- end
876
-
877
930
  # Returns a <tt>Relation</tt> object for the records in this association
878
931
  def scope
879
- @association.scope
932
+ @scope ||= @association.scope
880
933
  end
881
- alias spawn scope
882
934
 
883
935
  # Equivalent to <tt>Array#==</tt>. Returns +true+ if the two arrays
884
936
  # contain the same number of elements and if each element is equal
@@ -908,6 +960,12 @@ module ActiveRecord
908
960
  load_target == other
909
961
  end
910
962
 
963
+ ##
964
+ # :method: to_ary
965
+ #
966
+ # :call-seq:
967
+ # to_ary()
968
+ #
911
969
  # Returns a new array of objects from the collection. If the collection
912
970
  # hasn't been loaded, it fetches the records from the database.
913
971
  #
@@ -941,14 +999,15 @@ module ActiveRecord
941
999
  # # #<Pet id: 5, name: "Brain", person_id: 1>,
942
1000
  # # #<Pet id: 6, name: "Boss", person_id: 1>
943
1001
  # # ]
944
- def to_ary
945
- load_target.dup
1002
+
1003
+ def records # :nodoc:
1004
+ load_target
946
1005
  end
947
- alias_method :to_a, :to_ary
948
1006
 
949
1007
  # Adds one or more +records+ to the collection by setting their foreign keys
950
- # to the association's primary key. Returns +self+, so several appends may be
951
- # 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.
952
1011
  #
953
1012
  # class Person < ActiveRecord::Base
954
1013
  # has_many :pets
@@ -971,21 +1030,24 @@ module ActiveRecord
971
1030
  end
972
1031
  alias_method :push, :<<
973
1032
  alias_method :append, :<<
1033
+ alias_method :concat, :<<
974
1034
 
975
1035
  def prepend(*args)
976
- 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"
977
1037
  end
978
1038
 
979
1039
  # Equivalent to +delete_all+. The difference is that returns +self+, instead
980
1040
  # of an array with the deleted objects, so methods can be chained. See
981
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.
982
1045
  def clear
983
1046
  delete_all
984
1047
  self
985
1048
  end
986
1049
 
987
1050
  # Reloads the collection from the database. Returns +self+.
988
- # Equivalent to <tt>collection(true)</tt>.
989
1051
  #
990
1052
  # class Person < ActiveRecord::Base
991
1053
  # has_many :pets
@@ -999,12 +1061,9 @@ module ActiveRecord
999
1061
  #
1000
1062
  # person.pets.reload # fetches pets from the database
1001
1063
  # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
1002
- #
1003
- # person.pets(true) # fetches pets from the database
1004
- # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
1005
1064
  def reload
1006
1065
  proxy_association.reload
1007
- self
1066
+ reset_scope
1008
1067
  end
1009
1068
 
1010
1069
  # Unloads the association. Returns +self+.
@@ -1026,8 +1085,47 @@ module ActiveRecord
1026
1085
  def reset
1027
1086
  proxy_association.reset
1028
1087
  proxy_association.reset_scope
1088
+ reset_scope
1089
+ end
1090
+
1091
+ def reset_scope # :nodoc:
1092
+ @offsets = {}
1093
+ @scope = nil
1029
1094
  self
1030
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
1031
1129
  end
1032
1130
  end
1033
1131
  end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord::Associations
2
- module ForeignAssociation
4
+ module ForeignAssociation # :nodoc:
3
5
  def foreign_key_present?
4
6
  if reflection.klass.primary_key
5
7
  owner.attribute_present?(reflection.active_record_primary_key)