aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/associations/collection_proxy.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record/associations/collection_proxy.rb')
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb133
1 files changed, 81 insertions, 52 deletions
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index 84c8cfe72b..fe693cfbb6 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -29,10 +29,11 @@ module ActiveRecord
# instantiation of the actual post records.
class CollectionProxy < Relation
delegate(*(ActiveRecord::Calculations.public_instance_methods - [:count]), to: :scope)
+ delegate :find_nth, to: :scope
def initialize(klass, association) #:nodoc:
@association = association
- super klass, klass.arel_table
+ super klass, klass.arel_table, klass.predicate_builder
merge! association.scope(nullify: false)
end
@@ -111,7 +112,7 @@ module ActiveRecord
end
# Finds an object in the collection responding to the +id+. Uses the same
- # rules as <tt>ActiveRecord::Base.find</tt>. Returns <tt>ActiveRecord::RecordNotFound</tt>
+ # rules as ActiveRecord::Base.find. Returns ActiveRecord::RecordNotFound
# error if the object cannot be found.
#
# class Person < ActiveRecord::Base
@@ -126,7 +127,7 @@ module ActiveRecord
# # ]
#
# 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(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>
@@ -170,27 +171,27 @@ module ActiveRecord
@association.first(*args)
end
- # Same as +first+ except returns only the second record.
+ # Same as #first except returns only the second record.
def second(*args)
@association.second(*args)
end
- # Same as +first+ except returns only the third record.
+ # Same as #first except returns only the third record.
def third(*args)
@association.third(*args)
end
- # Same as +first+ except returns only the fourth record.
+ # Same as #first except returns only the fourth record.
def fourth(*args)
@association.fourth(*args)
end
- # Same as +first+ except returns only the fifth record.
+ # Same as #first except returns only the fifth record.
def fifth(*args)
@association.fifth(*args)
end
- # Same as +first+ except returns only the forty second record.
+ # Same as #first except returns only the forty second record.
# Also known as accessing "the reddit".
def forty_two(*args)
@association.forty_two(*args)
@@ -226,6 +227,35 @@ module ActiveRecord
@association.last(*args)
end
+ # Gives a record (or N records if a parameter is supplied) from the collection
+ # using the same rules as <tt>ActiveRecord::Base.take</tt>.
+ #
+ # 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) # => []
+ def take(n = nil)
+ @association.take(n)
+ 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
@@ -285,7 +315,7 @@ module ActiveRecord
@association.create(attributes, &block)
end
- # Like +create+, except that if the record is invalid, raises an exception.
+ # Like #create, except that if the record is invalid, raises an exception.
#
# class Person
# has_many :pets
@@ -302,8 +332,8 @@ module ActiveRecord
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+
+ # 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
@@ -355,14 +385,15 @@ module ActiveRecord
@association.replace(other_array)
end
- # Deletes all the records from the collection. For +has_many+ associations,
- # the deletion is done according to the strategy specified by the <tt>:dependent</tt>
- # option.
+ # 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.
#
- # If no <tt>:dependent</tt> option is given, then it will follow the
- # default strategy. The default strategy is <tt>:nullify</tt>. This
- # sets the foreign keys to <tt>NULL</tt>. For, +has_many+ <tt>:through</tt>,
- # the default strategy is +delete_all+.
+ # For <tt>has_many :through</tt> 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
@@ -393,9 +424,9 @@ module ActiveRecord
# # #<Pet id: 3, name: "Choo-Choo", person_id: nil>
# # ]
#
- # If it is set to <tt>:destroy</tt> all the objects from the collection
- # are removed by calling their +destroy+ method. See +destroy+ for more
- # information.
+ # Both +has_many+ and <tt>has_many :through</tt> 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
@@ -410,14 +441,9 @@ module ActiveRecord
# # ]
#
# 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>
- # # ]
#
# Pet.find(1, 2, 3)
- # # => ActiveRecord::RecordNotFound
+ # # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
#
# If it is set to <tt>:delete_all</tt>, all the objects are deleted
# *without* calling their +destroy+ method.
@@ -437,14 +463,15 @@ module ActiveRecord
# person.pets.delete_all
#
# Pet.find(1, 2, 3)
- # # => ActiveRecord::RecordNotFound
+ # # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
def delete_all(dependent = nil)
@association.delete_all(dependent)
end
# Deletes the records of the collection directly from the database
- # ignoring the +:dependent+ option. It invokes +before_remove+,
- # +after_remove+ , +before_destroy+ and +after_destroy+ callbacks.
+ # 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
@@ -468,15 +495,16 @@ module ActiveRecord
@association.destroy_all
end
- # Deletes the +records+ supplied and removes them from the collection. For
- # +has_many+ associations, the deletion is done according to the strategy
- # specified by the <tt>:dependent</tt> option. Returns an array with the
+ # 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.
#
- # If no <tt>:dependent</tt> option is given, then it will follow the default
- # strategy. The default strategy is <tt>:nullify</tt>. This sets the foreign
- # keys to <tt>NULL</tt>. For, +has_many+ <tt>:through</tt>, the default
- # strategy is +delete_all+.
+ # For <tt>has_many :through</tt> 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
@@ -529,7 +557,7 @@ module ActiveRecord
# # => [#<Pet id: 2, name: "Spook", person_id: 1>]
#
# Pet.find(1, 3)
- # # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (1, 3)
+ # # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 3)
#
# If it is set to <tt>:delete_all</tt>, all the +records+ are deleted
# *without* calling their +destroy+ method.
@@ -557,7 +585,7 @@ module ActiveRecord
# # ]
#
# Pet.find(1)
- # # => ActiveRecord::RecordNotFound: Couldn't find Pet with id=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.
@@ -621,7 +649,7 @@ module ActiveRecord
# person.pets.size # => 0
# person.pets # => []
#
- # Pet.find(1, 2, 3) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (1, 2, 3)
+ # Pet.find(1, 2, 3) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (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.
@@ -653,7 +681,7 @@ module ActiveRecord
# person.pets.size # => 0
# person.pets # => []
#
- # Pet.find(4, 5, 6) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (4, 5, 6)
+ # Pet.find(4, 5, 6) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (4, 5, 6)
def destroy(*records)
@association.destroy(*records)
end
@@ -690,10 +718,8 @@ module ActiveRecord
# # #<Pet id: 2, name: "Spook", person_id: 1>,
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# # ]
- def count(column_name = nil, options = {})
- # TODO: Remove options argument as soon we remove support to
- # activerecord-deprecated_finders.
- @association.count(column_name, options)
+ def count(column_name = nil)
+ @association.count(column_name)
end
# Returns the size of the collection. If the collection hasn't been loaded,
@@ -780,10 +806,10 @@ module ActiveRecord
# person.pets.any? # => false
#
# person.pets << Pet.new(name: 'Snoop')
- # person.pets.count # => 0
+ # person.pets.count # => 1
# person.pets.any? # => true
#
- # You can also pass a block to define criteria. The behavior
+ # 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.
#
@@ -817,7 +843,7 @@ module ActiveRecord
# person.pets.count # => 2
# person.pets.many? # => true
#
- # You can also pass a block to define criteria. The
+ # 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.
#
@@ -841,7 +867,7 @@ module ActiveRecord
@association.many?(&block)
end
- # Returns +true+ if the given object is present in the collection.
+ # Returns +true+ if the given +record+ is present in the collection.
#
# class Person < ActiveRecord::Base
# has_many :pets
@@ -855,7 +881,7 @@ module ActiveRecord
!!@association.include?(record)
end
- def arel
+ def arel #:nodoc:
scope.arel
end
@@ -879,7 +905,7 @@ module ActiveRecord
# Equivalent to <tt>Array#==</tt>. 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
+ # to the corresponding element in the +other+ array, otherwise returns
# +false+.
#
# class Person < ActiveRecord::Base
@@ -970,12 +996,15 @@ module ActiveRecord
alias_method :append, :<<
def prepend(*args)
- raise NoMethodError, "prepend on association is not defined. Please use << or append"
+ raise NoMethodError, "prepend on association is not defined. Please use <<, push or append"
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.
def clear
delete_all
self