diff options
Diffstat (limited to 'activerecord/lib/active_record/associations/collection_proxy.rb')
-rw-r--r-- | activerecord/lib/active_record/associations/collection_proxy.rb | 161 |
1 files changed, 103 insertions, 58 deletions
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index 7c43e37cf2..060b2278d9 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -75,23 +75,23 @@ module ActiveRecord # # #<Pet id: nil, name: "Choo-Choo"> # # ] # - # person.pets.select([:id, :name]) + # 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 that is not in the initialized record you’ll + # 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 build an array of objects from the database for the scope, + # This builds an array of objects from the database for the scope, # converting them into an array and iterating through them using # Array#select. # @@ -106,13 +106,13 @@ module ActiveRecord # # #<Pet id: 2, name: "Spook">, # # #<Pet id: 3, name: "Choo-Choo"> # # ] - def select(select = nil, &block) - @association.select(select, &block) + def select(*fields, &block) + @association.select(*fields, &block) 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> - # error if the object can not be found. + # error if the object cannot be found. # # class Person < ActiveRecord::Base # has_many :pets @@ -170,6 +170,32 @@ module ActiveRecord @association.first(*args) end + # 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. + def third(*args) + @association.third(*args) + end + + # 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. + def fifth(*args) + @association.fifth(*args) + end + + # Same as +first+ except returns only the forty second record. + # Also known as accessing "the reddit". + def forty_two(*args) + @association.forty_two(*args) + 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. @@ -227,6 +253,7 @@ module ActiveRecord def build(attributes = {}, &block) @association.build(attributes, &block) end + alias_method :new, :build # 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 @@ -280,7 +307,7 @@ module ActiveRecord # so method calls may be chained. # # class Person < ActiveRecord::Base - # pets :has_many + # has_many :pets # end # # person.pets.size # => 0 @@ -302,7 +329,7 @@ module ActiveRecord @association.concat(*records) end - # Replace this collection with +other_array+. This will perform a diff + # Replaces this collection with +other_array+. This will perform a diff # and delete/add only records that have changed. # # class Person < ActiveRecord::Base @@ -328,14 +355,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. Returns an array with the deleted records. + # 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 +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 @@ -366,9 +394,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 +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 @@ -383,11 +411,6 @@ 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 @@ -408,21 +431,17 @@ 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 - def delete_all - @association.delete_all + def delete_all(dependent = nil) + @association.delete_all(dependent) end - # Deletes the records of the collection directly from the database. - # This will _always_ remove the records ignoring the +:dependent+ - # option. + # 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 @@ -648,11 +667,12 @@ module ActiveRecord # # #<Pet name: "Fancy-Fancy"> # # ] # - # person.pets.select(:name).uniq + # person.pets.select(:name).distinct # # => [#<Pet name: "Fancy-Fancy">] - def uniq - @association.uniq + def distinct + @association.distinct end + alias uniq distinct # Count all records using SQL. # @@ -668,6 +688,8 @@ module ActiveRecord # # #<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) end @@ -724,7 +746,7 @@ module ActiveRecord end # Returns +true+ if the collection is empty. If the collection has been - # loaded or the <tt>:counter_sql</tt> option is provided, it is equivalent + # loaded it is equivalent # to <tt>collection.size.zero?</tt>. If the collection has not been loaded, # it is equivalent to <tt>collection.exists?</tt>. If the collection has # not already been loaded and you are going to fetch the records anyway it @@ -758,7 +780,7 @@ module ActiveRecord # person.pets.count # => 0 # person.pets.any? # => true # - # You can also pass a block to define criteria. The behaviour + # 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. # @@ -785,15 +807,15 @@ module ActiveRecord # has_many :pets # end # - # person.pets.count #=> 1 - # person.pets.many? #=> false + # person.pets.count # => 1 + # person.pets.many? # => false # # person.pets << Pet.new(name: 'Snoopy') - # person.pets.count #=> 2 - # person.pets.many? #=> true + # person.pets.count # => 2 + # person.pets.many? # => true # - # You can also pass a block to define criteria. The - # behaviour is the same, it returns true if the collection + # 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 @@ -816,7 +838,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 @@ -827,10 +849,12 @@ module ActiveRecord # person.pets.include?(Pet.find(20)) # => true # person.pets.include?(Pet.find(21)) # => false def include?(record) - @association.include?(record) + !!@association.include?(record) end - alias_method :new, :build + def arel + scope.arel + end def proxy_association @association @@ -846,19 +870,13 @@ module ActiveRecord # Returns a <tt>Relation</tt> object for the records in this association def scope - association = @association - - @association.scope.extending! do - define_method(:proxy_association) { association } - end + @association.scope end - - # :nodoc: alias spawn scope # 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 @@ -923,7 +941,7 @@ module ActiveRecord alias_method :to_a, :to_ary # 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 + # to the association's primary key. Returns +self+, so several appends may be # chained together. # # class Person < ActiveRecord::Base @@ -946,6 +964,11 @@ module ActiveRecord proxy_association.concat(records) && self end alias_method :push, :<< + alias_method :append, :<< + + def prepend(*args) + raise NoMethodError, "prepend on association is not defined. Please use << 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 @@ -977,6 +1000,28 @@ module ActiveRecord proxy_association.reload self 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>] + def reset + proxy_association.reset + proxy_association.reset_scope + self + end end end end |