Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #6466: fix_add_and_delete_for_has_many_through.diff

File fix_add_and_delete_for_has_many_through.diff, 6.9 kB (added by naffis, 2 years ago)

fix add and delete for has many through, includes test

  • test/associations/join_model_test.rb

    old new  
    66require 'fixtures/author' 
    77require 'fixtures/category' 
    88require 'fixtures/categorization' 
     9require 'fixtures/book' 
     10require 'fixtures/citation' 
    911 
    1012class AssociationsJoinModelTest < Test::Unit::TestCase 
    1113  self.use_transactional_fixtures = false 
    12   fixtures :posts, :authors, :categories, :categorizations, :comments, :tags, :taggings, :author_favorites 
     14  fixtures :posts, :authors, :categories, :categorizations, :comments, :tags, :taggings, :author_favorites, :books 
    1315 
    1416  def test_has_many 
    1517    assert authors(:david).categories.include?(categories(:general)) 
     
    424426    tags = posts(:thinking).tags 
    425427    assert_equal tags, posts(:thinking).tags.push(tags(:general)) 
    426428  end 
     429   
     430  def test_delete_associate_when_deleting_from_has_many_through_with_non_standard_id 
     431    count = books(:awdr).references.count 
     432    references_before = books(:awdr).references 
     433    book = Book.create!(:name => 'Getting Real') 
     434    book_awdr = books(:awdr) 
     435    book_awdr.references << book 
     436    assert_equal(count + 1, book_awdr.references(true).size) 
    427437 
     438    assert_nothing_raised { book_awdr.references.delete(book) } 
     439    assert_equal(count, book_awdr.references.size) 
     440    assert_equal(count, book_awdr.references(true).size) 
     441    assert_equal(references_before.sort, book_awdr.references.sort) 
     442  end 
     443   
    428444  def test_delete_associate_when_deleting_from_has_many_through 
    429445    count = posts(:thinking).tags.count 
    430446    tags_before = posts(:thinking).tags 
  • test/fixtures/citation.rb

    old new  
     1class Citation < ActiveRecord::Base 
     2  belongs_to :reference_of, :class_name => "Book", :foreign_key => :book2_id  
     3  belongs_to :book1, :class_name => "Book", :foreign_key => :book1_id  
     4  belongs_to :book2, :class_name => "Book", :foreign_key => :book2_id  
     5 
     6end 
  • test/fixtures/db_definitions/schema.rb

    old new  
    5757  create_table :lock_with_custom_column_without_defaults, :force => true do |t| 
    5858    t.column :custom_lock_version, :integer 
    5959  end 
     60   
     61  create_table :books, :force => true do |t| 
     62    t.column :name, :string 
     63  end 
     64   
     65  create_table :citations, :id => false, :force => true do |t| 
     66    t.column :book1_id, :integer 
     67    t.column :book2_id, :integer 
     68  end 
     69   
    6070end 
  • test/fixtures/book.rb

    old new  
     1class Book < ActiveRecord::Base 
     2  has_many :citations, :foreign_key => 'book1_id'   
     3  has_many :references, :through => :citations, :source => :reference_of, :uniq => true 
     4 
     5end 
  • test/fixtures/books.yml

    old new  
     1awdr: 
     2  id: 1 
     3  name: "Agile Web Development with Rails" 
     4 
     5rfr: 
     6  id: 2 
     7  name: "Ruby for Rails" 
  • lib/active_record/associations/has_many_through_association.rb

    old new  
    7171 
    7272      # Remove +records+ from this association.  Does not destroy +records+. 
    7373      def delete(*records) 
    74         records = flatten_deeper(records) 
    75         records.each { |associate| raise_on_type_mismatch(associate) } 
    76         records.reject! { |associate| @target.delete(associate) if associate.new_record? } 
    77         return if records.empty? 
    78          
    79         @delete_join_finder ||= "find_all_by_#{@reflection.source_reflection.association_foreign_key}" 
    80         through = @reflection.through_reflection 
    81         through.klass.transaction do 
    82           records.each do |associate| 
    83             joins = @owner.send(through.name).send(@delete_join_finder, associate.id) 
    84             @owner.send(through.name).delete(joins) 
    85             @target.delete(associate) 
    86           end 
    87         end 
    88       end 
     74         return if records.empty? 
     75         records.each { |associate| raise_on_type_mismatch(associate) } 
     76         through = @reflection.through_reflection 
     77         raise ActiveRecord::HasManyThroughCantDisassociateNewRecords.new(@owner, through) if @owner.new_record? 
    8978 
     79         load_target 
     80 
     81         klass = through.klass 
     82         klass.transaction do 
     83           flatten_deeper(records).each do |associate| 
     84             raise_on_type_mismatch(associate) 
     85             raise ActiveRecord::HasManyThroughCantDisassociateNewRecords.new(@owner, through) unless associate.respond_to?(:new_record?) && !associate.new_record? 
     86 
     87             @owner.send(@reflection.through_reflection.name).proxy_target.delete(klass.delete_all(construct_join_attributes(associate))) 
     88             @target.delete(associate) 
     89           end 
     90         end 
     91 
     92         self   
     93       end 
     94 
    9095      def build(attrs = nil) 
    9196        raise ActiveRecord::HasManyThroughCantAssociateNewRecords.new(@owner, @reflection.through_reflection) 
    9297      end 
     
    138143 
    139144        # Construct attributes for :through pointing to owner and associate. 
    140145        def construct_join_attributes(associate) 
    141           construct_owner_attributes(@reflection.through_reflection).merge(@reflection.source_reflection.association_foreign_key => associate.id) 
     146          construct_owner_attributes(@reflection.through_reflection).merge(@reflection.source_reflection.primary_key_name => associate.id) 
    142147        end 
    143148 
    144149        # Associate attributes pointing to owner, quoted. 
  • lib/active_record/associations.rb

    old new  
    4444    end 
    4545  end 
    4646 
     47  class HasManyThroughCantDisassociateNewRecords < ActiveRecordError #:nodoc: 
     48    def initialize(owner, reflection) 
     49      super("Cannot disassociate new records through '#{owner.class.name}##{reflection.name}' on '#{reflection.source_reflection.class_name rescue nil}##{reflection.source_reflection.name rescue nil}'. Both records must have an id in order to delete the has_many :through record associating them.") 
     50    end 
     51  end 
     52   
    4753  class EagerLoadPolymorphicError < ActiveRecordError #:nodoc: 
    4854    def initialize(reflection) 
    4955      super("Can not eagerly load the polymorphic association #{reflection.name.inspect}")