activerecord 3.0.0 → 4.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 (181) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +2102 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +35 -44
  5. data/examples/performance.rb +110 -100
  6. data/lib/active_record/aggregations.rb +59 -75
  7. data/lib/active_record/associations/alias_tracker.rb +76 -0
  8. data/lib/active_record/associations/association.rb +248 -0
  9. data/lib/active_record/associations/association_scope.rb +135 -0
  10. data/lib/active_record/associations/belongs_to_association.rb +60 -59
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +16 -59
  12. data/lib/active_record/associations/builder/association.rb +108 -0
  13. data/lib/active_record/associations/builder/belongs_to.rb +98 -0
  14. data/lib/active_record/associations/builder/collection_association.rb +89 -0
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +39 -0
  16. data/lib/active_record/associations/builder/has_many.rb +15 -0
  17. data/lib/active_record/associations/builder/has_one.rb +25 -0
  18. data/lib/active_record/associations/builder/singular_association.rb +32 -0
  19. data/lib/active_record/associations/collection_association.rb +608 -0
  20. data/lib/active_record/associations/collection_proxy.rb +986 -0
  21. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +40 -112
  22. data/lib/active_record/associations/has_many_association.rb +83 -76
  23. data/lib/active_record/associations/has_many_through_association.rb +147 -66
  24. data/lib/active_record/associations/has_one_association.rb +67 -108
  25. data/lib/active_record/associations/has_one_through_association.rb +21 -25
  26. data/lib/active_record/associations/join_dependency/join_association.rb +174 -0
  27. data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
  28. data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
  29. data/lib/active_record/associations/join_dependency.rb +235 -0
  30. data/lib/active_record/associations/join_helper.rb +45 -0
  31. data/lib/active_record/associations/preloader/association.rb +121 -0
  32. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  33. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  34. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
  35. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  36. data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
  37. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  38. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  39. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  40. data/lib/active_record/associations/preloader/through_association.rb +63 -0
  41. data/lib/active_record/associations/preloader.rb +178 -0
  42. data/lib/active_record/associations/singular_association.rb +64 -0
  43. data/lib/active_record/associations/through_association.rb +87 -0
  44. data/lib/active_record/associations.rb +512 -1224
  45. data/lib/active_record/attribute_assignment.rb +201 -0
  46. data/lib/active_record/attribute_methods/before_type_cast.rb +49 -12
  47. data/lib/active_record/attribute_methods/dirty.rb +51 -28
  48. data/lib/active_record/attribute_methods/primary_key.rb +94 -22
  49. data/lib/active_record/attribute_methods/query.rb +5 -4
  50. data/lib/active_record/attribute_methods/read.rb +63 -72
  51. data/lib/active_record/attribute_methods/serialization.rb +162 -0
  52. data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -41
  53. data/lib/active_record/attribute_methods/write.rb +39 -13
  54. data/lib/active_record/attribute_methods.rb +362 -29
  55. data/lib/active_record/autosave_association.rb +132 -75
  56. data/lib/active_record/base.rb +83 -1627
  57. data/lib/active_record/callbacks.rb +69 -47
  58. data/lib/active_record/coders/yaml_column.rb +38 -0
  59. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +411 -138
  60. data/lib/active_record/connection_adapters/abstract/database_limits.rb +21 -11
  61. data/lib/active_record/connection_adapters/abstract/database_statements.rb +234 -173
  62. data/lib/active_record/connection_adapters/abstract/query_cache.rb +36 -22
  63. data/lib/active_record/connection_adapters/abstract/quoting.rb +82 -25
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +176 -414
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +562 -232
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +281 -53
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +782 -0
  70. data/lib/active_record/connection_adapters/column.rb +318 -0
  71. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  72. data/lib/active_record/connection_adapters/mysql2_adapter.rb +273 -0
  73. data/lib/active_record/connection_adapters/mysql_adapter.rb +365 -450
  74. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
  75. data/lib/active_record/connection_adapters/postgresql/cast.rb +152 -0
  76. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid.rb +366 -0
  78. data/lib/active_record/connection_adapters/postgresql/quoting.rb +171 -0
  79. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  80. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +489 -0
  81. data/lib/active_record/connection_adapters/postgresql_adapter.rb +672 -752
  82. data/lib/active_record/connection_adapters/schema_cache.rb +129 -0
  83. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +588 -17
  84. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  85. data/lib/active_record/connection_handling.rb +98 -0
  86. data/lib/active_record/core.rb +463 -0
  87. data/lib/active_record/counter_cache.rb +108 -101
  88. data/lib/active_record/dynamic_matchers.rb +131 -0
  89. data/lib/active_record/errors.rb +54 -13
  90. data/lib/active_record/explain.rb +38 -0
  91. data/lib/active_record/explain_registry.rb +30 -0
  92. data/lib/active_record/explain_subscriber.rb +29 -0
  93. data/lib/active_record/fixture_set/file.rb +55 -0
  94. data/lib/active_record/fixtures.rb +703 -785
  95. data/lib/active_record/inheritance.rb +200 -0
  96. data/lib/active_record/integration.rb +60 -0
  97. data/lib/active_record/locale/en.yml +8 -1
  98. data/lib/active_record/locking/optimistic.rb +69 -60
  99. data/lib/active_record/locking/pessimistic.rb +34 -12
  100. data/lib/active_record/log_subscriber.rb +40 -6
  101. data/lib/active_record/migration/command_recorder.rb +164 -0
  102. data/lib/active_record/migration/join_table.rb +15 -0
  103. data/lib/active_record/migration.rb +614 -216
  104. data/lib/active_record/model_schema.rb +345 -0
  105. data/lib/active_record/nested_attributes.rb +248 -119
  106. data/lib/active_record/null_relation.rb +65 -0
  107. data/lib/active_record/persistence.rb +275 -57
  108. data/lib/active_record/query_cache.rb +29 -9
  109. data/lib/active_record/querying.rb +62 -0
  110. data/lib/active_record/railtie.rb +135 -21
  111. data/lib/active_record/railties/console_sandbox.rb +5 -0
  112. data/lib/active_record/railties/controller_runtime.rb +17 -5
  113. data/lib/active_record/railties/databases.rake +249 -359
  114. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  115. data/lib/active_record/readonly_attributes.rb +30 -0
  116. data/lib/active_record/reflection.rb +283 -103
  117. data/lib/active_record/relation/batches.rb +38 -34
  118. data/lib/active_record/relation/calculations.rb +252 -139
  119. data/lib/active_record/relation/delegation.rb +125 -0
  120. data/lib/active_record/relation/finder_methods.rb +182 -188
  121. data/lib/active_record/relation/merger.rb +161 -0
  122. data/lib/active_record/relation/predicate_builder.rb +86 -21
  123. data/lib/active_record/relation/query_methods.rb +917 -134
  124. data/lib/active_record/relation/spawn_methods.rb +53 -92
  125. data/lib/active_record/relation.rb +405 -143
  126. data/lib/active_record/result.rb +67 -0
  127. data/lib/active_record/runtime_registry.rb +17 -0
  128. data/lib/active_record/sanitization.rb +168 -0
  129. data/lib/active_record/schema.rb +20 -14
  130. data/lib/active_record/schema_dumper.rb +55 -46
  131. data/lib/active_record/schema_migration.rb +39 -0
  132. data/lib/active_record/scoping/default.rb +146 -0
  133. data/lib/active_record/scoping/named.rb +175 -0
  134. data/lib/active_record/scoping.rb +82 -0
  135. data/lib/active_record/serialization.rb +8 -46
  136. data/lib/active_record/serializers/xml_serializer.rb +21 -68
  137. data/lib/active_record/statement_cache.rb +26 -0
  138. data/lib/active_record/store.rb +156 -0
  139. data/lib/active_record/tasks/database_tasks.rb +203 -0
  140. data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
  141. data/lib/active_record/tasks/mysql_database_tasks.rb +143 -0
  142. data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
  143. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  144. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  145. data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
  146. data/lib/active_record/test_case.rb +57 -28
  147. data/lib/active_record/timestamp.rb +49 -18
  148. data/lib/active_record/transactions.rb +106 -63
  149. data/lib/active_record/translation.rb +22 -0
  150. data/lib/active_record/validations/associated.rb +25 -24
  151. data/lib/active_record/validations/presence.rb +65 -0
  152. data/lib/active_record/validations/uniqueness.rb +123 -83
  153. data/lib/active_record/validations.rb +29 -29
  154. data/lib/active_record/version.rb +7 -5
  155. data/lib/active_record.rb +83 -34
  156. data/lib/rails/generators/active_record/migration/migration_generator.rb +46 -9
  157. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
  158. data/lib/rails/generators/active_record/migration/templates/migration.rb +30 -8
  159. data/lib/rails/generators/active_record/model/model_generator.rb +15 -5
  160. data/lib/rails/generators/active_record/model/templates/model.rb +7 -2
  161. data/lib/rails/generators/active_record/model/templates/module.rb +3 -1
  162. data/lib/rails/generators/active_record.rb +4 -8
  163. metadata +163 -121
  164. data/CHANGELOG +0 -6023
  165. data/examples/associations.png +0 -0
  166. data/lib/active_record/association_preload.rb +0 -403
  167. data/lib/active_record/associations/association_collection.rb +0 -562
  168. data/lib/active_record/associations/association_proxy.rb +0 -295
  169. data/lib/active_record/associations/through_association_scope.rb +0 -154
  170. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -113
  171. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -401
  172. data/lib/active_record/dynamic_finder_match.rb +0 -53
  173. data/lib/active_record/dynamic_scope_match.rb +0 -32
  174. data/lib/active_record/named_scope.rb +0 -138
  175. data/lib/active_record/observer.rb +0 -140
  176. data/lib/active_record/session_store.rb +0 -340
  177. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -16
  178. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  179. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -2
  180. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -24
  181. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -16
