Skip to content

joshbuddy/has_many_versions

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

39 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

has_many_versions

This attempts to provide versioning for ‘has many’ relationships within ActiveRecord.

Installation

script/plugin install git://github.com/joshbuddy/has_many_versions.git

Usage

Here is a pretty typical relationship.

class Book < ActiveRecord::Base
  belongs_to :author
  validates_associated :author
end

class Author < ActiveRecord::Base
  has_many :books
end

Lets say we want to include versioning now to modifications made to the collection of books. We can modify the has_many relationship the following way:

has_many :books, :extend => HasManyVersions

Now changes made to the relationship with the normal << and delete methods will be tracked. There are some modifications needed for both tables for this to work. The owner of the relationship (in this case Author) needs an integer column named version (with a default value of one). The collection (in this case Book) needs two columns, initial_version and version.

Assuming we’ve done all that, lets try out some magical versioning to see how it all works.

We’ll start by defining some books.

the_eyre_affair = Book.new(:name => 'The Eyre Affair')
=> #<Book id: nil, initial_version: nil, version: nil, author_id: nil, name: "The Eyre Affair">
lost_in_a_good_book = Book.new(:name => 'Lost in a Good Book')
=> #<Book id: nil, initial_version: nil, version: nil, author_id: nil, name: "Lost in a Good Book">
the_well_of_lost_plots = Book.new(:name => 'The Well of Lost Plots')
=> #<Book id: nil, initial_version: nil, version: nil, author_id: nil, name: "The Well of Lost Plots">
something_rotten = Book.new(:name => 'Something Rotten')
=> #<Book id: nil, initial_version: nil, version: nil, author_id: nil, name: "Something Rotten">

And an author to take them.

jasper = Author.new(:name => 'Jasper Fforde')
=> #<Author id: nil, version: 1, name: "Jasper Fforde">

That sounds good. Just so we’re all on the same page, how does Jasper look at the moment?

jasper.version
=> 1
jasper.books
=> []

Version 1 is where all versioned objects initially start.

Now, lets start adding some books.

jasper.books.push(the_eyre_affair, something_rotten)

And our new version?

jasper.version
=> 2

And our books?

jasper.books
=> [#<Book id: 1, initial_version: 2, version: 2, author_id: 1, name: "The Eyre Affair">,
    #<Book id: 2, initial_version: 2, version: 2, author_id: 1, name: "Something Rotten">]

Yeah! Everything is good and green.

Lets add on some more books.

jasper.books.push(lost_in_a_good_book)
jasper.version
=> 3
jasper.books.push(the_well_of_lost_plots)
jasper.version
=> 4

Because we did them one at a time, the version number incremented for each one.

Now, Jasper has a lot of books.

jasper.books
=> [#<Book id: 1, initial_version: 2, version: 4, author_id: 1, name: "The Eyre Affair">,
    #<Book id: 2, initial_version: 2, version: 4, author_id: 1, name: "Something Rotten">,
    #<Book id: 3, initial_version: 3, version: 4, author_id: 1, name: "Lost in a Good Book">,
    #<Book id: 4, initial_version: 4, version: 4, author_id: 1, name: "The Well of Lost Plots">]

Lets take one away…

jasper.books.delete(lost_in_a_good_book)

And the version has incremented

jasper.version
=> 5
jasper.books
=> [#<Book id: 1, initial_version: 2, version: 5, author_id: 1, name: "The Eyre Affair">,
    #<Book id: 2, initial_version: 2, version: 5, author_id: 1, name: "Something Rotten">,
    #<Book id: 4, initial_version: 4, version: 5, author_id: 1, name: "The Well of Lost Plots">]

But thats not right, he did write that book. Lets go back in time.

jasper.books.rollback

And the version get incremented…

jasper.version
=> 6

And the books…

jasper.books
=> [#<Book id: 5, initial_version: 6, version: 6, author_id: 1, name: "The Eyre Affair">,
    #<Book id: 6, initial_version: 6, version: 6, author_id: 1, name: "Something Rotten">,
    #<Book id: 7, initial_version: 6, version: 6, author_id: 1, name: "Lost in a Good Book">,
    #<Book id: 8, initial_version: 6, version: 6, author_id: 1, name: "The Well of Lost Plots">]

Are right where we left them!

In fact, lets roll it right back to version 2.

jasper.books.rollback(2)
jasper.version
=> 7
jasper.books
=> [#<Book id: 9, initial_version: 7, version: 7, author_id: 1, name: "The Eyre Affair">,
    #<Book id: 10, initial_version: 7, version: 7, author_id: 1, name: "Something Rotten">]

And what would the world look like if Jasper Fforde hadn’t written anything?

jasper.books.rollback(1) #back to version one
jasper.version
=> 8
jasper.books
=> []

Of course, it’s always fun to reminisce

jasper.books.at(3)
=> [#<Book id: 27, author_id: 3, initial_version: 2, version: 5, name: "The Eyre Affair">,
    #<Book id: 28, author_id: 3, initial_version: 2, version: 5, name: "Something Rotten">,
    #<Book id: 29, author_id: 3, initial_version: 3, version: 4, name: "Lost in a Good Book">]
jasper.books
=> []

About

Versioning for has_many relationships

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages