class ActiveRecord::Associations::CollectionProxy
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.
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/active_record/associations/collection_proxy.rb, line 969 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/active_record/associations/collection_proxy.rb, line 907 def ==(other) load_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 # => 0 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/active_record/associations/collection_proxy.rb, line 805 def any?(&block) @association.any?(&block) end
# File lib/active_record/associations/collection_proxy.rb, line 861 def arel scope.arel end
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/active_record/associations/collection_proxy.rb, line 258 def build(attributes = {}, &block) @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.
# File lib/active_record/associations/collection_proxy.rb, line 982 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/active_record/associations/collection_proxy.rb, line 333 def concat(*records) @association.concat(*records) end
Count all records using SQL.
class Person < ActiveRecord::Base has_many :pets end 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> # ]
# File lib/active_record/associations/collection_proxy.rb, line 696 def count(column_name = nil, options = {}) # TODO: Remove options argument as soon we remove support to # activerecord-deprecated_finders. @association.count(column_name, options) end
Returns a new object of the collection type that has been instantiated with attributes, linked to this object and that has already been saved (if it passes the validations).
class Person has_many :pets end person.pets.create(name: 'Fancy-Fancy') # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1> person.pets.create([{name: 'Spook'}, {name: 'Choo-Choo'}]) # => [ # #<Pet id: 2, name: "Spook", person_id: 1>, # #<Pet id: 3, name: "Choo-Choo", person_id: 1> # ] person.pets.size # => 3 person.pets.count # => 3 person.pets.find(1, 2, 3) # => [ # #<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/active_record/associations/collection_proxy.rb, line 289 def create(attributes = {}, &block) @association.create(attributes, &block) end
Like create
, except that if the record is invalid, raises an
exception.
class Person has_many :pets end class Pet validates :name, presence: true end person.pets.create!(name: nil) # => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
# File lib/active_record/associations/collection_proxy.rb, line 305 def create!(attributes = {}, &block) @association.create!(attributes, &block) end
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 IDs (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 Fixnum
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/active_record/associations/collection_proxy.rb, line 588 def delete(*records) @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
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
# File lib/active_record/associations/collection_proxy.rb, line 442 def delete_all(dependent = nil) @association.delete_all(dependent) 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 IDs (1, 2, 3)
You can pass Fixnum
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 IDs (4, 5, 6)
# File lib/active_record/associations/collection_proxy.rb, line 660 def destroy(*records) @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/active_record/associations/collection_proxy.rb, line 469 def destroy_all @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">]
# File lib/active_record/associations/collection_proxy.rb, line 678 def distinct @association.distinct end
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/active_record/associations/collection_proxy.rb, line 772 def empty? @association.empty? end
Same as first
except returns only the fifth record.
# File lib/active_record/associations/collection_proxy.rb, line 190 def fifth(*args) @association.fifth(*args) end
Finds an object in the collection responding to the id
. Uses
the same rules as ActiveRecord::Base.find
. Returns
ActiveRecord::RecordNotFound
error if the object cannot be
found.
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.find(1) # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1> person.pets.find(4) # => ActiveRecord::RecordNotFound: Couldn't find Pet with id=4 person.pets.find(2) { |pet| pet.name.downcase! } # => #<Pet id: 2, name: "fancy-fancy", person_id: 1> person.pets.find(2, 3) # => [ # #<Pet id: 2, name: "Spook", person_id: 1>, # #<Pet id: 3, name: "Choo-Choo", person_id: 1> # ]
# File lib/active_record/associations/collection_proxy.rb, line 140 def find(*args, &block) @association.find(*args, &block) end
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/active_record/associations/collection_proxy.rb, line 170 def first(*args) @association.first(*args) end
Same as first
except returns only the forty second record.
Also known as accessing “the reddit”.
# File lib/active_record/associations/collection_proxy.rb, line 196 def forty_two(*args) @association.forty_two(*args) end
Same as first
except returns only the fourth record.
# File lib/active_record/associations/collection_proxy.rb, line 185 def fourth(*args) @association.fourth(*args) end
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/active_record/associations/collection_proxy.rb, line 857 def include?(record) !!@association.include?(record) end
Returns the last record, or the last 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.last # => #<Pet id: 3, name: "Choo-Choo", person_id: 1> person.pets.last(2) # => [ # #<Pet id: 2, name: "Spook", person_id: 1>, # #<Pet id: 3, name: "Choo-Choo", person_id: 1> # ] another_person_without.pets # => [] another_person_without.pets.last # => nil another_person_without.pets.last(3) # => []
# File lib/active_record/associations/collection_proxy.rb, line 226 def last(*args) @association.last(*args) 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/active_record/associations/collection_proxy.rb, line 750 def length @association.length end
# File lib/active_record/associations/collection_proxy.rb, line 44 def load_target @association.load_target end
Returns true
if the association has been loaded, otherwise
false
.
person.pets.loaded? # => false person.pets person.pets.loaded? # => true
# File lib/active_record/associations/collection_proxy.rb, line 53 def loaded? @association.loaded? end
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/active_record/associations/collection_proxy.rb, line 843 def many?(&block) @association.many?(&block) end
# File lib/active_record/associations/collection_proxy.rb, line 975 def prepend(*args) raise NoMethodError, "prepend on association is not defined. Please use << or append" end
# File lib/active_record/associations/collection_proxy.rb, line 865 def proxy_association @association end
Reloads the collection from the database. Returns self
.
Equivalent to collection(true)
.
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.reload # fetches pets from the database # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>] person.pets(true) # fetches pets from the database # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
# File lib/active_record/associations/collection_proxy.rb, line 1005 def reload proxy_association.reload self 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/active_record/associations/collection_proxy.rb, line 359 def replace(other_array) @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/active_record/associations/collection_proxy.rb, line 1026 def reset proxy_association.reset proxy_association.reset_scope self end
Returns a Relation
object for the records in this association
# File lib/active_record/associations/collection_proxy.rb, line 878 def scope @association.scope end
We don't want this object to be put on the scoping stack, because that could create an infinite loop where we call an @association method, which gets the current scope, which is this object, which delegates to @association, and so on.
# File lib/active_record/associations/collection_proxy.rb, line 873 def scoping @association.scope.scoping { yield } end
Same as first
except returns only the second record.
# File lib/active_record/associations/collection_proxy.rb, line 175 def second(*args) @association.second(*args) end
Works in two ways.
First: Specify a subset of fields to be selected from the result set.
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.select(:name) # => [ # #<Pet id: nil, name: "Fancy-Fancy">, # #<Pet id: nil, name: "Spook">, # #<Pet id: nil, name: "Choo-Choo"> # ] person.pets.select(:id, :name ) # => [ # #<Pet id: 1, name: "Fancy-Fancy">, # #<Pet id: 2, name: "Spook">, # #<Pet id: 3, name: "Choo-Choo"> # ]
Be careful because this also means you're initializing a model object
with only the fields that you've selected. If you attempt to access a
field except id
that is not in the initialized record
you'll receive:
person.pets.select(:name).first.person_id # => ActiveModel::MissingAttributeError: missing attribute: person_id
Second: You can pass a block so it can be used just like Array#select. This builds an array of objects from the database for the scope, converting them into an array and iterating through them using Array#select.
person.pets.select { |pet| pet.name =~ /oo/ } # => [ # #<Pet id: 2, name: "Spook", person_id: 1>, # #<Pet id: 3, name: "Choo-Choo", person_id: 1> # ] person.pets.select(:name) { |pet| pet.name =~ /oo/ } # => [ # #<Pet id: 2, name: "Spook">, # #<Pet id: 3, name: "Choo-Choo"> # ]
# File lib/active_record/associations/collection_proxy.rb, line 110 def select(*fields, &block) @association.select(*fields, &block) end
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/active_record/associations/collection_proxy.rb, line 726 def size @association.size end
# File lib/active_record/associations/collection_proxy.rb, line 230 def take(n = nil) @association.take(n) end
# File lib/active_record/associations/collection_proxy.rb, line 40 def target @association.target end
Same as first
except returns only the third record.
# File lib/active_record/associations/collection_proxy.rb, line 180 def third(*args) @association.third(*args) end
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/active_record/associations/collection_proxy.rb, line 944 def to_ary load_target.dup end