Collection has a different setter logic for persistent and new record.

New record writes changes to the database only after save is triggered, 
persisted record on the contrary writes changes to the database once ids or 
array of records assigned via setter.  

This inconsistency leads to a different issues, even not related to the 
inconsistency between new and persisted record.
I just list some of them I already faced with in a production:


   - *Validation raises an error.* Lets asume we have has_many records, 
   which should create a has_many through associations while passing ids to 
   the parent record via *collection_ids*. In case when has_many through 
   will have a validation error - this will raise an error, which only could 
   be rescued, even if record is saved with soft `*save*`, not `*save!*`.
   - *Collection assignment writes changes at the moment of passing data 
   via setter, not while `save` is invoked.* Lets asume I have a model with 
   a lot of different associations. This model feeded via large form with 
   different ids and other fields. When I set a parameters through a `
   *assign_attributes*` method (without any transaction block), and save 
   returns false because of the validation failed, collection ids will be 
   changed anyway. One of the solutions would be use transactions and raise an 
   exception with *ActiveRecord::Rollback*. Other solution would be use 
   *update_attributes* or *update* in the latest version of rails. Other 
   one is to use undocumented *with_transaction_returning_status**.*
   - *Difference between new and persisted record.* I believe there are not 
   many developers who work with rails framework know about the difference 
   between new and persisted record with has_many and has_many through records.
   - *has_one works differently.* Means you can update _id of a has_many or 
   belongs_to via setter and it will not modify anything in the database.

Main case to reproduce an issue:

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";
  gem "rails", path: "../projects/rails/"
  gem "sqlite3"
end


require "active_record"
require "minitest/autorun"
require "logger"


# 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 :posts, force: true do |t|
  end


  create_table :comments, force: true do |t|
    t.integer :post_id
  end
end


class Post < ActiveRecord::Base
  has_many :comments
end


class Comment < ActiveRecord::Base
  belongs_to :post
end


class BugTest < Minitest::Test
  def test_association_stuff
    comment = Comment.create!
    attributes = {comment_ids: [comment.id]}
    
    #new_post = Post.new(attributes)
    #assert_equal 1, new_post.comments.size


    post = Post.create!
    post.assign_attributes(attributes)


    assert_equal 1, post.comments.size
    assert_equal 0, post.comments.count


    post.save!


    assert_equal 1, post.comments.size
    assert_equal 1, post.comments.count
  end
end

Please let me know if you would like to see the other cases I've listed 
above.

Solution would be to change behaviour for the autosave has_many records, so 
the associations of the autosave records will never modify database on data 
passed to a setter.

-- 
You received this message because you are subscribed to the Google Groups "Ruby 
on Rails: Core" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to rubyonrails-core+unsubscr...@googlegroups.com.
To post to this group, send email to rubyonrails-core@googlegroups.com.
Visit this group at https://groups.google.com/group/rubyonrails-core.
For more options, visit https://groups.google.com/d/optout.

Reply via email to