@@ -0,0 +1,986 @@
1
+ module ActiveRecord
2
+ module Associations
3
+ # Association proxies in Active Record are middlemen between the object that
4
+ # holds the association, known as the <tt>@owner</tt>, and the actual associated
5
+ # object, known as the <tt>@target</tt>. The kind of association any proxy is
6
+ # about is available in <tt>@reflection</tt>. That's an instance of the class
7
+ # ActiveRecord::Reflection::AssociationReflection.
8
+ #
9
+ # For example, given
10
+ #
11
+ # class Blog < ActiveRecord::Base
12
+ # has_many :posts
13
+ # end
14
+ #
15
+ # blog = Blog.first
16
+ #
17
+ # the association proxy in <tt>blog.posts</tt> has the object in +blog+ as
18
+ # <tt>@owner</tt>, the collection of its posts as <tt>@target</tt>, and
19
+ # the <tt>@reflection</tt> object represents a <tt>:has_many</tt> macro.
20
+ #
21
+ # This class delegates unknown methods to <tt>@target</tt> via
22
+ # <tt>method_missing</tt>.
23
+ #
24
+ # The <tt>@target</tt> object is not \loaded until needed. For example,
25
+ #
26
+ # blog.posts.count
27
+ #
28
+ # is computed directly through SQL and does not trigger by itself the
29
+ # instantiation of the actual post records.
30
+ class CollectionProxy < Relation
31
+ delegate(*(ActiveRecord::Calculations.public_instance_methods - [:count]), to: :scope)
32
+
33
+ def initialize(klass, association) #:nodoc:
34
+ @association = association
35
+ super klass, klass.arel_table
36
+ self.default_scoped = true
37
+ merge! association.scope(nullify: false)
38
+ end
39
+
40
+ def target
41
+ @association.target
42
+ end
43
+
44
+ def load_target
45
+ @association.load_target
46
+ end
47
+
48
+ # Returns +true+ if the association has been loaded, otherwise +false+.
49
+ #
50
+ # person.pets.loaded? # => false
51
+ # person.pets
52
+ # person.pets.loaded? # => true
53
+ def loaded?
54
+ @association.loaded?
55
+ end
56
+
57
+ # Works in two ways.
58
+ #
59
+ # *First:* Specify a subset of fields to be selected from the result set.
60
+ #
61
+ # class Person < ActiveRecord::Base
62
+ # has_many :pets
63
+ # end
64
+ #
65
+ # person.pets
66
+ # # => [
67
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
68
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
69
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
70
+ # # ]
71
+ #
72
+ # person.pets.select(:name)
73
+ # # => [
74
+ # # #<Pet id: nil, name: "Fancy-Fancy">,
75
+ # # #<Pet id: nil, name: "Spook">,
76
+ # # #<Pet id: nil, name: "Choo-Choo">
77
+ # # ]
78
+ #
79
+ # person.pets.select([:id, :name])
80
+ # # => [
81
+ # # #<Pet id: 1, name: "Fancy-Fancy">,
82
+ # # #<Pet id: 2, name: "Spook">,
83
+ # # #<Pet id: 3, name: "Choo-Choo">
84
+ # # ]
85
+ #
86
+ # Be careful because this also means you're initializing a model
87
+ # object with only the fields that you've selected. If you attempt
88
+ # to access a field that is not in the initialized record you'll
89
+ # receive:
90
+ #
91
+ # person.pets.select(:name).first.person_id
92
+ # # => ActiveModel::MissingAttributeError: missing attribute: person_id
93
+ #
94
+ # *Second:* You can pass a block so it can be used just like Array#select.
95
+ # This builds an array of objects from the database for the scope,
96
+ # converting them into an array and iterating through them using
97
+ # Array#select.
98
+ #
99
+ # person.pets.select { |pet| pet.name =~ /oo/ }
100
+ # # => [
101
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
102
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
103
+ # # ]
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(select = nil, &block)
111
+ @association.select(select, &block)
112
+ end
113
+
114
+ # 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>
116
+ # error if the object can not be found.
117
+ #
118
+ # class Person < ActiveRecord::Base
119
+ # has_many :pets
120
+ # end
121
+ #
122
+ # person.pets
123
+ # # => [
124
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
125
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
126
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
127
+ # # ]
128
+ #
129
+ # 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
131
+ #
132
+ # person.pets.find(2) { |pet| pet.name.downcase! }
133
+ # # => #<Pet id: 2, name: "fancy-fancy", person_id: 1>
134
+ #
135
+ # person.pets.find(2, 3)
136
+ # # => [
137
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
138
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
139
+ # # ]
140
+ def find(*args, &block)
141
+ @association.find(*args, &block)
142
+ end
143
+
144
+ # Returns the first record, or the first +n+ records, from the collection.
145
+ # If the collection is empty, the first form returns +nil+, and the second
146
+ # form returns an empty array.
147
+ #
148
+ # class Person < ActiveRecord::Base
149
+ # has_many :pets
150
+ # end
151
+ #
152
+ # person.pets
153
+ # # => [
154
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
155
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
156
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
157
+ # # ]
158
+ #
159
+ # person.pets.first # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
160
+ #
161
+ # person.pets.first(2)
162
+ # # => [
163
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
164
+ # # #<Pet id: 2, name: "Spook", person_id: 1>
165
+ # # ]
166
+ #
167
+ # another_person_without.pets # => []
168
+ # another_person_without.pets.first # => nil
169
+ # another_person_without.pets.first(3) # => []
170
+ def first(*args)
171
+ @association.first(*args)
172
+ end
173
+
174
+ # Returns the last record, or the last +n+ records, from the collection.
175
+ # If the collection is empty, the first form returns +nil+, and the second
176
+ # form returns an empty array.
177
+ #
178
+ # class Person < ActiveRecord::Base
179
+ # has_many :pets
180
+ # end
181
+ #
182
+ # person.pets
183
+ # # => [
184
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
185
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
186
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
187
+ # # ]
188
+ #
189
+ # person.pets.last # => #<Pet id: 3, name: "Choo-Choo", person_id: 1>
190
+ #
191
+ # person.pets.last(2)
192
+ # # => [
193
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
194
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
195
+ # # ]
196
+ #
197
+ # another_person_without.pets # => []
198
+ # another_person_without.pets.last # => nil
199
+ # another_person_without.pets.last(3) # => []
200
+ def last(*args)
201
+ @association.last(*args)
202
+ end
203
+
204
+ # Returns a new object of the collection type that has been instantiated
205
+ # with +attributes+ and linked to this object, but have not yet been saved.
206
+ # You can pass an array of attributes hashes, this will return an array
207
+ # with the new objects.
208
+ #
209
+ # class Person
210
+ # has_many :pets
211
+ # end
212
+ #
213
+ # person.pets.build
214
+ # # => #<Pet id: nil, name: nil, person_id: 1>
215
+ #
216
+ # person.pets.build(name: 'Fancy-Fancy')
217
+ # # => #<Pet id: nil, name: "Fancy-Fancy", person_id: 1>
218
+ #
219
+ # person.pets.build([{name: 'Spook'}, {name: 'Choo-Choo'}, {name: 'Brain'}])
220
+ # # => [
221
+ # # #<Pet id: nil, name: "Spook", person_id: 1>,
222
+ # # #<Pet id: nil, name: "Choo-Choo", person_id: 1>,
223
+ # # #<Pet id: nil, name: "Brain", person_id: 1>
224
+ # # ]
225
+ #
226
+ # person.pets.size # => 5 # size of the collection
227
+ # person.pets.count # => 0 # count from database
228
+ def build(attributes = {}, &block)
229
+ @association.build(attributes, &block)
230
+ end
231
+ alias_method :new, :build
232
+
233
+ # Returns a new object of the collection type that has been instantiated with
234
+ # attributes, linked to this object and that has already been saved (if it
235
+ # passes the validations).
236
+ #
237
+ # class Person
238
+ # has_many :pets
239
+ # end
240
+ #
241
+ # person.pets.create(name: 'Fancy-Fancy')
242
+ # # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
243
+ #
244
+ # person.pets.create([{name: 'Spook'}, {name: 'Choo-Choo'}])
245
+ # # => [
246
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
247
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
248
+ # # ]
249
+ #
250
+ # person.pets.size # => 3
251
+ # person.pets.count # => 3
252
+ #
253
+ # person.pets.find(1, 2, 3)
254
+ # # => [
255
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
256
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
257
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
258
+ # # ]
259
+ def create(attributes = {}, &block)
260
+ @association.create(attributes, &block)
261
+ end
262
+
263
+ # Like +create+, except that if the record is invalid, raises an exception.
264
+ #
265
+ # class Person
266
+ # has_many :pets
267
+ # end
268
+ #
269
+ # class Pet
270
+ # validates :name, presence: true
271
+ # end
272
+ #
273
+ # person.pets.create!(name: nil)
274
+ # # => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
275
+ def create!(attributes = {}, &block)
276
+ @association.create!(attributes, &block)
277
+ end
278
+
279
+ # Add one or more records to the collection by setting their foreign keys
280
+ # to the association's primary key. Since << flattens its argument list and
281
+ # inserts each record, +push+ and +concat+ behave identically. Returns +self+
282
+ # so method calls may be chained.
283
+ #
284
+ # class Person < ActiveRecord::Base
285
+ # pets :has_many
286
+ # end
287
+ #
288
+ # person.pets.size # => 0
289
+ # person.pets.concat(Pet.new(name: 'Fancy-Fancy'))
290
+ # person.pets.concat(Pet.new(name: 'Spook'), Pet.new(name: 'Choo-Choo'))
291
+ # person.pets.size # => 3
292
+ #
293
+ # person.id # => 1
294
+ # person.pets
295
+ # # => [
296
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
297
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
298
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
299
+ # # ]
300
+ #
301
+ # person.pets.concat([Pet.new(name: 'Brain'), Pet.new(name: 'Benny')])
302
+ # person.pets.size # => 5
303
+ def concat(*records)
304
+ @association.concat(*records)
305
+ end
306
+
307
+ # Replaces this collection with +other_array+. This will perform a diff
308
+ # and delete/add only records that have changed.
309
+ #
310
+ # class Person < ActiveRecord::Base
311
+ # has_many :pets
312
+ # end
313
+ #
314
+ # person.pets
315
+ # # => [#<Pet id: 1, name: "Gorby", group: "cats", person_id: 1>]
316
+ #
317
+ # other_pets = [Pet.new(name: 'Puff', group: 'celebrities']
318
+ #
319
+ # person.pets.replace(other_pets)
320
+ #
321
+ # person.pets
322
+ # # => [#<Pet id: 2, name: "Puff", group: "celebrities", person_id: 1>]
323
+ #
324
+ # If the supplied array has an incorrect association type, it raises
325
+ # an <tt>ActiveRecord::AssociationTypeMismatch</tt> error:
326
+ #
327
+ # person.pets.replace(["doo", "ggie", "gaga"])
328
+ # # => ActiveRecord::AssociationTypeMismatch: Pet expected, got String
329
+ def replace(other_array)
330
+ @association.replace(other_array)
331
+ end
332
+
333
+ # Deletes all the records from the collection. For +has_many+ associations,
334
+ # the deletion is done according to the strategy specified by the <tt>:dependent</tt>
335
+ # option. Returns an array with the deleted records.
336
+ #
337
+ # If no <tt>:dependent</tt> option is given, then it will follow the
338
+ # default strategy. The default strategy is <tt>:nullify</tt>. This
339
+ # sets the foreign keys to <tt>NULL</tt>. For, +has_many+ <tt>:through</tt>,
340
+ # the default strategy is +delete_all+.
341
+ #
342
+ # class Person < ActiveRecord::Base
343
+ # has_many :pets # dependent: :nullify option by default
344
+ # end
345
+ #
346
+ # person.pets.size # => 3
347
+ # person.pets
348
+ # # => [
349
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
350
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
351
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
352
+ # # ]
353
+ #
354
+ # person.pets.delete_all
355
+ # # => [
356
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
357
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
358
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
359
+ # # ]
360
+ #
361
+ # person.pets.size # => 0
362
+ # person.pets # => []
363
+ #
364
+ # Pet.find(1, 2, 3)
365
+ # # => [
366
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: nil>,
367
+ # # #<Pet id: 2, name: "Spook", person_id: nil>,
368
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: nil>
369
+ # # ]
370
+ #
371
+ # If it is set to <tt>:destroy</tt> all the objects from the collection
372
+ # are removed by calling their +destroy+ method. See +destroy+ for more
373
+ # information.
374
+ #
375
+ # class Person < ActiveRecord::Base
376
+ # has_many :pets, dependent: :destroy
377
+ # end
378
+ #
379
+ # person.pets.size # => 3
380
+ # person.pets
381
+ # # => [
382
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
383
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
384
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
385
+ # # ]
386
+ #
387
+ # person.pets.delete_all
388
+ # # => [
389
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
390
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
391
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
392
+ # # ]
393
+ #
394
+ # Pet.find(1, 2, 3)
395
+ # # => ActiveRecord::RecordNotFound
396
+ #
397
+ # If it is set to <tt>:delete_all</tt>, all the objects are deleted
398
+ # *without* calling their +destroy+ method.
399
+ #
400
+ # class Person < ActiveRecord::Base
401
+ # has_many :pets, dependent: :delete_all
402
+ # end
403
+ #
404
+ # person.pets.size # => 3
405
+ # person.pets
406
+ # # => [
407
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
408
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
409
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
410
+ # # ]
411
+ #
412
+ # person.pets.delete_all
413
+ # # => [
414
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
415
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
416
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
417
+ # # ]
418
+ #
419
+ # Pet.find(1, 2, 3)
420
+ # # => ActiveRecord::RecordNotFound
421
+ def delete_all
422
+ @association.delete_all
423
+ end
424
+
425
+ # Deletes the records of the collection directly from the database.
426
+ # This will _always_ remove the records ignoring the +:dependent+
427
+ # option.
428
+ #
429
+ # class Person < ActiveRecord::Base
430
+ # has_many :pets
431
+ # end
432
+ #
433
+ # person.pets.size # => 3
434
+ # person.pets
435
+ # # => [
436
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
437
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
438
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
439
+ # # ]
440
+ #
441
+ # person.pets.destroy_all
442
+ #
443
+ # person.pets.size # => 0
444
+ # person.pets # => []
445
+ #
446
+ # Pet.find(1) # => Couldn't find Pet with id=1
447
+ def destroy_all
448
+ @association.destroy_all
449
+ end
450
+
451
+ # Deletes the +records+ supplied and removes them from the collection. For
452
+ # +has_many+ associations, the deletion is done according to the strategy
453
+ # specified by the <tt>:dependent</tt> option. Returns an array with the
454
+ # deleted records.
455
+ #
456
+ # If no <tt>:dependent</tt> option is given, then it will follow the default
457
+ # strategy. The default strategy is <tt>:nullify</tt>. This sets the foreign
458
+ # keys to <tt>NULL</tt>. For, +has_many+ <tt>:through</tt>, the default
459
+ # strategy is +delete_all+.
460
+ #
461
+ # class Person < ActiveRecord::Base
462
+ # has_many :pets # dependent: :nullify option by default
463
+ # end
464
+ #
465
+ # person.pets.size # => 3
466
+ # person.pets
467
+ # # => [
468
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
469
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
470
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
471
+ # # ]
472
+ #
473
+ # person.pets.delete(Pet.find(1))
474
+ # # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
475
+ #
476
+ # person.pets.size # => 2
477
+ # person.pets
478
+ # # => [
479
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
480
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
481
+ # # ]
482
+ #
483
+ # Pet.find(1)
484
+ # # => #<Pet id: 1, name: "Fancy-Fancy", person_id: nil>
485
+ #
486
+ # If it is set to <tt>:destroy</tt> all the +records+ are removed by calling
487
+ # their +destroy+ method. See +destroy+ for more information.
488
+ #
489
+ # class Person < ActiveRecord::Base
490
+ # has_many :pets, dependent: :destroy
491
+ # end
492
+ #
493
+ # person.pets.size # => 3
494
+ # person.pets
495
+ # # => [
496
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
497
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
498
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
499
+ # # ]
500
+ #
501
+ # person.pets.delete(Pet.find(1), Pet.find(3))
502
+ # # => [
503
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
504
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
505
+ # # ]
506
+ #
507
+ # person.pets.size # => 1
508
+ # person.pets
509
+ # # => [#<Pet id: 2, name: "Spook", person_id: 1>]
510
+ #
511
+ # Pet.find(1, 3)
512
+ # # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (1, 3)
513
+ #
514
+ # If it is set to <tt>:delete_all</tt>, all the +records+ are deleted
515
+ # *without* calling their +destroy+ method.
516
+ #
517
+ # class Person < ActiveRecord::Base
518
+ # has_many :pets, dependent: :delete_all
519
+ # end
520
+ #
521
+ # person.pets.size # => 3
522
+ # person.pets
523
+ # # => [
524
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
525
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
526
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
527
+ # # ]
528
+ #
529
+ # person.pets.delete(Pet.find(1))
530
+ # # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
531
+ #
532
+ # person.pets.size # => 2
533
+ # person.pets
534
+ # # => [
535
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
536
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
537
+ # # ]
538
+ #
539
+ # Pet.find(1)
540
+ # # => ActiveRecord::RecordNotFound: Couldn't find Pet with id=1
541
+ #
542
+ # You can pass +Fixnum+ or +String+ values, it finds the records
543
+ # responding to the +id+ and executes delete on them.
544
+ #
545
+ # class Person < ActiveRecord::Base
546
+ # has_many :pets
547
+ # end
548
+ #
549
+ # person.pets.size # => 3
550
+ # person.pets
551
+ # # => [
552
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
553
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
554
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
555
+ # # ]
556
+ #
557
+ # person.pets.delete("1")
558
+ # # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
559
+ #
560
+ # person.pets.delete(2, 3)
561
+ # # => [
562
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
563
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
564
+ # # ]
565
+ def delete(*records)
566
+ @association.delete(*records)
567
+ end
568
+
569
+ # Destroys the +records+ supplied and removes them from the collection.
570
+ # This method will _always_ remove record from the database ignoring
571
+ # the +:dependent+ option. Returns an array with the removed records.
572
+ #
573
+ # class Person < ActiveRecord::Base
574
+ # has_many :pets
575
+ # end
576
+ #
577
+ # person.pets.size # => 3
578
+ # person.pets
579
+ # # => [
580
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
581
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
582
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
583
+ # # ]
584
+ #
585
+ # person.pets.destroy(Pet.find(1))
586
+ # # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
587
+ #
588
+ # person.pets.size # => 2
589
+ # person.pets
590
+ # # => [
591
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
592
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
593
+ # # ]
594
+ #
595
+ # person.pets.destroy(Pet.find(2), Pet.find(3))
596
+ # # => [
597
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
598
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
599
+ # # ]
600
+ #
601
+ # person.pets.size # => 0
602
+ # person.pets # => []
603
+ #
604
+ # Pet.find(1, 2, 3) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (1, 2, 3)
605
+ #
606
+ # You can pass +Fixnum+ or +String+ values, it finds the records
607
+ # responding to the +id+ and then deletes them from the database.
608
+ #
609
+ # person.pets.size # => 3
610
+ # person.pets
611
+ # # => [
612
+ # # #<Pet id: 4, name: "Benny", person_id: 1>,
613
+ # # #<Pet id: 5, name: "Brain", person_id: 1>,
614
+ # # #<Pet id: 6, name: "Boss", person_id: 1>
615
+ # # ]
616
+ #
617
+ # person.pets.destroy("4")
618
+ # # => #<Pet id: 4, name: "Benny", person_id: 1>
619
+ #
620
+ # person.pets.size # => 2
621
+ # person.pets
622
+ # # => [
623
+ # # #<Pet id: 5, name: "Brain", person_id: 1>,
624
+ # # #<Pet id: 6, name: "Boss", person_id: 1>
625
+ # # ]
626
+ #
627
+ # person.pets.destroy(5, 6)
628
+ # # => [
629
+ # # #<Pet id: 5, name: "Brain", person_id: 1>,
630
+ # # #<Pet id: 6, name: "Boss", person_id: 1>
631
+ # # ]
632
+ #
633
+ # person.pets.size # => 0
634
+ # person.pets # => []
635
+ #
636
+ # Pet.find(4, 5, 6) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (4, 5, 6)
637
+ def destroy(*records)
638
+ @association.destroy(*records)
639
+ end
640
+
641
+ # Specifies whether the records should be unique or not.
642
+ #
643
+ # class Person < ActiveRecord::Base
644
+ # has_many :pets
645
+ # end
646
+ #
647
+ # person.pets.select(:name)
648
+ # # => [
649
+ # # #<Pet name: "Fancy-Fancy">,
650
+ # # #<Pet name: "Fancy-Fancy">
651
+ # # ]
652
+ #
653
+ # person.pets.select(:name).distinct
654
+ # # => [#<Pet name: "Fancy-Fancy">]
655
+ def distinct
656
+ @association.distinct
657
+ end
658
+ alias uniq distinct
659
+
660
+ # Count all records using SQL.
661
+ #
662
+ # class Person < ActiveRecord::Base
663
+ # has_many :pets
664
+ # end
665
+ #
666
+ # person.pets.count # => 3
667
+ # person.pets
668
+ # # => [
669
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
670
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
671
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
672
+ # # ]
673
+ def count(column_name = nil, options = {})
674
+ @association.count(column_name, options)
675
+ end
676
+
677
+ # Returns the size of the collection. If the collection hasn't been loaded,
678
+ # it executes a <tt>SELECT COUNT(*)</tt> query. Else it calls <tt>collection.size</tt>.
679
+ #
680
+ # If the collection has been already loaded +size+ and +length+ are
681
+ # equivalent. If not and you are going to need the records anyway
682
+ # +length+ will take one less query. Otherwise +size+ is more efficient.
683
+ #
684
+ # class Person < ActiveRecord::Base
685
+ # has_many :pets
686
+ # end
687
+ #
688
+ # person.pets.size # => 3
689
+ # # executes something like SELECT COUNT(*) FROM "pets" WHERE "pets"."person_id" = 1
690
+ #
691
+ # person.pets # This will execute a SELECT * FROM query
692
+ # # => [
693
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
694
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
695
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
696
+ # # ]
697
+ #
698
+ # person.pets.size # => 3
699
+ # # Because the collection is already loaded, this will behave like
700
+ # # collection.size and no SQL count query is executed.
701
+ def size
702
+ @association.size
703
+ end
704
+
705
+ # Returns the size of the collection calling +size+ on the target.
706
+ # If the collection has been already loaded, +length+ and +size+ are
707
+ # equivalent. If not and you are going to need the records anyway this
708
+ # method will take one less query. Otherwise +size+ is more efficient.
709
+ #
710
+ # class Person < ActiveRecord::Base
711
+ # has_many :pets
712
+ # end
713
+ #
714
+ # person.pets.length # => 3
715
+ # # executes something like SELECT "pets".* FROM "pets" WHERE "pets"."person_id" = 1
716
+ #
717
+ # # Because the collection is loaded, you can
718
+ # # call the collection with no additional queries:
719
+ # person.pets
720
+ # # => [
721
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
722
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
723
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
724
+ # # ]
725
+ def length
726
+ @association.length
727
+ end
728
+
729
+ # Returns +true+ if the collection is empty. If the collection has been
730
+ # loaded or the <tt>:counter_sql</tt> option is provided, it is equivalent
731
+ # to <tt>collection.size.zero?</tt>. If the collection has not been loaded,
732
+ # it is equivalent to <tt>collection.exists?</tt>. If the collection has
733
+ # not already been loaded and you are going to fetch the records anyway it
734
+ # is better to check <tt>collection.length.zero?</tt>.
735
+ #
736
+ # class Person < ActiveRecord::Base
737
+ # has_many :pets
738
+ # end
739
+ #
740
+ # person.pets.count # => 1
741
+ # person.pets.empty? # => false
742
+ #
743
+ # person.pets.delete_all
744
+ #
745
+ # person.pets.count # => 0
746
+ # person.pets.empty? # => true
747
+ def empty?
748
+ @association.empty?
749
+ end
750
+
751
+ # Returns +true+ if the collection is not empty.
752
+ #
753
+ # class Person < ActiveRecord::Base
754
+ # has_many :pets
755
+ # end
756
+ #
757
+ # person.pets.count # => 0
758
+ # person.pets.any? # => false
759
+ #
760
+ # person.pets << Pet.new(name: 'Snoop')
761
+ # person.pets.count # => 0
762
+ # person.pets.any? # => true
763
+ #
764
+ # You can also pass a block to define criteria. The behavior
765
+ # is the same, it returns true if the collection based on the
766
+ # criteria is not empty.
767
+ #
768
+ # person.pets
769
+ # # => [#<Pet name: "Snoop", group: "dogs">]
770
+ #
771
+ # person.pets.any? do |pet|
772
+ # pet.group == 'cats'
773
+ # end
774
+ # # => false
775
+ #
776
+ # person.pets.any? do |pet|
777
+ # pet.group == 'dogs'
778
+ # end
779
+ # # => true
780
+ def any?(&block)
781
+ @association.any?(&block)
782
+ end
783
+
784
+ # Returns true if the collection has more than one record.
785
+ # Equivalent to <tt>collection.size > 1</tt>.
786
+ #
787
+ # class Person < ActiveRecord::Base
788
+ # has_many :pets
789
+ # end
790
+ #
791
+ # person.pets.count #=> 1
792
+ # person.pets.many? #=> false
793
+ #
794
+ # person.pets << Pet.new(name: 'Snoopy')
795
+ # person.pets.count #=> 2
796
+ # person.pets.many? #=> true
797
+ #
798
+ # You can also pass a block to define criteria. The
799
+ # behavior is the same, it returns true if the collection
800
+ # based on the criteria has more than one record.
801
+ #
802
+ # person.pets
803
+ # # => [
804
+ # # #<Pet name: "Gorby", group: "cats">,
805
+ # # #<Pet name: "Puff", group: "cats">,
806
+ # # #<Pet name: "Snoop", group: "dogs">
807
+ # # ]
808
+ #
809
+ # person.pets.many? do |pet|
810
+ # pet.group == 'dogs'
811
+ # end
812
+ # # => false
813
+ #
814
+ # person.pets.many? do |pet|
815
+ # pet.group == 'cats'
816
+ # end
817
+ # # => true
818
+ def many?(&block)
819
+ @association.many?(&block)
820
+ end
821
+
822
+ # Returns +true+ if the given object is present in the collection.
823
+ #
824
+ # class Person < ActiveRecord::Base
825
+ # has_many :pets
826
+ # end
827
+ #
828
+ # person.pets # => [#<Pet id: 20, name: "Snoop">]
829
+ #
830
+ # person.pets.include?(Pet.find(20)) # => true
831
+ # person.pets.include?(Pet.find(21)) # => false
832
+ def include?(record)
833
+ @association.include?(record)
834
+ end
835
+
836
+ def proxy_association
837
+ @association
838
+ end
839
+
840
+ # We don't want this object to be put on the scoping stack, because
841
+ # that could create an infinite loop where we call an @association
842
+ # method, which gets the current scope, which is this object, which
843
+ # delegates to @association, and so on.
844
+ def scoping
845
+ @association.scope.scoping { yield }
846
+ end
847
+
848
+ # Returns a <tt>Relation</tt> object for the records in this association
849
+ def scope
850
+ @association.scope.tap do |scope|
851
+ scope.proxy_association = @association
852
+ end
853
+ end
854
+
855
+ # :nodoc:
856
+ alias spawn scope
857
+
858
+ # Equivalent to <tt>Array#==</tt>. Returns +true+ if the two arrays
859
+ # contain the same number of elements and if each element is equal
860
+ # to the corresponding element in the other array, otherwise returns
861
+ # +false+.
862
+ #
863
+ # class Person < ActiveRecord::Base
864
+ # has_many :pets
865
+ # end
866
+ #
867
+ # person.pets
868
+ # # => [
869
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
870
+ # # #<Pet id: 2, name: "Spook", person_id: 1>
871
+ # # ]
872
+ #
873
+ # other = person.pets.to_ary
874
+ #
875
+ # person.pets == other
876
+ # # => true
877
+ #
878
+ # other = [Pet.new(id: 1), Pet.new(id: 2)]
879
+ #
880
+ # person.pets == other
881
+ # # => false
882
+ def ==(other)
883
+ load_target == other
884
+ end
885
+
886
+ # Returns a new array of objects from the collection. If the collection
887
+ # hasn't been loaded, it fetches the records from the database.
888
+ #
889
+ # class Person < ActiveRecord::Base
890
+ # has_many :pets
891
+ # end
892
+ #
893
+ # person.pets
894
+ # # => [
895
+ # # #<Pet id: 4, name: "Benny", person_id: 1>,
896
+ # # #<Pet id: 5, name: "Brain", person_id: 1>,
897
+ # # #<Pet id: 6, name: "Boss", person_id: 1>
898
+ # # ]
899
+ #
900
+ # other_pets = person.pets.to_ary
901
+ # # => [
902
+ # # #<Pet id: 4, name: "Benny", person_id: 1>,
903
+ # # #<Pet id: 5, name: "Brain", person_id: 1>,
904
+ # # #<Pet id: 6, name: "Boss", person_id: 1>
905
+ # # ]
906
+ #
907
+ # other_pets.replace([Pet.new(name: 'BooGoo')])
908
+ #
909
+ # other_pets
910
+ # # => [#<Pet id: nil, name: "BooGoo", person_id: 1>]
911
+ #
912
+ # person.pets
913
+ # # This is not affected by replace
914
+ # # => [
915
+ # # #<Pet id: 4, name: "Benny", person_id: 1>,
916
+ # # #<Pet id: 5, name: "Brain", person_id: 1>,
917
+ # # #<Pet id: 6, name: "Boss", person_id: 1>
918
+ # # ]
919
+ def to_ary
920
+ load_target.dup
921
+ end
922
+ alias_method :to_a, :to_ary
923
+
924
+ # Adds one or more +records+ to the collection by setting their foreign keys
925
+ # to the association's primary key. Returns +self+, so several appends may be
926
+ # chained together.
927
+ #
928
+ # class Person < ActiveRecord::Base
929
+ # has_many :pets
930
+ # end
931
+ #
932
+ # person.pets.size # => 0
933
+ # person.pets << Pet.new(name: 'Fancy-Fancy')
934
+ # person.pets << [Pet.new(name: 'Spook'), Pet.new(name: 'Choo-Choo')]
935
+ # person.pets.size # => 3
936
+ #
937
+ # person.id # => 1
938
+ # person.pets
939
+ # # => [
940
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
941
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
942
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
943
+ # # ]
944
+ def <<(*records)
945
+ proxy_association.concat(records) && self
946
+ end
947
+ alias_method :push, :<<
948
+ alias_method :append, :<<
949
+
950
+ def prepend(*args)
951
+ raise NoMethodError, "prepend on association is not defined. Please use << or append"
952
+ end
953
+
954
+ # Equivalent to +delete_all+. The difference is that returns +self+, instead
955
+ # of an array with the deleted objects, so methods can be chained. See
956
+ # +delete_all+ for more information.
957
+ def clear
958
+ delete_all
959
+ self
960
+ end
961
+
962
+ # Reloads the collection from the database. Returns +self+.
963
+ # Equivalent to <tt>collection(true)</tt>.
964
+ #
965
+ # class Person < ActiveRecord::Base
966
+ # has_many :pets
967
+ # end
968
+ #
969
+ # person.pets # fetches pets from the database
970
+ # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
971
+ #
972
+ # person.pets # uses the pets cache
973
+ # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
974
+ #
975
+ # person.pets.reload # fetches pets from the database
976
+ # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
977
+ #
978
+ # person.pets(true) # fetches pets from the database
979
+ # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
980
+ def reload
981
+ proxy_association.reload
982
+ self
983
+ end
984
+ end
985
+ end
986
+ end