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