Class: ActiveRecord::Associations::CollectionProxy

Inherits:
Relation show all
Defined in:
activerecord/lib/active_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.

Constant Summary

Constants inherited from Relation

Relation::CLAUSE_METHODS, Relation::INVALID_METHODS_FOR_DELETE_ALL, Relation::MULTI_VALUE_METHODS, Relation::SINGLE_VALUE_METHODS, Relation::VALUE_METHODS

Constants included from FinderMethods

FinderMethods::ONE_AS_ONE

Constants included from QueryMethods

QueryMethods::FROZEN_EMPTY_ARRAY, QueryMethods::FROZEN_EMPTY_HASH, QueryMethods::VALID_UNSCOPING_VALUES

Constants included from Batches

Batches::ORDER_OR_LIMIT_IGNORED_MESSAGE

Instance Attribute Summary

Attributes inherited from Relation

#klass, #loaded, #predicate_builder, #table

Instance Method Summary collapse

Methods inherited from Relation

#_update_record, #arel_attribute, #as_json, #blank?, #cache_key, #eager_loading?, #encode_with, #explain, #find_or_create_by, #find_or_create_by!, #find_or_initialize_by, #first_or_create, #first_or_create!, #first_or_initialize, #initialize_copy, #insert, #inspect, #joined_includes_values, #load, #none?, #one?, #pretty_print, #scope_for_create, #substitute_values, #to_sql, #uniq_value, #update, #update_all, #values, #where_values_hash

Methods included from FinderMethods

#exists?, #fifth!, #find_by, #find_by!, #first!, #forty_two!, #fourth!, #last!, #raise_record_not_found_exception!, #second!, #second_to_last!, #take!, #third!, #third_to_last!

Methods included from Calculations

#average, #ids, #maximum, #minimum, #sum

Methods included from SpawnMethods

#except, #merge, #merge!, #only

Methods included from QueryMethods

#_select!, #arel, #bound_attributes, #create_with, #create_with!, #create_with_value, #distinct!, #eager_load, #eager_load!, #extending, #extending!, #from, #from!, #group, #group!, #having, #having!, #includes, #includes!, #joins, #joins!, #left_outer_joins, #left_outer_joins!, #limit, #limit!, #lock, #lock!, #none, #none!, #offset, #offset!, #or, #or!, #order, #order!, #preload, #preload!, #readonly, #readonly!, #references, #references!, #reorder, #reorder!, #reverse_order, #reverse_order!, #rewhere, #unscope, #unscope!, #where, #where!

Methods included from ActiveSupport::Concern

#append_features, #class_methods, extended, #included

Methods included from Batches

#find_each, #find_in_batches, #in_batches

Methods included from Explain

#collecting_queries_for_explain, #exec_explain

Methods included from Delegation

#respond_to?

Methods included from Enumerable

#as_json, #exclude?, #index_by, #sum, #without

Constructor Details

#initialize(klass, association) ⇒ CollectionProxy

:nodoc:


33
34
35
36
37
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 33

def initialize(klass, association) #:nodoc:
  @association = association
  super klass, klass.arel_table, klass.predicate_builder
  merge! association.scope(nullify: false)
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class ActiveRecord::Delegation

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>
#    ]


1009
1010
1011
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 1009

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


943
944
945
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 943

def ==(other)
  load_target == other
end

#any?(&block) ⇒ Boolean

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


845
846
847
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 845

def any?(&block)
  @association.any?(&block)
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

292
293
294
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 292

def build(attributes = {}, &block)
  @association.build(attributes, &block)
end

#calculate(operation, column_name) ⇒ Object


734
735
736
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 734

def calculate(operation, column_name)
  null_scope? ? scope.calculate(operation, column_name) : super
end

#clearObject

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.


1025
1026
1027
1028
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 1025

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

367
368
369
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 367

def concat(*records)
  @association.concat(*records)
end

#count(column_name = nil) ⇒ Object

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>
#    ]


730
731
732
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 730

def count(column_name = nil)
  @association.count(column_name)
end

#create(attributes = {}, &block) ⇒ Object

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>
#    ]


323
324
325
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 323

def create(attributes = {}, &block)
  @association.create(attributes, &block)
end

#create!(attributes = {}, &block) ⇒ Object

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


339
340
341
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 339

def create!(attributes = {}, &block)
  @association.create!(attributes, &block)
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>
#    ]


622
623
624
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 622

def delete(*records)
  @association.delete(*records)
end

#delete_all(dependent = nil) ⇒ 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)


476
477
478
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 476

