class DuckRecord::Associations::EmbedsManyProxy
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.
Attributes
Public Instance Methods
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> # ]
# File lib/duck_record/associations/embeds_many_proxy.rb, line 842 def <<(*records) proxy_association.concat(records) && self end
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
# File lib/duck_record/associations/embeds_many_proxy.rb, line 776 def ==(other) proxy_association.target == other end
Returns true
if the collection is not empty.
class Person < ActiveRecord::Base has_many :pets end person.pets.count # => 0 person.pets.any? # => false person.pets << Pet.new(name: 'Snoop') person.pets.count # => 1 person.pets.any? # => true
You can also pass a block
to define criteria. The behavior is the same, it returns true if the collection based on the criteria is not empty.
person.pets # => [#<Pet name: "Snoop", group: "dogs">] person.pets.any? do |pet| pet.group == 'cats' end # => false person.pets.any? do |pet| pet.group == 'dogs' end # => true
# File lib/duck_record/associations/embeds_many_proxy.rb, line 658
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
# File lib/duck_record/associations/embeds_many_proxy.rb, line 162 def build(attributes = {}, &block) proxy_association.build(attributes, &block) end
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.
# File lib/duck_record/associations/embeds_many_proxy.rb, line 858 def clear delete_all self end
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
# File lib/duck_record/associations/embeds_many_proxy.rb, line 191 def concat(*records) proxy_association.concat(*records) end
Count all records.
class Person < ActiveRecord::Base has_many :pets end # This will perform the count using SQL. person.pets.count # => 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> # ]
Passing a block will select all of a person's pets in SQL and then perform the count using Ruby.
person.pets.count { |pet| pet.name.include?('-') } # => 2
# File lib/duck_record/associations/embeds_many_proxy.rb, line 555
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> # ]
# File lib/duck_record/associations/embeds_many_proxy.rb, line 446 def delete(*records) proxy_association.delete(*records) end
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)
# File lib/duck_record/associations/embeds_many_proxy.rb, line 300 def delete_all proxy_association.delete_all end
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)
# File lib/duck_record/associations/embeds_many_proxy.rb, line 518 def destroy(*records) proxy_association.destroy(*records) end
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
# File lib/duck_record/associations/embeds_many_proxy.rb, line 327 def destroy_all proxy_association.destroy_all end
Specifies whether the records should be unique or not.
class Person < ActiveRecord::Base has_many :pets end person.pets.select(:name) # => [ # #<Pet name: "Fancy-Fancy">, # #<Pet name: "Fancy-Fancy"> # ] person.pets.select(:name).distinct # => [#<Pet name: "Fancy-Fancy">] person.pets.select(:name).distinct.distinct(false) # => [ # #<Pet name: "Fancy-Fancy">, # #<Pet name: "Fancy-Fancy"> # ]
# File lib/duck_record/associations/embeds_many_proxy.rb, line 523
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
# File lib/duck_record/associations/embeds_many_proxy.rb, line 653 def empty? proxy_association.empty? end
Same as first
except returns only the fifth record.
# File lib/duck_record/associations/embeds_many_proxy.rb, line 106
Returns the first record, or the first n
records, from the collection. If the collection is empty, the first form returns nil
, and the second form returns an empty array.
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>, # #<Pet id: 3, name: "Choo-Choo", person_id: 1> # ] person.pets.first # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1> person.pets.first(2) # => [ # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>, # #<Pet id: 2, name: "Spook", person_id: 1> # ] another_person_without.pets # => [] another_person_without.pets.first # => nil another_person_without.pets.first(3) # => []
# File lib/duck_record/associations/embeds_many_proxy.rb, line 49
Same as first
except returns only the forty second record. Also known as accessing “the reddit”.
# File lib/duck_record/associations/embeds_many_proxy.rb, line 114
Same as first
except returns only the fourth record.
# File lib/duck_record/associations/embeds_many_proxy.rb, line 98
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
# File lib/duck_record/associations/embeds_many_proxy.rb, line 744 def include?(record) !!proxy_association.include?(record) end
# File lib/duck_record/associations/embeds_many_proxy.rb, line 884 def inspect entries = records.take(11).map!(&:inspect) entries[10] = "..." if entries.size == 11 "#<#{self.class.name} [#{entries.join(', ')}]>" end
Returns the size of the collection calling size
on the target. If the collection has been already loaded, length
and size
are equivalent. If not and you are going to need the records anyway this method will take one less query. Otherwise size
is more efficient.
class Person < ActiveRecord::Base has_many :pets end person.pets.length # => 3 # executes something like SELECT "pets".* FROM "pets" WHERE "pets"."person_id" = 1 # Because the collection is loaded, you can # call the collection with no additional queries: 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> # ]
# File lib/duck_record/associations/embeds_many_proxy.rb, line 609
Returns true if the collection has more than one record. Equivalent to collection.size > 1
.
class Person < ActiveRecord::Base has_many :pets end person.pets.count # => 1 person.pets.many? # => false person.pets << Pet.new(name: 'Snoopy') person.pets.count # => 2 person.pets.many? # => true
You can also pass a block
to define criteria. The behavior is the same, it returns true if the collection based on the criteria has more than one record.
person.pets # => [ # #<Pet name: "Gorby", group: "cats">, # #<Pet name: "Puff", group: "cats">, # #<Pet name: "Snoop", group: "dogs"> # ] person.pets.many? do |pet| pet.group == 'dogs' end # => false person.pets.many? do |pet| pet.group == 'cats' end # => true
# File lib/duck_record/associations/embeds_many_proxy.rb, line 694
# File lib/duck_record/associations/embeds_many_proxy.rb, line 848 def prepend(*_args) raise NoMethodError, "prepend on association is not defined. Please use <<, push or append" end
# File lib/duck_record/associations/embeds_many_proxy.rb, line 748 def proxy_association @association end
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
# File lib/duck_record/associations/embeds_many_proxy.rb, line 217 def replace(other_array) proxy_association.replace(other_array) end
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>]
# File lib/duck_record/associations/embeds_many_proxy.rb, line 879 def reset proxy_association.reset self end
Same as first
except returns only the second record.
# File lib/duck_record/associations/embeds_many_proxy.rb, line 82
Same as first
except returns only the second-to-last record.
# File lib/duck_record/associations/embeds_many_proxy.rb, line 131
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.
# File lib/duck_record/associations/embeds_many_proxy.rb, line 604 def size proxy_association.size end
Same as first
except returns only the third record.
# File lib/duck_record/associations/embeds_many_proxy.rb, line 90
Same as first
except returns only the third-to-last record.
# File lib/duck_record/associations/embeds_many_proxy.rb, line 123
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> # ]
# File lib/duck_record/associations/embeds_many_proxy.rb, line 813 def to_ary proxy_association.target.dup end
# File lib/duck_record/associations/embeds_many_proxy.rb, line 550 def uniq proxy_association.uniq end