0

Implementing versioning for a Rails app I'd like to have a view that displays all versions of a model with some extra functionality like reverting etc. I use the paper_trail gem for the versioning.

I know that I could do that by writing a controller function like versions and a view for every model but I'd like to do it for all models at once. This should be possible because the model.versions attribute is always structured identically.

Ideally the URL should look like /pages/testpage/versions while testpage is the page id.

This seems similar to the concept of nested routes in rails.

resources :pages do                                                    
    resources :versions                                                  
end

The problems with nested routes however are:

  • Needs extra configuration per model
  • I cannot access the testpage object without knowing of which model it is an instance. I also wasn't able to find a way to determine the model since the only thing that is provided to my versions controller is the params hash.

I'm completely open to alternative solutions that might not follow my initial ideas.

Martin Klepsch
  • 1,875
  • 3
  • 18
  • 24

2 Answers2

1

Write it in your ApplicationController and define it as a helper_method.

For example

class ApplicationController < ActionController::Base
  helper_method :current_time

  def current_time
    Time.now
  end
end

Now you can cal current_time everywhere in controllers or views.

Also you can write separate Module/Class and define there your helpers methods. Than you should include this file into your ApplicationController as well

UPD after theme is changed

I didn't think about your actual question. But I can say that your approach is nod the best here.

You should create new resource instead of creating new functionality which will hard to be tested. So create new resource (controller): versions and play around this controller.

For example how it can work:

/versions/pages/132
/versions/comments/1003

How to realize it:

match "/versions/:model/:id", :to => "versions#index"

In your controller:

class VersionsController < ActionController::Base
  def index
    @object = my_type.find(params[:id])
    @versions = @object.versions
  end

  private
  def my_type
    params[:model].constantize
  end
end

Of course you can change routes the way you want:

match "/:model/:id/versions", :to => "versions#show"

So now your pretty /pages/testpage/versions will work fine for you without any new strange logic.

UPD 2

Imagine you have got this route:

match "/:model/:id/versions", :to => "versions#index", :as => :versions

And this objects:

@page = Page.last
@hotel = Hotel.find(123)
@comment = @page.comments.first

How will we create links for versions:

<%= link_to "Versions of this page", versions_path(:model => @page.class.to_s, :id => @page.id) %>
<%= link_to "Versions of this hotel", versions_path(:model => @hotel.class.to_s, :id => @hotel.id) %>
<%= link_to "Versions of this comment", versions_path(:model => @comment.class.to_s, :id => @comment.id) %>
fl00r
  • 82,987
  • 33
  • 217
  • 237
  • Maybe I just don't understand but this does not seem to solve my problem. I changed the question a little so hopefully it is more clear now. – Martin Klepsch Apr 20 '11 at 13:53
  • Yes. Now it is another question :) – fl00r Apr 20 '11 at 13:54
  • I'm sorry. I forgot to review the question after being done with the description. – Martin Klepsch Apr 20 '11 at 13:55
  • Thanks. This is actually what I'd do when it's not possible to have a route like /pages/213/versions. I already have the versions controller which should contain the functions and views to display the list of versions of a model. – Martin Klepsch Apr 20 '11 at 14:04
  • Ok, but trust me - this is not good idea to do what you want to do. – fl00r Apr 20 '11 at 14:06
  • If you say it is a bad idea, why? – Martin Klepsch Apr 20 '11 at 14:09
  • and also I've shown you how to generate links like you want `match "/:model/:id/versions", :to => "versions#show"` – fl00r Apr 20 '11 at 14:10
  • The main reason is actually that I can't clear understand your approach. It looks like you want to create shared controller actions for all resorces – fl00r Apr 20 '11 at 14:18
  • Yes thats basically what I want to do. I noticed that this doesn't work for resources that have a different :path than the default one. However this would also cause trouble when using your first suggestion. I'm confused and curious how other people implemented versioning of multiple models. – Martin Klepsch Apr 20 '11 at 14:36
  • `However this would also cause trouble when using your first suggestion` I am pretty sure it wont :) – fl00r Apr 20 '11 at 14:37
  • `params[:model].constantize` only works when the route (= URL segment) matches the model name. With a line like `resources :news_articles, :path => :news` this cannot work, or is there other magic happening? – Martin Klepsch Apr 20 '11 at 15:16
  • other. Because it doesn't need to use this model resource path. It sends model name to Versions controller. You are sending not a path, but model name. I'll add some examples now. – fl00r Apr 20 '11 at 15:19
0

I would suggest passing a param such as 'type' and stuff the model name there. Then in your controller you can do:

class VersionsController < ApplicationController
  def index
    model = params[:type].classify.constantize
    @obj = model.find(params[:id])
  end
end

For your links, you can pass queries to the link_to helper

<%= link_to versions_path(@model, :type => @model.class) %>

Or something along those lines.

dogenpunk
  • 4,332
  • 1
  • 21
  • 29