For the sake of ease, the steps below outline how to create a basic Rails application, with a single model (the old
fruit example again) that supports version history. This basic model will also support:

UUID id column - this is a fairly common requirement for web apps

Correct primary key constraints - ActiveRecord doesn't do a great job of getting these right for more custom models

Optimistic locking - because we want our
users to be sure their updates are applied to the versions they were editing

What follows has been tested against the following software and corresponding versions:

There are various aspects of what we are doing that are not supported by ActiveRecord (Arel). So we need to make some
changes to the migration file that has been created (which is db/migrate/20140226220508_create_fruits.rb in my case).
It should look like this:

We are almost done. Having overridden the default primary key, we need to tell ActiveRecord what the new primary key is
by editing the model class:

# app/models/fruit.rbclassFruit<ActiveRecord::Base# we have overridden the default primary_key# even though lock_version is strictly part of the# primary key, its use happens behind the scenesself.primary_key=:id# by default, when searching and finding bits of fruit# we want the latest versiondefault_scope{where(valid_to: 'infinity').reorder('')}end

And that's our model created. Relatively painless. Let's see it in action.

Creating some fruit

The simplest way to test out our model is via the Rails console which is started via:

See how the use of optimistic locking ensures that the update is applied to the latest version (thereby obviating
the need for the restriction valid_to = 'infinity'). Again, let's check in on the database to see what's happened:

Again, the optimistic locking support in ActiveRecord is helping ensure we delete only the latest version. If we glance
in the database, we should expect to see just the valid_to be updated from infinity to the time of the delete:

Again, note the use of unscoped. But also the use of find which works by virtue of the preceding where clauses.

A note on optimistic locking

Earlier we set out optimistic locking as one of the additional goals of this exercise. But there are some important
benefits that ActiveRecord's optimistic locking brings to our version control implementation.

Firstly, it makes the definition of our primary key very clean. The pair [id, lock_version] is much more
understandable than a primary key that involves valid_from and/or valid_to.

The second benefit, as we have seen, is that it ensures ActiveRecord only updates/deletes the latest version (the
function behind the trigger also ensures this,
but using lock_version is much cleaner), thereby obviating the need for us to restrict by valid_to = 'infinity' on
all updates (the default scope helps on reads, but not writes).

Source code

The complete source code (minus passwords) is available on
GitHub as usual.

Conclusion

This is a simple(ish) method of achieving version history on Rails (ActiveRecord) models with PostgreSQL. There are
alternative methods out there, but this time-based approach allows for huge power when querying (not least because it
works with joins too).