[ACCEPTED]-How to keep track of model history with mapping table in Ruby on Rails?-schema

Accepted answer
Score: 13

Use the Vestal versions plugin for this:

Refer to this screen cast 1 for more details.

class Address < ActiveRecord::Base
  belongs_to :user

class Order < ActiveRecord::Base
   belongs_to :user

   def address
     @address ||= (user.address.revert_to(updated_at) and user.address)
Score: 12

Thought I'd add an updated answer. Seems 12 the paper_trail gem has become the most popular one 11 for versioning in Rails. It supports Rails 10 4 as well.


From their readme:

To setup and 9 install:

gem 'paper_trail', '~> 3.0.6'
bundle exec rails generate paper_trail:install
bundle exec rake db:migrate

Basic Usage:

class Widget < ActiveRecord::Base

For a specific instance 8 of the Widget class:

v = widget.versions.last
v.event                     # 'update' (or 'create' or 'destroy')
v.whodunnit                 # '153'  (if the update was via a controller and
                               #         the controller has a current_user method,
                               #         here returning the id of the current user)
v.created_at                # when the update occurred
widget = v.reify            # the widget as it was before the update;
                               # would be nil for a create event

I've only played with it but 7 I'm about to start a pretty ambitious site 6 which will require good versioning of certain 5 classes and I've decided to use paper_trail.


I 4 have implemented the paper_trail gem in production 3 at www.muusical.com and it has worked well 2 using the above. The only change is that 1 I am using gem 'paper_trail', '~> 4.0.0.rc' in my Gemfile.

Score: 6

From a data architecture point of view, I 40 suggest that to solve your stated problem 39 of

...when an order is placed, it will always 38 be able to reference the user address 37 that was used at the time of order placement.

... you 36 simply copy the person's address into an 35 Order model. The items would be in OrderItem 34 model. I would reformulate the issue as 33 "An order happens at a point in time. The 32 OrderHeader includes all of the relevant 31 data at that point in time."

Is it non-normal?

No, because 30 the OrderHeader represents a point in time, not 29 ongoing "truth".

The above is a standard 28 way of handling order header data and removes 27 a lot of complexity from your schema as 26 opposed to tracking all changes in a model.

--Stick 25 with a solution that solves the real problem, not 24 possible problems--does anyone need a history 23 of the user's changes? Or do you just need 22 the order headers to reflect the reality 21 of the order itself?

Added: And note that 20 you need to know which address was eventually 19 used to ship the order/invoice to. You do 18 not want to look at an old order and see 17 the user's current address, you want to see the 16 address that the order used when the order 15 was shipped. See my comment below for more 14 on this.

Remember that, ultimately, the purpose 13 of the system is to model the real world. In 12 the real world, once the order is printed 11 out and sent with the ordered goods, the 10 order's ship-to isn't changing any further. If 9 you're sending soft goods or services then 8 you need to extrapolate from the easier 7 example.

Order systems are an excellent case 6 where it is very important to understand the 5 business needs and realities--don't just 4 talk with the business managers, also talk 3 with the front-line sales people, order 2 clerks, accounts receivable clerks, shipping 1 dept folks, etc.

Score: 3

You're looking for the acts_as_audited plugin. It provides an 14 audits table and model to be used in place 13 of your map.

To set it up run the migration 12 and add the following to your user address 11 model.

class UserAddress < ActiveRecord::Base
  belongs_to :user

Once you've set it up, all you need 10 to do is define an address method on order. Something 9 like this:

class Order < ActiveRecord::Base
   belongs_to :user
   attr_reader :address

   def address
     @address ||= user.user_address.revision_at(updated_at)

And you can access the users' address 8 at the time of order completion with @order.address

revision_at is 7 a method added to an audited model by acts_as_audited. It 6 takes a timestamp and reconstructs the model 5 as it was in that point of time. I believe 4 it pieces the revision together from the 3 audits up on that specific model before 2 the given time. So it doesn't matter if 1 updated_at on the order matches a time exactly.

Score: 0

I think this would be as simple as:



  user_id #to get the users name
  user_address_id #to get the users address

Then 13 when a user changes their address, you do 12 a sort of "logical delete" on 11 the old data by creating a new UserAddress, and 10 setting the "previous_address_id" field 9 to be the pointer to the old data. This 8 removes the need for your map table, and 7 creates a sort of linked list. In this way, whenever 6 an order is placed, you associate it to 5 a particular UserAddress which is guaranteed 4 never to change.

Another benefit to doing 3 this is that it allows you to following 2 the changes of a users address, sort of 1 like a rudimentary logger.

More Related questions