Skip to content

Instantly share code, notes, and snippets.

@naw
Created October 15, 2015 19:33
Show Gist options
  • Save naw/47f814ffd106bdd57feb to your computer and use it in GitHub Desktop.
Save naw/47f814ffd106bdd57feb to your computer and use it in GitHub Desktop.
has_many_through nested_attributes setter vs collection setter
# This is not a bug report
#
# The purpose of this file is to illustrate
# differences in how Rails treats assignment
# to a nested_attributes setter vs. assignment
# to a collection directly.
#
# In the former, existing records are _not_ deleted if they are not present in the assigned data.
# In the latter, existing records _are_ deleted if they are not present in the assigned data.
begin
require 'bundler/inline'
rescue LoadError => e
$stderr.puts 'Bundler version 1.10 or later is required. Please update your Bundler'
raise e
end
gemfile(true) do
source 'https://rubygems.org'
# Activate the gem you are reporting the issue against.
gem 'activerecord', '4.2.2'
gem 'sqlite3'
gem 'database_cleaner'
end
require 'active_record'
require 'database_cleaner'
require 'minitest/autorun'
require 'logger'
# Ensure backward compatibility with Minitest 4
Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test)
# This connection will do for database-independent bug reports.
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Schema.define do
create_table :authors, force: true do |t|
t.string :name
end
create_table :books, force: true do |t|
t.string :name
end
create_table :authorships, force: true do |t|
t.integer :book_id
t.integer :author_id
t.integer :num_chapters_contributed
end
end
class Book < ActiveRecord::Base
has_many :authorships
has_many :authors, through: :authorships
end
class Author < ActiveRecord::Base
has_many :authorships, dependent: :destroy
has_many :books, through: :authorships
accepts_nested_attributes_for :authorships
end
class Authorship < ActiveRecord::Base
belongs_to :book
belongs_to :author
end
class BugTest < Minitest::Test
def setup
DatabaseCleaner.start
@author = Author.create(name: 'Ed')
@book1 = Book.create(name: 'Elephants')
@book2 = Book.create(name: 'Frogs')
@book3 = Book.create(name: 'Gazelles')
end
def teardown
DatabaseCleaner.clean
end
def test_assigning_through_nested_attributes
@author.authorships_attributes = [ { book_id: @book1.id, num_chapters_contributed: 5 },
{ book_id: @book2.id, num_chapters_contributed: 4} ]
@author.save
# We want this to delete the first two authorships, but it doesn't
@author.authorships_attributes = [ { book_id: @book3.id, num_chapters_contributed: 3 } ]
@author.save
assert_equal 1, @author.reload.books.count
end
def test_assigning_to_join_collection_directly
@author.authorships = [Authorship.create(book_id: @book1.id, num_chapters_contributed: 5),
Authorship.create(book_id: @book2.id, num_chapters_contributed: 4)]
@author.save
assert_equal 2, @author.reload.books.count
assert_equal 2, Authorship.count
# Ensure author_id is assigned automatically
Authorship.all.each do |authorship|
assert_equal @author.id, authorship.author_id
end
# We want this to delete this assignment to delete the first two authorships
@author.authorships = [Authorship.new(book_id: @book3.id, num_chapters_contributed: 3)]
@author.save
assert_equal 1, @author.reload.books.count
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment