I prefer to build the new state in an instance before committing (after validate passes). For example, I have groups and users, and use memberships to associate them:
class Membership < AR::Base belongs_to :user belongs_to :group end
class Group < AR::Base has_many :memberships has_many :users, :through => :memberships end
class User < AR::Base has_many :memberships has_many :groups, :through => :memberships end
I add users to groups in the groups controller, and send an array of user id's to the groups controller like so: group[:users] - then in the controller:
def update group = Group.find(params[:id]) group.update_attributes(params[:group]) group.save end
And then in Group:
def users=(args) memberships.clear User.find(args).each do |user| memberships.build(:user => user, :group => group) end end
First question: The problem is, that memberships.clear hits the DB immediatly. I would rather clear the association in memory and then do the delete/inserts upon commit. Is there an operation equivalent to build which just removes a membership from the association - but only in memory?
Second question: Using the users=(args) approach is convenient, but somehow not quite right because the method could rely on other properties of the group instance, eg. is_open_for_new_users? - I'm thinking of adding a set_users method instead, and then in the controller:
group.update_attributes(params[:group]) group.set_users(params[:users]) group.save
Any thoughts on the pros/cons of these approaches? I'm not quite certain where to put the transaction when updating multiple model types, but I guess that placing it in the controller, wrapping all operations on group in the transaction is a way.
Any insight much appreciated.