Active record associations

I want to accomplish the following. I have an object that can be
edited. However, it can occur that two people might edit the object
at the same time. Instead of locking the object down, I prefer to
tell the user how their version differs from the last version that was
committed.

Kinda like:

respond_to do |format|
@document = Document.find_by_id(1)
@document.attributes = params[:document]
@changes = @document.diff(Document.find_by_id(1))
format.html { render :action => “edit” }
end

The edit screen then renders the changes for the user to compare. The
user can then apply some changes or ignore the changes and save.

This works fine for regular fields, but when the object has has_many
assoctiations, then the following would occur.

respond_to do |format|
@document = Document.find_by_id(1)
@document.attributes = params[:document] # => this call would
update the database for all has_many associations
@changes = @document.diff(Document.find_by_id(1)) #=> retrieving
the previous object state from the database is not possible since the
associations have changed
format.html { render :action => “edit” }
end

I also tried to compare the object to the attributes kinda like this;

respond_to do |format|
@document = Document.find_by_id(1)
@changes = @document.diff(params[:document])
format.html { render :action => “edit” }
end

But then the user’s changes aren’t stored on the object and all
updates are lost.

Any help is appreciated.

This sounds like the dirty object feature of Rails 2.1 would work
great:
http://ryandaigle.com/articles/2008/3/31/what-s-new-in-edge-rails-dirty-objects

Maybe even try to utilize some callback methods so you can capture and
display the changes to the user:
http://railsforum.com/viewtopic.php?id=23001

The dirty object feature doesn’t work for has_many associations. I
solved the problem by wrapping a transaction around the code to revert
the changes in the database. I kinda wish, ror could keep the
functionality between regular fields and association fields consistent
to avoid confusion.

kinda like:

class Person
has_many :cars
end

person = Person.find_by_name(“Jack”)
person.cars
=> [#<Car id: 2, name: “BMW”, created_at: “2008-11-04 03:04:46”,
updated_at: “2008-11-04 03:04:46”>]

person.cars=[Car.find_by_name(“Porsche”), Car.find_by_name(“VW”)]
person.cars
=> [#<Car id: 1, name: “Porsche”, created_at: “2008-11-04 03:04:46”,
updated_at: “2008-11-04 03:04:46”>, #<Car id: 3, name: “VW”,
created_at: “2008-11-04 03:04:46”, updated_at: “2008-11-04 03:04:46”>]

person.changed?
=> true

person.changes
=> {“cars”=>[[#<Car id: 2, name: “BMW”, created_at: “2008-11-04
03:04:46”, updated_at: “2008-11-04 03:04:46”>], [#<Car id: 1, name:
“Porsche”, created_at: “2008-11-04 03:04:46”, updated_at: “2008-11-04
03:04:46”>, #<Car id: 3, name: “VW”, created_at: “2008-11-04
03:04:46”, updated_at: “2008-11-04 03:04:46”>]]}

person.save #associations are saved in the database

However ror returns the following

person = Person.find_by_name(“Jack”)
person.cars
=> [#<Car id: 2, name: “BMW”, created_at: “2008-11-04 03:04:46”,
updated_at: “2008-11-04 03:04:46”>]

person.cars=[Car.find_by_name(“Porsche”), Car.find_by_name(“VW”)] #associations are saved in the database
person.cars
=> [#<Car id: 1, name: “Porsche”, created_at: “2008-11-04 03:04:46”,
updated_at: “2008-11-04 03:04:46”>, #<Car id: 3, name: “VW”,
created_at: “2008-11-04 03:04:46”, updated_at: “2008-11-04 03:04:46”>]

person.changed?
=> false

person.changes
=> {}