开发者

How to define a controller function that works for instances of any model

Implementing versioning for a Rails app I'd like to have a view that开发者_C百科 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.


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) %>


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