active-fedora 6.8.0 → 7.0.0.pre1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +15 -5
  4. data/CONTRIBUTING.md +2 -0
  5. data/Gemfile +0 -2
  6. data/History.txt +2 -32
  7. data/README.md +143 -0
  8. data/Rakefile +5 -7
  9. data/active-fedora.gemspec +9 -9
  10. data/gemfiles/rails3.gemfile +11 -0
  11. data/gemfiles/rails4.gemfile +10 -0
  12. data/lib/active_fedora.rb +31 -4
  13. data/lib/active_fedora/association_relation.rb +18 -0
  14. data/lib/active_fedora/associations.rb +38 -171
  15. data/lib/active_fedora/associations/association.rb +163 -0
  16. data/lib/active_fedora/associations/association_scope.rb +39 -0
  17. data/lib/active_fedora/associations/belongs_to_association.rb +47 -25
  18. data/lib/active_fedora/associations/builder/association.rb +55 -0
  19. data/lib/active_fedora/associations/builder/belongs_to.rb +100 -0
  20. data/lib/active_fedora/associations/builder/collection_association.rb +56 -0
  21. data/lib/active_fedora/associations/builder/has_and_belongs_to_many.rb +30 -0
  22. data/lib/active_fedora/associations/builder/has_many.rb +63 -0
  23. data/lib/active_fedora/associations/builder/singular_association.rb +32 -0
  24. data/lib/active_fedora/associations/{association_collection.rb → collection_association.rb} +203 -53
  25. data/lib/active_fedora/associations/collection_proxy.rb +862 -0
  26. data/lib/active_fedora/associations/has_and_belongs_to_many_association.rb +35 -25
  27. data/lib/active_fedora/associations/has_many_association.rb +36 -11
  28. data/lib/active_fedora/associations/singular_association.rb +62 -0
  29. data/lib/active_fedora/attributes.rb +43 -139
  30. data/lib/active_fedora/autosave_association.rb +317 -0
  31. data/lib/active_fedora/base.rb +10 -327
  32. data/lib/active_fedora/callbacks.rb +1 -3
  33. data/lib/active_fedora/content_model.rb +16 -0
  34. data/lib/active_fedora/core.rb +151 -0
  35. data/lib/active_fedora/datastream_attribute.rb +76 -0
  36. data/lib/active_fedora/datastream_hash.rb +8 -13
  37. data/lib/active_fedora/datastreams.rb +39 -26
  38. data/lib/active_fedora/digital_object.rb +2 -2
  39. data/lib/active_fedora/fedora_attributes.rb +45 -0
  40. data/lib/active_fedora/fixture_loader.rb +1 -1
  41. data/lib/active_fedora/indexing.rb +6 -1
  42. data/lib/active_fedora/model.rb +0 -17
  43. data/lib/active_fedora/nested_attributes.rb +2 -2
  44. data/lib/active_fedora/null_relation.rb +7 -0
  45. data/lib/active_fedora/om_datastream.rb +3 -4
  46. data/lib/active_fedora/persistence.rb +41 -29
  47. data/lib/active_fedora/querying.rb +2 -163
  48. data/lib/active_fedora/rdf.rb +1 -0
  49. data/lib/active_fedora/rdf/indexing.rb +67 -0
  50. data/lib/active_fedora/rdf_datastream.rb +2 -50
  51. data/lib/active_fedora/rdf_node.rb +12 -7
  52. data/lib/active_fedora/rdf_node/term_proxy.rb +30 -21
  53. data/lib/active_fedora/rdfxml_rdf_datastream.rb +1 -1
  54. data/lib/active_fedora/reflection.rb +163 -20
  55. data/lib/active_fedora/relation.rb +33 -130
  56. data/lib/active_fedora/relation/calculations.rb +19 -0
  57. data/lib/active_fedora/relation/delegation.rb +22 -0
  58. data/lib/active_fedora/relation/finder_methods.rb +247 -0
  59. data/lib/active_fedora/relation/merger.rb +22 -0
  60. data/lib/active_fedora/relation/query_methods.rb +58 -0
  61. data/lib/active_fedora/relation/spawn_methods.rb +46 -0
  62. data/lib/active_fedora/relationship_graph.rb +0 -2
  63. data/lib/active_fedora/rels_ext_datastream.rb +1 -4
  64. data/lib/active_fedora/rubydora_connection.rb +1 -1
  65. data/lib/active_fedora/scoping.rb +20 -0
  66. data/lib/active_fedora/scoping/default.rb +38 -0
  67. data/lib/active_fedora/scoping/named.rb +32 -0
  68. data/lib/active_fedora/semantic_node.rb +54 -106
  69. data/lib/active_fedora/serialization.rb +19 -0
  70. data/lib/active_fedora/sharding.rb +58 -0
  71. data/lib/active_fedora/solr_digital_object.rb +15 -5
  72. data/lib/active_fedora/solr_instance_loader.rb +1 -1
  73. data/lib/active_fedora/solr_service.rb +1 -1
  74. data/lib/active_fedora/unsaved_digital_object.rb +6 -4
  75. data/lib/active_fedora/version.rb +1 -1
  76. data/lib/tasks/active_fedora.rake +3 -0
  77. data/lib/tasks/active_fedora_dev.rake +6 -5
  78. data/spec/config_helper.rb +14 -14
  79. data/spec/integration/associations_spec.rb +168 -455
  80. data/spec/integration/attributes_spec.rb +12 -11
  81. data/spec/integration/auditable_spec.rb +11 -11
  82. data/spec/integration/autosave_association_spec.rb +25 -0
  83. data/spec/integration/base_spec.rb +163 -163
  84. data/spec/integration/belongs_to_association_spec.rb +166 -0
  85. data/spec/integration/bug_spec.rb +7 -7
  86. data/spec/integration/collection_association_spec.rb +58 -0
  87. data/spec/integration/complex_rdf_datastream_spec.rb +88 -88
  88. data/spec/integration/datastream_collections_spec.rb +69 -69
  89. data/spec/integration/datastream_spec.rb +43 -43
  90. data/spec/integration/datastreams_spec.rb +63 -63
  91. data/spec/integration/delete_all_spec.rb +46 -39
  92. data/spec/integration/fedora_solr_sync_spec.rb +5 -5
  93. data/spec/integration/field_to_solr_name_spec.rb +34 -0
  94. data/spec/integration/full_featured_model_spec.rb +100 -101
  95. data/spec/integration/has_and_belongs_to_many_associations_spec.rb +341 -0
  96. data/spec/integration/has_many_associations_spec.rb +172 -24
  97. data/spec/integration/json_serialization_spec.rb +31 -0
  98. data/spec/integration/load_from_solr_spec.rb +48 -0
  99. data/spec/integration/model_spec.rb +35 -40
  100. data/spec/integration/nested_attribute_spec.rb +42 -43
  101. data/spec/integration/ntriples_datastream_spec.rb +131 -113
  102. data/spec/integration/om_datastream_spec.rb +67 -67
  103. data/spec/integration/persistence_spec.rb +7 -7
  104. data/spec/integration/rdf_nested_attributes_spec.rb +56 -56
  105. data/spec/integration/relation_delegation_spec.rb +26 -25
  106. data/spec/integration/relation_spec.rb +42 -0
  107. data/spec/integration/rels_ext_datastream_spec.rb +20 -20
  108. data/spec/integration/scoped_query_spec.rb +61 -51
  109. data/spec/integration/solr_instance_loader_spec.rb +5 -5
  110. data/spec/integration/solr_service_spec.rb +46 -46
  111. data/spec/samples/hydra-mods_article_datastream.rb +334 -334
  112. data/spec/samples/hydra-rights_metadata_datastream.rb +57 -57
  113. data/spec/samples/marpa-dc_datastream.rb +17 -17
  114. data/spec/samples/models/audio_record.rb +16 -16
  115. data/spec/samples/models/image.rb +2 -2
  116. data/spec/samples/models/mods_article.rb +5 -5
  117. data/spec/samples/models/oral_history.rb +18 -18
  118. data/spec/samples/models/seminar.rb +24 -24
  119. data/spec/samples/models/seminar_audio_file.rb +17 -17
  120. data/spec/samples/oral_history_sample_model.rb +21 -21
  121. data/spec/samples/special_thing.rb +14 -14
  122. data/spec/spec_helper.rb +11 -7
  123. data/spec/support/an_active_model.rb +2 -8
  124. data/spec/support/freeze_mocks.rb +12 -0
  125. data/spec/support/mock_fedora.rb +17 -16
  126. data/spec/unit/active_fedora_spec.rb +58 -60
  127. data/spec/unit/attributes_spec.rb +314 -0
  128. data/spec/unit/base_active_model_spec.rb +28 -27
  129. data/spec/unit/base_cma_spec.rb +5 -5
  130. data/spec/unit/base_datastream_management_spec.rb +27 -27
  131. data/spec/unit/base_extra_spec.rb +76 -48
  132. data/spec/unit/base_spec.rb +277 -348
  133. data/spec/unit/callback_spec.rb +18 -19
  134. data/spec/unit/code_configurator_spec.rb +17 -17
  135. data/spec/unit/config_spec.rb +8 -16
  136. data/spec/unit/content_model_spec.rb +79 -60
  137. data/spec/unit/datastream_collections_spec.rb +229 -229
  138. data/spec/unit/datastream_spec.rb +51 -63
  139. data/spec/unit/datastreams_spec.rb +87 -87
  140. data/spec/unit/file_configurator_spec.rb +217 -217
  141. data/spec/unit/has_and_belongs_to_many_collection_spec.rb +44 -25
  142. data/spec/unit/has_many_collection_spec.rb +26 -8
  143. data/spec/unit/inheritance_spec.rb +13 -12
  144. data/spec/unit/model_spec.rb +39 -45
  145. data/spec/unit/nom_datastream_spec.rb +15 -15
  146. data/spec/unit/ntriples_datastream_spec.rb +123 -118
  147. data/spec/unit/om_datastream_spec.rb +227 -233
  148. data/spec/unit/persistence_spec.rb +34 -15
  149. data/spec/unit/predicates_spec.rb +73 -73
  150. data/spec/unit/property_spec.rb +17 -9
  151. data/spec/unit/qualified_dublin_core_datastream_spec.rb +33 -33
  152. data/spec/unit/query_spec.rb +222 -198
  153. data/spec/unit/rdf_datastream_spec.rb +21 -28
  154. data/spec/unit/rdf_list_nested_attributes_spec.rb +34 -34
  155. data/spec/unit/rdf_list_spec.rb +65 -64
  156. data/spec/unit/rdf_node_spec.rb +7 -7
  157. data/spec/unit/rdf_xml_writer_spec.rb +10 -10
  158. data/spec/unit/rdfxml_rdf_datastream_spec.rb +27 -27
  159. data/spec/unit/relationship_graph_spec.rb +51 -51
  160. data/spec/unit/rels_ext_datastream_spec.rb +68 -74
  161. data/spec/unit/rspec_matchers/belong_to_associated_active_fedora_object_matcher_spec.rb +15 -15
  162. data/spec/unit/rspec_matchers/have_many_associated_active_fedora_objects_matcher_spec.rb +15 -15
  163. data/spec/unit/rspec_matchers/have_predicate_matcher_spec.rb +15 -15
  164. data/spec/unit/rspec_matchers/match_fedora_datastream_matcher_spec.rb +12 -12
  165. data/spec/unit/rubydora_connection_spec.rb +5 -5
  166. data/spec/unit/semantic_node_spec.rb +48 -107
  167. data/spec/unit/serializers_spec.rb +4 -4
  168. data/spec/unit/service_definitions_spec.rb +26 -26
  169. data/spec/unit/simple_datastream_spec.rb +17 -17
  170. data/spec/unit/solr_config_options_spec.rb +29 -28
  171. data/spec/unit/solr_digital_object_spec.rb +17 -25
  172. data/spec/unit/solr_service_spec.rb +95 -82
  173. data/spec/unit/unsaved_digital_object_spec.rb +24 -23
  174. data/spec/unit/validations_spec.rb +21 -21
  175. metadata +110 -159
  176. data/.rspec +0 -1
  177. data/.rubocop.yml +0 -1
  178. data/.rubocop_todo.yml +0 -938
  179. data/CONSOLE_GETTING_STARTED.textile +0 -1
  180. data/NOKOGIRI_DATASTREAMS.textile +0 -1
  181. data/README.textile +0 -116
  182. data/lib/active_fedora/associations/association_proxy.rb +0 -178
  183. data/lib/active_fedora/delegating.rb +0 -72
  184. data/lib/active_fedora/nokogiri_datastream.rb +0 -11
  185. data/spec/integration/delegating_spec.rb +0 -59
  186. data/spec/rails3_test_app/.gitignore +0 -4
  187. data/spec/rails3_test_app/.rspec +0 -1
  188. data/spec/rails3_test_app/Gemfile +0 -40
  189. data/spec/rails3_test_app/Rakefile +0 -7
  190. data/spec/rails3_test_app/app/controllers/application_controller.rb +0 -3
  191. data/spec/rails3_test_app/app/helpers/application_helper.rb +0 -2
  192. data/spec/rails3_test_app/app/views/layouts/application.html.erb +0 -14
  193. data/spec/rails3_test_app/config.ru +0 -4
  194. data/spec/rails3_test_app/config/application.rb +0 -42
  195. data/spec/rails3_test_app/config/boot.rb +0 -6
  196. data/spec/rails3_test_app/config/database.yml +0 -22
  197. data/spec/rails3_test_app/config/environment.rb +0 -5
  198. data/spec/rails3_test_app/config/environments/development.rb +0 -25
  199. data/spec/rails3_test_app/config/environments/production.rb +0 -49
  200. data/spec/rails3_test_app/config/environments/test.rb +0 -35
  201. data/spec/rails3_test_app/config/initializers/backtrace_silencers.rb +0 -7
  202. data/spec/rails3_test_app/config/initializers/inflections.rb +0 -10
  203. data/spec/rails3_test_app/config/initializers/mime_types.rb +0 -5
  204. data/spec/rails3_test_app/config/initializers/secret_token.rb +0 -7
  205. data/spec/rails3_test_app/config/initializers/session_store.rb +0 -8
  206. data/spec/rails3_test_app/config/locales/en.yml +0 -5
  207. data/spec/rails3_test_app/config/routes.rb +0 -58
  208. data/spec/rails3_test_app/db/seeds.rb +0 -7
  209. data/spec/rails3_test_app/run_tests +0 -3
  210. data/spec/rails3_test_app/script/rails +0 -6
  211. data/spec/rails3_test_app/spec/spec_helper.rb +0 -27
  212. data/spec/rails3_test_app/spec/unit/rails_3_init.rb +0 -15
  213. data/spec/unit/association_proxy_spec.rb +0 -12
  214. data/spec/unit/base_delegate_spec.rb +0 -197
  215. data/spec/unit/base_delegate_to_spec.rb +0 -73
