Class: DuckRecord::Associations::CollectionProxy
- Inherits:
-
Object
- Object
- DuckRecord::Associations::CollectionProxy
- Includes:
- Enumerable
- Defined in:
- lib/duck_record/associations/collection_proxy.rb
Overview
Association proxies in Active Record are middlemen between the object that holds the association, known as the @owner, and the actual associated object, known as the @target. The kind of association any proxy is about is available in @reflection. That’s an instance of the class ActiveRecord::Reflection::AssociationReflection.
For example, given
class Blog < ActiveRecord::Base
has_many :posts
end
blog = Blog.first
the association proxy in blog.posts has the object in blog as @owner, the collection of its posts as @target, and the @reflection object represents a :has_many macro.
This class delegates unknown methods to @target via method_missing.
The @target object is not loaded until needed. For example,
blog.posts.count
is computed directly through SQL and does not trigger by itself the instantiation of the actual post records.
Instance Attribute Summary collapse
-
#klass ⇒ Object
(also: #model)
readonly
Returns the value of attribute klass.
Instance Method Summary collapse
-
#<<(*records) ⇒ Object
(also: #push, #append)
Adds one or more
recordsto the collection by setting their foreign keys to the association’s primary key. -
#==(other) ⇒ Object
Equivalent to
Array#==. -
#build(attributes = {}, &block) ⇒ Object
(also: #new)
Returns a new object of the collection type that has been instantiated with
attributesand linked to this object, but have not yet been saved. -
#clear ⇒ Object
Equivalent to
delete_all. -
#concat(*records) ⇒ Object
Add one or more records to the collection by setting their foreign keys to the association’s primary key.
-
#delete(*records) ⇒ Object
Deletes the
recordssupplied from the collection according to the strategy specified by the:dependentoption. -
#delete_all ⇒ Object
Deletes all the records from the collection according to the strategy specified by the
:dependentoption. -
#destroy(*records) ⇒ Object
Destroys the
recordssupplied and removes them from the collection. -
#destroy_all ⇒ Object
Deletes the records of the collection directly from the database ignoring the
:dependentoption. -
#empty? ⇒ Boolean
Returns
trueif the collection is empty. -
#include?(record) ⇒ Boolean
Returns
trueif the givenrecordis present in the collection. -
#initialize(klass, association) ⇒ CollectionProxy
constructor
:nodoc:.
- #inspect ⇒ Object
- #prepend(*_args) ⇒ Object
- #proxy_association ⇒ Object
-
#records ⇒ Object
:nodoc:.
-
#replace(other_array) ⇒ Object
Replaces this collection with
other_array. -
#reset ⇒ Object
Unloads the association.
-
#size ⇒ Object
Returns the size of the collection.
- #target ⇒ Object
-
#to_ary ⇒ Object
(also: #to_a)
Returns a new array of objects from the collection.
-
#uniq ⇒ Object
–.
Constructor Details
#initialize(klass, association) ⇒ CollectionProxy
:nodoc:
41 42 43 44 |
# File 'lib/duck_record/associations/collection_proxy.rb', line 41 def initialize(klass, association) #:nodoc: @klass = klass @association = association end |
Instance Attribute Details
#klass ⇒ Object (readonly) Also known as: model
Returns the value of attribute klass.
38 39 40 |
# File 'lib/duck_record/associations/collection_proxy.rb', line 38 def klass @klass end |
Instance Method Details
#<<(*records) ⇒ Object Also known as: push, append
Adds one or more records to the collection by setting their foreign keys to the association’s primary key. Returns self, so several appends may be chained together.
class Person < ActiveRecord::Base
has_many :pets
end
person.pets.size # => 0
person.pets << Pet.new(name: 'Fancy-Fancy')
person.pets << [Pet.new(name: 'Spook'), Pet.new(name: 'Choo-Choo')]
person.pets.size # => 3
person.id # => 1
person.pets
# => [
# #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
# #<Pet id: 2, name: "Spook", person_id: 1>,
# #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# ]
844 845 846 |
# File 'lib/duck_record/associations/collection_proxy.rb', line 844 def <<(*records) proxy_association.concat(records) && self end |
#==(other) ⇒ Object
Equivalent to Array#==. Returns true if the two arrays contain the same number of elements and if each element is equal to the corresponding element in the other array, otherwise returns false.
class Person < ActiveRecord::Base
has_many :pets
end
person.pets
# => [
# #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
# #<Pet id: 2, name: "Spook", person_id: 1>
# ]
other = person.pets.to_ary
person.pets == other
# => true
other = [Pet.new(id: 1), Pet.new(id: 2)]
person.pets == other
# => false
778 779 780 |
# File 'lib/duck_record/associations/collection_proxy.rb', line 778 def ==(other) proxy_association.target == other end |
#build(attributes = {}, &block) ⇒ Object Also known as: new
Returns a new object of the collection type that has been instantiated with attributes and linked to this object, but have not yet been saved. You can pass an array of attributes hashes, this will return an array with the new objects.
class Person
has_many :pets
end
person.pets.build
# => #<Pet id: nil, name: nil, person_id: 1>
person.pets.build(name: 'Fancy-Fancy')
# => #<Pet id: nil, name: "Fancy-Fancy", person_id: 1>
person.pets.build([{name: 'Spook'}, {name: 'Choo-Choo'}, {name: 'Brain'}])
# => [
# #<Pet id: nil, name: "Spook", person_id: 1>,
# #<Pet id: nil, name: "Choo-Choo", person_id: 1>,
# #<Pet id: nil, name: "Brain", person_id: 1>
# ]
person.pets.size # => 5 # size of the collection
person.pets.count # => 0 # count from database
164 165 166 |
# File 'lib/duck_record/associations/collection_proxy.rb', line 164 def build(attributes = {}, &block) proxy_association.build(attributes, &block) end |
#clear ⇒ Object
Equivalent to delete_all. The difference is that returns self, instead of an array with the deleted objects, so methods can be chained. See delete_all for more information. Note that because delete_all removes records by directly running an SQL query into the database, the updated_at column of the object is not changed.
860 861 862 863 |
# File 'lib/duck_record/associations/collection_proxy.rb', line 860 def clear delete_all self end |
#concat(*records) ⇒ Object
Add one or more records to the collection by setting their foreign keys to the association’s primary key. Since #<< flattens its argument list and inserts each record, push and #concat behave identically. Returns self so method calls may be chained.
class Person < ActiveRecord::Base
has_many :pets
end
person.pets.size # => 0
person.pets.concat(Pet.new(name: 'Fancy-Fancy'))
person.pets.concat(Pet.new(name: 'Spook'), Pet.new(name: 'Choo-Choo'))
person.pets.size # => 3
person.id # => 1
person.pets
# => [
# #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
# #<Pet id: 2, name: "Spook", person_id: 1>,
# #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# ]
person.pets.concat([Pet.new(name: 'Brain'), Pet.new(name: 'Benny')])
person.pets.size # => 5
193 194 195 |
# File 'lib/duck_record/associations/collection_proxy.rb', line 193 def concat(*records) proxy_association.concat(*records) end |
#delete(*records) ⇒ Object
Deletes the records supplied from the collection according to the strategy specified by the :dependent option. If no :dependent option is given, then it will follow the default strategy. Returns an array with the deleted records.
For has_many :through associations, the default deletion strategy is :delete_all.
For has_many associations, the default deletion strategy is :nullify. This sets the foreign keys to NULL.
class Person < ActiveRecord::Base
has_many :pets # dependent: :nullify option by default
end
person.pets.size # => 3
person.pets
# => [
# #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
# #<Pet id: 2, name: "Spook", person_id: 1>,
# #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# ]
person.pets.delete(Pet.find(1))
# => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
person.pets.size # => 2
person.pets
# => [
# #<Pet id: 2, name: "Spook", person_id: 1>,
# #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# ]
Pet.find(1)
# => #<Pet id: 1, name: "Fancy-Fancy", person_id: nil>
If it is set to :destroy all the records are removed by calling their destroy method. See destroy for more information.
class Person < ActiveRecord::Base
has_many :pets, dependent: :destroy
end
person.pets.size # => 3
person.pets
# => [
# #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
# #<Pet id: 2, name: "Spook", person_id: 1>,
# #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# ]
person.pets.delete(Pet.find(1), Pet.find(3))
# => [
# #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
# #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# ]
person.pets.size # => 1
person.pets
# => [#<Pet id: 2, name: "Spook", person_id: 1>]
Pet.find(1, 3)
# => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 3)
If it is set to :delete_all, all the records are deleted without calling their destroy method.
class Person < ActiveRecord::Base
has_many :pets, dependent: :delete_all
end
person.pets.size # => 3
person.pets
# => [
# #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
# #<Pet id: 2, name: "Spook", person_id: 1>,
# #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# ]
person.pets.delete(Pet.find(1))
# => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
person.pets.size # => 2
person.pets
# => [
# #<Pet id: 2, name: "Spook", person_id: 1>,
# #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# ]
Pet.find(1)
# => ActiveRecord::RecordNotFound: Couldn't find Pet with 'id'=1
You can pass Integer or String values, it finds the records responding to the id and executes delete on them.
class Person < ActiveRecord::Base
has_many :pets
end
person.pets.size # => 3
person.pets
# => [
# #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
# #<Pet id: 2, name: "Spook", person_id: 1>,
# #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# ]
person.pets.delete("1")
# => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
person.pets.delete(2, 3)
# => [
# #<Pet id: 2, name: "Spook", person_id: 1>,
# #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# ]
448 449 450 |
# File 'lib/duck_record/associations/collection_proxy.rb', line 448 def delete(*records) proxy_association.delete(*records) end |
#delete_all ⇒ Object
Deletes all the records from the collection according to the strategy specified by the :dependent option. If no :dependent option is given, then it will follow the default strategy.
For has_many :through associations, the default deletion strategy is :delete_all.
For has_many associations, the default deletion strategy is :nullify. This sets the foreign keys to NULL.
class Person < ActiveRecord::Base
has_many :pets # dependent: :nullify option by default
end
person.pets.size # => 3
person.pets
# => [
# #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
# #<Pet id: 2, name: "Spook", person_id: 1>,
# #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# ]
person.pets.delete_all
# => [
# #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
# #<Pet id: 2, name: "Spook", person_id: 1>,
# #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# ]
person.pets.size # => 0
person.pets # => []
Pet.find(1, 2, 3)
# => [
# #<Pet id: 1, name: "Fancy-Fancy", person_id: nil>,
# #<Pet id: 2, name: "Spook", person_id: nil>,
# #<Pet id: 3, name: "Choo-Choo", person_id: nil>
# ]
Both has_many and has_many :through dependencies default to the :delete_all strategy if the :dependent option is set to :destroy. Records are not instantiated and callbacks will not be fired.
class Person < ActiveRecord::Base
has_many :pets, dependent: :destroy
end
person.pets.size # => 3
person.pets
# => [
# #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
# #<Pet id: 2, name: "Spook", person_id: 1>,
# #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# ]
person.pets.delete_all
Pet.find(1, 2, 3)
# => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
If it is set to :delete_all, all the objects are deleted without calling their destroy method.
class Person < ActiveRecord::Base
has_many :pets, dependent: :delete_all
end
person.pets.size # => 3
person.pets
# => [
# #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
# #<Pet id: 2, name: "Spook", person_id: 1>,
# #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# ]
person.pets.delete_all
Pet.find(1, 2, 3)
# => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
302 303 304 |
# File 'lib/duck_record/associations/collection_proxy.rb', line 302 def delete_all proxy_association.delete_all end |
#destroy(*records) ⇒ Object
Destroys the records supplied and removes them from the collection. This method will always remove record from the database ignoring the :dependent option. Returns an array with the removed records.
class Person < ActiveRecord::Base
has_many :pets
end
person.pets.size # => 3
person.pets
# => [
# #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
# #<Pet id: 2, name: "Spook", person_id: 1>,
# #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# ]
person.pets.destroy(Pet.find(1))
# => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
person.pets.size # => 2
person.pets
# => [
# #<Pet id: 2, name: "Spook", person_id: 1>,
# #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# ]
person.pets.destroy(Pet.find(2), Pet.find(3))
# => [
# #<Pet id: 2, name: "Spook", person_id: 1>,
# #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# ]
person.pets.size # => 0
person.pets # => []
Pet.find(1, 2, 3) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
You can pass Integer or String values, it finds the records responding to the id and then deletes them from the database.
person.pets.size # => 3
person.pets
# => [
# #<Pet id: 4, name: "Benny", person_id: 1>,
# #<Pet id: 5, name: "Brain", person_id: 1>,
# #<Pet id: 6, name: "Boss", person_id: 1>
# ]
person.pets.destroy("4")
# => #<Pet id: 4, name: "Benny", person_id: 1>
person.pets.size # => 2
person.pets
# => [
# #<Pet id: 5, name: "Brain", person_id: 1>,
# #<Pet id: 6, name: "Boss", person_id: 1>
# ]
person.pets.destroy(5, 6)
# => [
# #<Pet id: 5, name: "Brain", person_id: 1>,
# #<Pet id: 6, name: "Boss", person_id: 1>
# ]
person.pets.size # => 0
person.pets # => []
Pet.find(4, 5, 6) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (4, 5, 6)
520 521 522 |
# File 'lib/duck_record/associations/collection_proxy.rb', line 520 def destroy(*records) proxy_association.destroy(*records) end |
#destroy_all ⇒ Object
Deletes the records of the collection directly from the database ignoring the :dependent option. Records are instantiated and it invokes before_remove, after_remove , before_destroy and after_destroy callbacks.
class Person < ActiveRecord::Base
has_many :pets
end
person.pets.size # => 3
person.pets
# => [
# #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
# #<Pet id: 2, name: "Spook", person_id: 1>,
# #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# ]
person.pets.destroy_all
person.pets.size # => 0
person.pets # => []
Pet.find(1) # => Couldn't find Pet with id=1
329 330 331 |
# File 'lib/duck_record/associations/collection_proxy.rb', line 329 def destroy_all proxy_association.destroy_all end |
#empty? ⇒ Boolean
Returns true if the collection is empty. If the collection has been loaded it is equivalent to collection.size.zero?. If the collection has not been loaded, it is equivalent to !collection.exists?. If the collection has not already been loaded and you are going to fetch the records anyway it is better to check collection.length.zero?.
class Person < ActiveRecord::Base
has_many :pets
end
person.pets.count # => 1
person.pets.empty? # => false
person.pets.delete_all
person.pets.count # => 0
person.pets.empty? # => true
655 656 657 |
# File 'lib/duck_record/associations/collection_proxy.rb', line 655 def empty? proxy_association.empty? end |
#include?(record) ⇒ Boolean
Returns true if the given record is present in the collection.
class Person < ActiveRecord::Base
has_many :pets
end
person.pets # => [#<Pet id: 20, name: "Snoop">]
person.pets.include?(Pet.find(20)) # => true
person.pets.include?(Pet.find(21)) # => false
746 747 748 |
# File 'lib/duck_record/associations/collection_proxy.rb', line 746 def include?(record) !!proxy_association.include?(record) end |
#inspect ⇒ Object
886 887 888 889 890 891 |
# File 'lib/duck_record/associations/collection_proxy.rb', line 886 def inspect entries = records.take(11).map!(&:inspect) entries[10] = '...' if entries.size == 11 "#<#{self.class.name} [#{entries.join(', ')}]>" end |
#prepend(*_args) ⇒ Object
850 851 852 |
# File 'lib/duck_record/associations/collection_proxy.rb', line 850 def prepend(*_args) raise NoMethodError, "prepend on association is not defined. Please use <<, push or append" end |
#proxy_association ⇒ Object
750 751 752 |
# File 'lib/duck_record/associations/collection_proxy.rb', line 750 def proxy_association @association end |
#records ⇒ Object
:nodoc:
820 821 822 |
# File 'lib/duck_record/associations/collection_proxy.rb', line 820 def records # :nodoc: proxy_association.target end |
#replace(other_array) ⇒ Object
Replaces this collection with other_array. This will perform a diff and delete/add only records that have changed.
class Person < ActiveRecord::Base
has_many :pets
end
person.pets
# => [#<Pet id: 1, name: "Gorby", group: "cats", person_id: 1>]
other_pets = [Pet.new(name: 'Puff', group: 'celebrities']
person.pets.replace(other_pets)
person.pets
# => [#<Pet id: 2, name: "Puff", group: "celebrities", person_id: 1>]
If the supplied array has an incorrect association type, it raises an ActiveRecord::AssociationTypeMismatch error:
person.pets.replace(["doo", "ggie", "gaga"])
# => ActiveRecord::AssociationTypeMismatch: Pet expected, got String
219 220 221 |
# File 'lib/duck_record/associations/collection_proxy.rb', line 219 def replace(other_array) proxy_association.replace(other_array) end |
#reset ⇒ Object
Unloads the association. Returns self.
class Person < ActiveRecord::Base
has_many :pets
end
person.pets # fetches pets from the database
# => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
person.pets # uses the pets cache
# => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
person.pets.reset # clears the pets cache
person.pets # fetches pets from the database
# => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
881 882 883 884 |
# File 'lib/duck_record/associations/collection_proxy.rb', line 881 def reset proxy_association.reset self end |
#size ⇒ Object
Returns the size of the collection. If the collection hasn’t been loaded, it executes a SELECT COUNT(*) query. Else it calls collection.size.
If the collection has been already loaded size and length are equivalent. If not and you are going to need the records anyway length will take one less query. Otherwise size is more efficient.
class Person < ActiveRecord::Base
has_many :pets
end
person.pets.size # => 3
# executes something like SELECT COUNT(*) FROM "pets" WHERE "pets"."person_id" = 1
person.pets # This will execute a SELECT * FROM query
# => [
# #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
# #<Pet id: 2, name: "Spook", person_id: 1>,
# #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# ]
person.pets.size # => 3
# Because the collection is already loaded, this will behave like
# collection.size and no SQL count query is executed.
606 607 608 |
# File 'lib/duck_record/associations/collection_proxy.rb', line 606 def size proxy_association.size end |
#target ⇒ Object
46 47 48 |
# File 'lib/duck_record/associations/collection_proxy.rb', line 46 def target @association.target end |
#to_ary ⇒ Object Also known as: to_a
Returns a new array of objects from the collection. If the collection hasn’t been loaded, it fetches the records from the database.
class Person < ActiveRecord::Base
has_many :pets
end
person.pets
# => [
# #<Pet id: 4, name: "Benny", person_id: 1>,
# #<Pet id: 5, name: "Brain", person_id: 1>,
# #<Pet id: 6, name: "Boss", person_id: 1>
# ]
other_pets = person.pets.to_ary
# => [
# #<Pet id: 4, name: "Benny", person_id: 1>,
# #<Pet id: 5, name: "Brain", person_id: 1>,
# #<Pet id: 6, name: "Boss", person_id: 1>
# ]
other_pets.replace([Pet.new(name: 'BooGoo')])
other_pets
# => [#<Pet id: nil, name: "BooGoo", person_id: 1>]
person.pets
# This is not affected by replace
# => [
# #<Pet id: 4, name: "Benny", person_id: 1>,
# #<Pet id: 5, name: "Brain", person_id: 1>,
# #<Pet id: 6, name: "Boss", person_id: 1>
# ]
815 816 817 |
# File 'lib/duck_record/associations/collection_proxy.rb', line 815 def to_ary proxy_association.target.dup end |
#uniq ⇒ Object
–
552 553 554 |
# File 'lib/duck_record/associations/collection_proxy.rb', line 552 def uniq proxy_association.uniq end |