Reputation: 12759
How can you revert records in a way that respects REST conventions and routing?
I am looking for examples of how to setup my routes.rb and build the link & controller action to do the revert. All the examples I have found are pre-REST rails.
My understanding is that I need to have revert_to_version function in my resource controller.
Upvotes: 1
Views: 1684
Reputation: 54603
I'll assume you're rolling your own system. My answer does not relate to any particular versioning plugin.
Because your URLs are paths to resources, I would consider the revision parameter as optional. You can leave it out, and get the latest revision, or specify the revision eplicitly.
GET /pages/1 GET /pages/1?revision=4
Same goes for editing.
GET /pages/1/edit GET /pages/1/edit?revision=4
PUT to /pages/1 would create a new revision, incrementing the version number by one. The new version number is created without regard to the current revision number. It's merely a starting point when editing.
Obviously, it should be impossible to specify a revision for a POST to /pages (create).
If you want to track reverts in particular, and the editing starting point mentioned above isn't sufficient, there are a few sensible alternatives. I'm not a REST geek, so I'm not sure which you should choose. Perhaps it's a matter of taste.
PUT /pages/1/revisions/4 PUT /pages/1/revert_to?revision=4 PUT /pages/1?revision=4
A code example for the first alternative:
# config/routes.rb
map.resources :pages do |page|
page.resources :revisions
end
# app/controllers/revisions_controller.rb
class RevisionsController < ApplicationController
def update
@page = Page.find(params[:page_id]) # page_id is via /pages/[page_id]/revisions/4
@revision = @page.revisions.find_by_version_number(params[:id])
@revision.revert
end
end
# app/models/revision.rb
class Revision < ActiveRecord::Base
belongs_to :page
def revert
page.revert_to(self) # or something like that..
end
end
If you roll with this, it probably makes sense to GET /pages/1/revisions/4 to display the page for revision 4, instead of GET /pages/1?revision=4.
Upvotes: 0
Reputation: 15596
I've never used acts-as-versioned in particular, but when I come across similar scenarios, the way I usually solve it is by reification of the attribute. In other words, I'd create a new resource only for the actual version number of the resource.
Eg.
/resources/:id/actual_version
would point to the actual version number of the resource with id :id. Then to change the actual version, we can just PUT desired number to it.
PUT /resources/:id/actual_version
:version = 123
would revert our resource to the version 123.
As a convention, I'd use something like "last-but-one" as a value of :version to refer to the version that preceded the actual one.
Then, to rollback the actual version, we can just do:
PUT /resources/:id/actual_version
:version=last-but-one
--
Expanding my own answer:
In routes.rb we can do something like:
map.connect '/resources/:id/actual_version', :controller => 'resources', :action => 'set_version', :conditions => { :method => :put }
And in resources_controller.rb:
def set_version
@resource = Resource.find_by_id(params[:id])
if params[:version] && @resource
version = params[:version] == "last-but-one" ? @resource.versions.last : params[:version]
if @resource.revert_to(version)
# Success, everything went fine!
else
# Error, the resource couldn't be reverted - unexisting version?
end
else
# Error, version number or resource id is missing.
end
end
Hope that clarified my previous thoughts a bit. ;)
Upvotes: 8