def delete_all(dependent = nil)
  @association.delete_all(dependent)
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)

694
695
696
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 694

def destroy(*records)
  @association.destroy(*records)
end

#destroy_allObject

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

503
504
505
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 503

def destroy_all
  @association.destroy_all
end

#distinctObject Also known as: uniq

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">]


712
713
714
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 712

def distinct
  @association.distinct
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

812
813
814
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 812

def empty?
  @association.empty?
end

#fifth(*args) ⇒ Object

Same as #first except returns only the fifth record.


189
190
191
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 189

def fifth(*args)
  @association.fifth(*args)
end

#find(*args, &block) ⇒ Object

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>
#    ]


139
140
141
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 139

def find(*args, &block)
  @association.find(*args, &block)
end

#first(*args) ⇒ Object

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) # => []

169
170
171
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 169

def first(*args)
  @association.first(*args)
end

#forty_two(*args) ⇒ Object

Same as #first except returns only the forty second record. Also known as accessing “the reddit”.


195
196
197
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 195

def forty_two(*args)
  @association.forty_two(*args)
end

#fourth(*args) ⇒ Object

Same as #first except returns only the fourth record.


184
185
186
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 184

def fourth(*args)
  @association.fourth(*args)
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

897
898
899
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 897

def include?(record)
  !!@association.include?(record)
end

#last(*args) ⇒ Object

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) # => []

235
236
237
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 235

def last(*args)
  @association.last(*args)
end

#lengthObject

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>
#    ]


790
791
792
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 790

def length
  @association.length
end

#load_targetObject


43
44
45
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 43

def load_target
  @association.load_target
end

#loaded?Boolean

Returns true if the association has been loaded, otherwise false.

person.pets.loaded? # => false
person.pets
person.pets.loaded? # => true

52
53
54
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 52

def loaded?
  @association.loaded?
end

#many?(&block) ⇒ Boolean

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


883
884
885
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 883

def many?(&block)
  @association.many?(&block)
end

#pluck(*column_names) ⇒ Object


738
739
740
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 738

def pluck(*column_names)
  null_scope? ? scope.pluck(*column_names) : super
end

#prepend(*args) ⇒ Object

Raises:

  • (NoMethodError)

1015
1016
1017
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 1015

def prepend(*args)
  raise NoMethodError, "prepend on association is not defined. Please use <<, push or append"
end

#proxy_associationObject


901
902
903
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 901

def proxy_association
  @association
end

#recordsObject

:nodoc:


985
986
987
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 985

def records # :nodoc:
  load_target
end

#reloadObject

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>]

1048
1049
1050
1051
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 1048

def reload
  proxy_association.reload
  self
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


393
394
395
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 393

def replace(other_array)
  @association.replace(other_array)
end

#resetObject

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>]

1069
1070
1071
1072
1073
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 1069

def reset
  proxy_association.reset
  proxy_association.reset_scope
  self
end

#scopeObject Also known as: spawn

Returns a Relation object for the records in this association


914
915
916
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 914

def scope
  @association.scope
end

#scopingObject

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.


909
910
911
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 909

def scoping
  @association.scope.scoping { yield }
end

#second(*args) ⇒ Object

Same as #first except returns only the second record.


174
175
176
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 174

def second(*args)
  @association.second(*args)
end

#second_to_last(*args) ⇒ Object

Same as #first except returns only the second-to-last record.


205
206
207
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 205

def second_to_last(*args)
  @association.second_to_last(*args)
end

#select(*fields, &block) ⇒ Object

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">
#    ]


109
110
111
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 109

def select(*fields, &block)
  @association.select(*fields, &block)
end

#sizeObject

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.

766
767
768
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 766

def size
  @association.size
end

#take(n = nil) ⇒ Object

Gives a record (or N records if a parameter is supplied) from the collection using the same rules as ActiveRecord::Base.take.

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.take # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>

person.pets.take(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.take    # => nil
another_person_without.pets.take(2) # => []

264
265
266
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 264

def take(n = nil)
  @association.take(n)
end

#targetObject


39
40
41
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 39

def target
  @association.target
end

#third(*args) ⇒ Object

Same as #first except returns only the third record.


179
180
181
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 179

def third(*args)
  @association.third(*args)
end

#third_to_last(*args) ⇒ Object

Same as #first except returns only the third-to-last record.


200
201
202
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 200

def third_to_last(*args)
  @association.third_to_last(*args)
end

#to_aryObject 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>
#    ]


980
981
982
# File 'activerecord/lib/active_record/associations/collection_proxy.rb', line 980

def to_ary
  load_target.dup
end