I was recently building an app which needed to take advantage of versioning. I liked the look of acts_as_versioned, which is a nice add-on to Rails to versions models.
However, the tutorial-fu is pretty sparse, and what is out there is pretty old, so I wanted to write up what it took to get it running for me.
Step 1 – The basics
First, create a new Rails app (rails testversioning
). I’m using the Rails default of not using MySQL here, but it should be similar. We’ll also need to install acts_as_versioned – gem install acts_as_versioned
. Change to the root of the rails application you just created.
Next, we need to make a tweak. There is a bug with Rails 2.1 and acts_as_versioned which is fixed in the trunk, but hasn’t made it to the release (which is 0.2.3 as of this post). To fix that, head over to GitHub, then click on the “Download” link which is next to the title near the center of the page. You can pick either the .zip or the .gz file. Extract the archive to the vendor/plugins/acts_as_versioned
folder in your rails application.
We’ll need to do one other thing. So Rails can see the gem, open up config/environment.rb
. Find the commented out lines around which start with config.gem (around line 30). Add the following line: config.gem "acts_as_versioned"
, then save the file.
Step 2 – Basic Model
Let’s go ahead and get a basic model working. From the root of your rails app, type script/generate scaffold Item title:string due_date:date status:string
. This creates a basic model which lets you edit Items.
Now, we’ll need to tell the database about this model, so run rake db:migrate
. This runs the migration script at db/migrations and gets your database ready.
Now, go ahead and run script/server
and navigate to http://localhost:3000/Items. Feel free to add a new item. Make sure that’s all working before we move on.
Step 3 – Adding Versioning
Now that we know our base stuff is working, let’s get versioning working. This will consist of three steps:
- Create a migration for our versioning table
- Mark our model as versioned
- Use the versioning to do something meaningful
To create a migration for our table, we run the handy script/generate migration AddVersioning
– the last part is just a handy name to remember why we are doing this versioning. Open up the file just created in db/migrate/[timestamp]_add_versioning.rb
. Change the file so it looks like:
class AddVersioning < ActiveRecord::Migration
def self.up
Item.create_versioned_table
end
def self.down
Item.drop_versioned_table
end
end
This may require tweaking if you have larger models where you don’t want to version all of the fields, but this will get us going for now. Now save the file.
Of course, that magic method we’re calling on our Item model is useless unless it knows what to do with it. So open up app/models/item.rb
and make it look like:
class Item < ActiveRecord::Base
acts_as_versioned
end
Yep. Really. That’s it. Convention over configuration is a magic thing. Let’s let our database know about these changes – so call rake db:migrate
.
Now, just to make sure nothing has been broken, fire up script/server
, navigate to your Items, and edit one. Everything should be fine.
Now we have to do something meaningful with the versioning information. Open up app/views/items/show.html.erb
. Before the Edit and Back links, put the following:
History
- <% for version in @item.versions.reverse %>
- <%=version.status %> (<%=version.updated_at %>)
<% end %>
Now save, and view the item you’ve edited. Edit it some more, and watch the version expand out. Yes, it really was that easy! There’s lots more to be learned from the RubyDocs, but hopefully that gets you going. Happy versioning!
Links that helped:
Thanks man. I wouldn’t have figured this out on my own
This is great man, I will link to your post when I write a tutorial about my app.
Any thoughts on editing/destroying a version?
Hi:
I start my server and when I go to localhost:3000/items/new and fill the form an click on Create, I get an NoMethodError as follows:
NoMethodError in ItemsController#create
You have a nil object when you didn’t expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.include?
Not sure what is happening? Any thoughts?
really Good, self explainatory