@@ -0,0 +1,862 @@
1
+ module ActiveFedora
2
+ module Associations
3
+ # Association proxies in Active Fedora 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
+ # ActiveFedora::Reflection::AssociationReflection.
8
+ #
9
+ # For example, given
10
+ #
11
+ # class Blog < ActiveFedora::Base
12
+ # has_many :posts
13
+ # end
14
+ #
15
+ # blog = Blog.find(: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 has most of the basic instance methods removed, and delegates
22
+ # unknown methods to <tt>@target</tt> via <tt>method_missing</tt>. As a
23
+ # corner case, it even removes the +class+ method and that's why you get
24
+ #
25
+ # blog.posts.class # => Array
26
+ #
27
+ # though the object behind <tt>blog.posts</tt> is not an Array, but an
28
+ # ActiveFedora::Associations::HasManyAssociation.
29
+ #
30
+ # The <tt>@target</tt> object is not \loaded until needed. For example,
31
+ #
32
+ # blog.posts.count
33
+ #
34
+ # is computed directly through Solr and does not trigger by itself the
35
+ # instantiation of the actual post records.
36
+ class CollectionProxy < Relation # :nodoc:
37
+
38
+ def initialize(association)
39
+ @association = association
40
+ super association.klass
41
+ merge! association.scope
42
+ end
43
+
44
+ def target
45
+ @association.target
46
+ end
47
+
48
+ def load_target
49
+ @association.load_target
50
+ end
51
+
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 < ActiveFedora::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 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 build 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(select = nil, &block)
110
+ @association.select(select, &block)
111
+ end
112
+
113
+ # Finds an object in the collection responding to the +id+. Uses the same
114
+ # rules as <tt>ActiveFedora::Base.find</tt>. Returns <tt>ActiveFedora::RecordNotFound</tt>
115
+ # error if the object can not be found.
116
+ #
117
+ # class Person < ActiveFedora::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) # => ActiveFedora::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 < ActiveFedora::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
+ # Returns the last record, or the last +n+ records, from the collection.
174
+ # If the collection is empty, the first form returns +nil+, and the second
175
+ # form returns an empty array.
176
+ #
177
+ # class Person < ActiveFedora::Base
178
+ # has_many :pets
179
+ # end
180
+ #
181
+ # person.pets
182
+ # # => [
183
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
184
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
185
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
186
+ # # ]
187
+ #
188
+ # person.pets.last # => #<Pet id: 3, name: "Choo-Choo", person_id: 1>
189
+ #
190
+ # person.pets.last(2)
191
+ # # => [
192
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
193
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
194
+ # # ]
195
+ #
196
+ # another_person_without.pets # => []
197
+ # another_person_without.pets.last # => nil
198
+ # another_person_without.pets.last(3) # => []
199
+ def last(*args)
200
+ @association.last(*args)
201
+ end
202
+
203
+ # Returns a new object of the collection type that has been instantiated
204
+ # with +attributes+ and linked to this object, but have not yet been saved.
205
+ # You can pass an array of attributes hashes, this will return an array
206
+ # with the new objects.
207
+ #
208
+ # class Person
209
+ # has_many :pets
210
+ # end
211
+ #
212
+ # person.pets.build
213
+ # # => #<Pet id: nil, name: nil, person_id: 1>
214
+ #
215
+ # person.pets.build(name: 'Fancy-Fancy')
216
+ # # => #<Pet id: nil, name: "Fancy-Fancy", person_id: 1>
217
+ #
218
+ # person.pets.build([{name: 'Spook'}, {name: 'Choo-Choo'}, {name: 'Brain'}])
219
+ # # => [
220
+ # # #<Pet id: nil, name: "Spook", person_id: 1>,
221
+ # # #<Pet id: nil, name: "Choo-Choo", person_id: 1>,
222
+ # # #<Pet id: nil, name: "Brain", person_id: 1>
223
+ # # ]
224
+ #
225
+ # person.pets.size # => 5 # size of the collection
226
+ # person.pets.count # => 0 # count from database
227
+ def build(attributes = {}, &block)
228
+ @association.build(attributes, &block)
229
+ end
230
+
231
+ # Returns a new object of the collection type that has been instantiated with
232
+ # attributes, linked to this object and that has already been saved (if it
233
+ # passes the validations).
234
+ #
235
+ # class Person
236
+ # has_many :pets
237
+ # end
238
+ #
239
+ # person.pets.create(name: 'Fancy-Fancy')
240
+ # # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
241
+ #
242
+ # person.pets.create([{name: 'Spook'}, {name: 'Choo-Choo'}])
243
+ # # => [
244
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
245
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
246
+ # # ]
247
+ #
248
+ # person.pets.size # => 3
249
+ # person.pets.count # => 3
250
+ #
251
+ # person.pets.find(1, 2, 3)
252
+ # # => [
253
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
254
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
255
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
256
+ # # ]
257
+ def create(attributes = {}, &block)
258
+ @association.create(attributes, &block)
259
+ end
260
+
261
+ # Like +create+, except that if the record is invalid, raises an exception.
262
+ #
263
+ # class Person
264
+ # has_many :pets
265
+ # end
266
+ #
267
+ # class Pet
268
+ # validates :name, presence: true
269
+ # end
270
+ #
271
+ # person.pets.create!(name: nil)
272
+ # # => ActiveFedora::RecordInvalid: Validation failed: Name can't be blank
273
+ def create!(attributes = {}, &block)
274
+ @association.create!(attributes, &block)
275
+ end
276
+
277
+ # Add one or more records to the collection by setting their foreign keys
278
+ # to the association's primary key. Since << flattens its argument list and
279
+ # inserts each record, +push+ and +concat+ behave identically. Returns +self+
280
+ # so method calls may be chained.
281
+ #
282
+ # class Person < ActiveFedora::Base
283
+ # pets :has_many
284
+ # end
285
+ #
286
+ # person.pets.size # => 0
287
+ # person.pets.concat(Pet.new(name: 'Fancy-Fancy'))
288
+ # person.pets.concat(Pet.new(name: 'Spook'), Pet.new(name: 'Choo-Choo'))
289
+ # person.pets.size # => 3
290
+ #
291
+ # person.id # => 1
292
+ # person.pets
293
+ # # => [
294
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
295
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
296
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
297
+ # # ]
298
+ #
299
+ # person.pets.concat([Pet.new(name: 'Brain'), Pet.new(name: 'Benny')])
300
+ # person.pets.size # => 5
301
+ def concat(*records)
302
+ @association.concat(*records)
303
+ end
304
+
305
+ # Replace this collection with +other_array+. This will perform a diff
306
+ # and delete/add only records that have changed.
307
+ #
308
+ # class Person < ActiveFedora::Base
309
+ # has_many :pets
310
+ # end
311
+ #
312
+ # person.pets
313
+ # # => [#<Pet id: 1, name: "Gorby", group: "cats", person_id: 1>]
314
+ #
315
+ # other_pets = [Pet.new(name: 'Puff', group: 'celebrities']
316
+ #
317
+ # person.pets.replace(other_pets)
318
+ #
319
+ # person.pets
320
+ # # => [#<Pet id: 2, name: "Puff", group: "celebrities", person_id: 1>]
321
+ #
322
+ # If the supplied array has an incorrect association type, it raises
323
+ # an <tt>ActiveFedora::AssociationTypeMismatch</tt> error:
324
+ #
325
+ # person.pets.replace(["doo", "ggie", "gaga"])
326
+ # # => ActiveFedora::AssociationTypeMismatch: Pet expected, got String
327
+ def replace(other_array)
328
+ @association.replace(other_array)
329
+ end
330
+
331
+ # Deletes all the records from the collection. For +has_many+ associations,
332
+ # the deletion is done according to the strategy specified by the <tt>:dependent</tt>
333
+ # option. Returns an array with the deleted records.
334
+ #
335
+ # If no <tt>:dependent</tt> option is given, then it will follow the
336
+ # default strategy. The default strategy is <tt>:nullify</tt>. This
337
+ # sets the foreign keys to <tt>NULL</tt>. For, +has_many+ <tt>:through</tt>,
338
+ # the default strategy is +delete_all+.
339
+ #
340
+ # class Person < ActiveFedora::Base
341
+ # has_many :pets # dependent: :nullify option by default
342
+ # end
343
+ #
344
+ # person.pets.size # => 3
345
+ # person.pets
346
+ # # => [
347
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
348
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
349
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
350
+ # # ]
351
+ #
352
+ # person.pets.delete_all
353
+ # # => [
354
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
355
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
356
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
357
+ # # ]
358
+ #
359
+ # person.pets.size # => 0
360
+ # person.pets # => []
361
+ #
362
+ # Pet.find(1, 2, 3)
363
+ # # => [
364
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: nil>,
365
+ # # #<Pet id: 2, name: "Spook", person_id: nil>,
366
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: nil>
367
+ # # ]
368
+ #
369
+ # If it is set to <tt>:destroy</tt> all the objects from the collection
370
+ # are removed by calling their +destroy+ method. See +destroy+ for more
371
+ # information.
372
+ #
373
+ # class Person < ActiveFedora::Base
374
+ # has_many :pets, dependent: :destroy
375
+ # end
376
+ #
377
+ # person.pets.size # => 3
378
+ # person.pets
379
+ # # => [
380
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
381
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
382
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
383
+ # # ]
384
+ #
385
+ # person.pets.delete_all
386
+ # # => [
387
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
388
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
389
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
390
+ # # ]
391
+ #
392
+ # Pet.find(1, 2, 3)
393
+ # # => ActiveFedora::RecordNotFound
394
+ #
395
+ # If it is set to <tt>:delete_all</tt>, all the objects are deleted
396
+ # *without* calling their +destroy+ method.
397
+ #
398
+ # class Person < ActiveFedora::Base
399
+ # has_many :pets, dependent: :delete_all
400
+ # end
401
+ #
402
+ # person.pets.size # => 3
403
+ # person.pets
404
+ # # => [
405
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
406
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
407
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
408
+ # # ]
409
+ #
410
+ # person.pets.delete_all
411
+ # # => [
412
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
413
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
414
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
415
+ # # ]
416
+ #
417
+ # Pet.find(1, 2, 3)
418
+ # # => ActiveFedora::RecordNotFound
419
+ def delete_all
420
+ @association.delete_all
421
+ end
422
+
423
+ # Deletes the records of the collection directly from the database.
424
+ # This will _always_ remove the records ignoring the +:dependent+
425
+ # option.
426
+ #
427
+ # class Person < ActiveFedora::Base
428
+ # has_many :pets
429
+ # end
430
+ #
431
+ # person.pets.size # => 3
432
+ # person.pets
433
+ # # => [
434
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
435
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
436
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
437
+ # # ]
438
+ #
439
+ # person.pets.destroy_all
440
+ #
441
+ # person.pets.size # => 0
442
+ # person.pets # => []
443
+ #
444
+ # Pet.find(1) # => Couldn't find Pet with id=1
445
+ def destroy_all
446
+ @association.destroy_all
447
+ end
448
+
449
+ # Deletes the +records+ supplied and removes them from the collection. For
450
+ # +has_many+ associations, the deletion is done according to the strategy
451
+ # specified by the <tt>:dependent</tt> option. Returns an array with the
452
+ # deleted records.
453
+ #
454
+ # If no <tt>:dependent</tt> option is given, then it will follow the default
455
+ # strategy. The default strategy is <tt>:nullify</tt>. This sets the foreign
456
+ # keys to <tt>NULL</tt>. For, +has_many+ <tt>:through</tt>, the default
457
+ # strategy is +delete_all+.
458
+ #
459
+ # class Person < ActiveFedora::Base
460
+ # has_many :pets # dependent: :nullify option by default
461
+ # end
462
+ #
463
+ # person.pets.size # => 3
464
+ # person.pets
465
+ # # => [
466
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
467
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
468
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
469
+ # # ]
470
+ #
471
+ # person.pets.delete(Pet.find(1))
472
+ # # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
473
+ #
474
+ # person.pets.size # => 2
475
+ # person.pets
476
+ # # => [
477
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
478
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
479
+ # # ]
480
+ #
481
+ # Pet.find(1)
482
+ # # => #<Pet id: 1, name: "Fancy-Fancy", person_id: nil>
483
+ #
484
+ # If it is set to <tt>:destroy</tt> all the +records+ are removed by calling
485
+ # their +destroy+ method. See +destroy+ for more information.
486
+ #
487
+ # class Person < ActiveFedora::Base
488
+ # has_many :pets, dependent: :destroy
489
+ # end
490
+ #
491
+ # person.pets.size # => 3
492
+ # person.pets
493
+ # # => [
494
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
495
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
496
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
497
+ # # ]
498
+ #
499
+ # person.pets.delete(Pet.find(1), Pet.find(3))
500
+ # # => [
501
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
502
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
503
+ # # ]
504
+ #
505
+ # person.pets.size # => 1
506
+ # person.pets
507
+ # # => [#<Pet id: 2, name: "Spook", person_id: 1>]
508
+ #
509
+ # Pet.find(1, 3)
510
+ # # => ActiveFedora::RecordNotFound: Couldn't find all Pets with IDs (1, 3)
511
+ #
512
+ # If it is set to <tt>:delete_all</tt>, all the +records+ are deleted
513
+ # *without* calling their +destroy+ method.
514
+ #
515
+ # class Person < ActiveFedora::Base
516
+ # has_many :pets, dependent: :delete_all
517
+ # end
518
+ #
519
+ # person.pets.size # => 3
520
+ # person.pets
521
+ # # => [
522
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
523
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
524
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
525
+ # # ]
526
+ #
527
+ # person.pets.delete(Pet.find(1))
528
+ # # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
529
+ #
530
+ # person.pets.size # => 2
531
+ # person.pets
532
+ # # => [
533
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
534
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
535
+ # # ]
536
+ #
537
+ # Pet.find(1)
538
+ # # => ActiveFedora::RecordNotFound: Couldn't find Pet with id=1
539
+ #
540
+ # You can pass +Fixnum+ or +String+ values, it finds the records
541
+ # responding to the +id+ and executes delete on them.
542
+ #
543
+ # class Person < ActiveFedora::Base
544
+ # has_many :pets
545
+ # end
546
+ #
547
+ # person.pets.size # => 3
548
+ # person.pets
549
+ # # => [
550
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
551
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
552
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
553
+ # # ]
554
+ #
555
+ # person.pets.delete("1")
556
+ # # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
557
+ #
558
+ # person.pets.delete(2, 3)
559
+ # # => [
560
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
561
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
562
+ # # ]
563
+ def delete(*records)
564
+ @association.delete(*records)
565
+ end
566
+
567
+ # Destroys the +records+ supplied and removes them from the collection.
568
+ # This method will _always_ remove record from the database ignoring
569
+ # the +:dependent+ option. Returns an array with the removed records.
570
+ #
571
+ # class Person < ActiveFedora::Base
572
+ # has_many :pets
573
+ # end
574
+ #
575
+ # person.pets.size # => 3
576
+ # person.pets
577
+ # # => [
578
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
579
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
580
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
581
+ # # ]
582
+ #
583
+ # person.pets.destroy(Pet.find(1))
584
+ # # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
585
+ #
586
+ # person.pets.size # => 2
587
+ # person.pets
588
+ # # => [
589
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
590
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
591
+ # # ]
592
+ #
593
+ # person.pets.destroy(Pet.find(2), Pet.find(3))
594
+ # # => [
595
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
596
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
597
+ # # ]
598
+ #
599
+ # person.pets.size # => 0
600
+ # person.pets # => []
601
+ #
602
+ # Pet.find(1, 2, 3) # => ActiveFedora::RecordNotFound: Couldn't find all Pets with IDs (1, 2, 3)
603
+ #
604
+ # You can pass +Fixnum+ or +String+ values, it finds the records
605
+ # responding to the +id+ and then deletes them from the database.
606
+ #
607
+ # person.pets.size # => 3
608
+ # person.pets
609
+ # # => [
610
+ # # #<Pet id: 4, name: "Benny", person_id: 1>,
611
+ # # #<Pet id: 5, name: "Brain", person_id: 1>,
612
+ # # #<Pet id: 6, name: "Boss", person_id: 1>
613
+ # # ]
614
+ #
615
+ # person.pets.destroy("4")
616
+ # # => #<Pet id: 4, name: "Benny", person_id: 1>
617
+ #
618
+ # person.pets.size # => 2
619
+ # person.pets
620
+ # # => [
621
+ # # #<Pet id: 5, name: "Brain", person_id: 1>,
622
+ # # #<Pet id: 6, name: "Boss", person_id: 1>
623
+ # # ]
624
+ #
625
+ # person.pets.destroy(5, 6)
626
+ # # => [
627
+ # # #<Pet id: 5, name: "Brain", person_id: 1>,
628
+ # # #<Pet id: 6, name: "Boss", person_id: 1>
629
+ # # ]
630
+ #
631
+ # person.pets.size # => 0
632
+ # person.pets # => []
633
+ #
634
+ # Pet.find(4, 5, 6) # => ActiveFedora::RecordNotFound: Couldn't find all Pets with IDs (4, 5, 6)
635
+ def destroy(*records)
636
+ @association.destroy(*records)
637
+ end
638
+
639
+ # Specifies whether the records should be unique or not.
640
+ #
641
+ # class Person < ActiveFedora::Base
642
+ # has_many :pets
643
+ # end
644
+ #
645
+ # person.pets.select(:name)
646
+ # # => [
647
+ # # #<Pet name: "Fancy-Fancy">,
648
+ # # #<Pet name: "Fancy-Fancy">
649
+ # # ]
650
+ #
651
+ # person.pets.select(:name).uniq
652
+ # # => [#<Pet name: "Fancy-Fancy">]
653
+ def uniq
654
+ @association.uniq
655
+ end
656
+
657
+ # Count all records using Solr.
658
+ #
659
+ # class Person < ActiveFedora::Base
660
+ # has_many :pets
661
+ # end
662
+ #
663
+ # person.pets.count # => 3
664
+ # person.pets
665
+ # # => [
666
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
667
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
668
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
669
+ # # ]
670
+ def count(options = {})
671
+ @association.count(options)
672
+ end
673
+
674
+ # Returns the size of the collection. If the collection hasn't been loaded,
675
+ # it executes a solr query to find the matching records. Else it calls
676
+ # <tt>collection.size</tt>.
677
+ #
678
+ # If the collection has been already loaded +size+ and +length+ are
679
+ # equivalent. If not and you are going to need the records anyway
680
+ # +length+ will take one less query. Otherwise +size+ is more efficient.
681
+ #
682
+ # class Person < ActiveFedora::Base
683
+ # has_many :pets
684
+ # end
685
+ #
686
+ # person.pets.size # => 3
687
+ # # queries solr for the number of matching records where "person_id_ssi" = 1
688
+ #
689
+ # person.pets # This will execute a solr query
690
+ # # => [
691
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
692
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
693
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
694
+ # # ]
695
+ #
696
+ # person.pets.size # => 3
697
+ # # Because the collection is already loaded, this will behave like
698
+ # # collection.size and no Solr count query is executed.
699
+ def size
700
+ @association.size
701
+ end
702
+
703
+ # Returns the size of the collection calling +size+ on the target.
704
+ # If the collection has been already loaded, +length+ and +size+ are
705
+ # equivalent. If not and you are going to need the records anyway this
706
+ # method will take one less query. Otherwise +size+ is more efficient.
707
+ #
708
+ # class Person < ActiveFedora::Base
709
+ # has_many :pets
710
+ # end
711
+ #
712
+ # person.pets.length # => 3
713
+ # # queries solr for the number of matching records where "person_id_ssi" = 1
714
+ #
715
+ # # Because the collection is loaded, you can
716
+ # # call the collection with no additional queries:
717
+ # person.pets
718
+ # # => [
719
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
720
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
721
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
722
+ # # ]
723
+ def length
724
+ @association.length
725
+ end
726
+
727
+ # Returns +true+ if the collection is empty. If the collection has been
728
+ # loaded or the <tt>:counter_sql</tt> option is provided, it is equivalent
729
+ # to <tt>collection.size.zero?</tt>. If the collection has not been loaded,
730
+ # it is equivalent to <tt>collection.exists?</tt>. If the collection has
731
+ # not already been loaded and you are going to fetch the records anyway it
732
+ # is better to check <tt>collection.length.zero?</tt>.
733
+ #
734
+ # class Person < ActiveFedora::Base
735
+ # has_many :pets
736
+ # end
737
+ #
738
+ # person.pets.count # => 1
739
+ # person.pets.empty? # => false
740
+ #
741
+ # person.pets.delete_all
742
+ #
743
+ # person.pets.count # => 0
744
+ # person.pets.empty? # => true
745
+ def empty?
746
+ @association.empty?
747
+ end
748
+
749
+ # Returns +true+ if the collection is not empty.
750
+ #
751
+ # class Person < ActiveFedora::Base
752
+ # has_many :pets
753
+ # end
754
+ #
755
+ # person.pets.count # => 0
756
+ # person.pets.any? # => false
757
+ #
758
+ # person.pets << Pet.new(name: 'Snoop')
759
+ # person.pets.count # => 0
760
+ # person.pets.any? # => true
761
+ #
762
+ # You can also pass a block to define criteria. The behavior
763
+ # is the same, it returns true if the collection based on the
764
+ # criteria is not empty.
765
+ #
766
+ # person.pets
767
+ # # => [#<Pet name: "Snoop", group: "dogs">]
768
+ #
769
+ # person.pets.any? do |pet|
770
+ # pet.group == 'cats'
771
+ # end
772
+ # # => false
773
+ #
774
+ # person.pets.any? do |pet|
775
+ # pet.group == 'dogs'
776
+ # end
777
+ # # => true
778
+ def any?(&block)
779
+ @association.any?(&block)
780
+ end
781
+
782
+ # Returns true if the collection has more than one record.
783
+ # Equivalent to <tt>collection.size > 1</tt>.
784
+ #
785
+ # class Person < ActiveFedora::Base
786
+ # has_many :pets
787
+ # end
788
+ #
789
+ # person.pets.count #=> 1
790
+ # person.pets.many? #=> false
791
+ #
792
+ # person.pets << Pet.new(name: 'Snoopy')
793
+ # person.pets.count #=> 2
794
+ # person.pets.many? #=> true
795
+ #
796
+ # You can also pass a block to define criteria. The
797
+ # behavior is the same, it returns true if the collection
798
+ # based on the criteria has more than one record.
799
+ #
800
+ # person.pets
801
+ # # => [
802
+ # # #<Pet name: "Gorby", group: "cats">,
803
+ # # #<Pet name: "Puff", group: "cats">,
804
+ # # #<Pet name: "Snoop", group: "dogs">
805
+ # # ]
806
+ #
807
+ # person.pets.many? do |pet|
808
+ # pet.group == 'dogs'
809
+ # end
810
+ # # => false
811
+ #
812
+ # person.pets.many? do |pet|
813
+ # pet.group == 'cats'
814
+ # end
815
+ # # => true
816
+ def many?(&block)
817
+ @association.many?(&block)
818
+ end
819
+
820
+ # Returns +true+ if the given object is present in the collection.
821
+ #
822
+ # class Person < ActiveFedora::Base
823
+ # has_many :pets
824
+ # end
825
+ #
826
+ # person.pets # => [#<Pet id: 20, name: "Snoop">]
827
+ #
828
+ # person.pets.include?(Pet.find(20)) # => true
829
+ # person.pets.include?(Pet.find(21)) # => false
830
+ def include?(record)
831
+ @association.include?(record)
832
+ end
833
+
834
+ alias_method :new, :build
835
+
836
+ def proxy_association
837
+ @association
838
+ end
839
+
840
+ def to_ary
841
+ load_target.dup
842
+ end
843
+ alias_method :to_a, :to_ary
844
+
845
+ def <<(*records)
846
+ proxy_association.concat(records) && self
847
+ end
848
+ alias_method :push, :<<
849
+
850
+ def clear
851
+ delete_all
852
+ self
853
+ end
854
+
855
+ def reload
856
+ proxy_association.reload
857
+ self
858
+ end
859
+
860
+ end
861
+ end
862
+ end