Rails: Rendering a Namespaced Polymorphic Partial - ruby-on-rails

I have a polymorphic model called Update. Typically, I can render its associated updatable model with a simple:
<%= render #update.updatable %>
Unfortunately, if I'm calling this method from within a namespaced controller, then Rails will try to namespace the view path as well, searching admin/reviews/review rather than reviews/review. This results in errors like:
Missing partial admin/reviews/review
Typically I can hardcode a workaround, such as:
<%= render :partial => "/reviews/review", :locals => {:review => #update.updatable}
This would be fine if the association weren't polymorphic, but since it is, I'll get errors if the updatable is anything other than a Review. Unfortunately, I have dozens of possible updatables, and branching through them with a case statement would be troublesome to maintain.
Is there a simpler approach I'm not thinking of?

You can define the partial path from your models. It is something like this:
class Review
def to_partial_path
"/reviews/review"
end
end

Related

Rendering polymorphic object in Rails

For people that only want the question, here it is :
Is there a way to specify the folder to look in when you call render on an object? I don't want to specify the view, only the folder to look in.
And for people that want context :
I am working on an activity stream system (something that looks like google+/facebook).
I have "Activities", which are exactly like google+ feeds (or facebook, or whatever!). So, I have a simple loop that display each activities, which are bound to one of the following object (polymorphic) : User, Group, Comment, Note.
In my view that render an activity (views/activities/_activity.html.erb), I have
<%= render activity.object %>
where activity.object is a reference to the bound object (User, Group, Note, Comment). If it's a user, it goes to views/users/_user.html.erb and renders it. For a group, views/groups/_group.html.erb.
That works just fine. However, I come to the point where the rendering of a group in my activities should not be the same rendering as in the group list page. Is there a way to specify the folder to look in when you call render on an object? So that my :
<%= render activity.object %>
would become :
<%= render activity.object, :folder => 'views/activities/' %>
Note that I don't want to specify which view directly, as I don't want to do a case for each of the possible types of objects (User, Group, Note, Comment) in the activity. I want to to have the same behaviour as of right now, which means if it finds a views/activities/_user.html.erb, it would load any user in the activities with that view instead of the one in the views/users/_user.html.erb.
Thanks
I'm not aware of any folder type option, but when I do this I usually do:
<%= render "activities/#{activity.object.class.name.underscore}" %>
That would give you similar behaviour.
EDIT A good point below by Dominic, if your classes are nested in namespaces, you will have to include the appropriate structure.
i.e.
module Foo
class Bar < ActiveRecord::Base
end
end
# class.name is Foo::Bar, underscored is 'foo/bar'
<%= render "activities/#{activity.object_type.underscore}" %>
# will be in
activities/foo/_bar.html
In current Rails (3.2.9), you can define a to_partial_path method on your model to tell it where to look.
For example, you can do:
class User
def to_partial_path; "user"; end
end
Rails will then look for _user.html.erb relative to the view from which you're calling render.
My own approach, which I extracted in polymorphic_render gem - to add suffixes for special partials, but store that partial in resource folders.
In your case views/users/_user.html.erb will have very common user representation (which probably used in users list rendering), but views/users/_user_activity.html.erb will render special partial for user activity.
And inserting that partials is very simple, just <%= polymorphic_render activity.object, :activity %>

Specify namespace when using "render" with an object in rails 3

So, you can do something like that in rails:
#features.each do |feature|
render feature
end
and it will look for a partial called _feature.html.erb in the views/features folder, based on the class name.
But what if features are in a cms namespace?
Is it possible to specify the namespace? Doing something like this (it doesnt work, obviously)
render [:cms, feature]
Thx
You'll have to be more explicit:
render :partial => '/cms/feature', :object => feature
This will render the 'app/views/cms/_feature.html.erb' partial with the object being 'feature'.

Rendering a Heterogeneous Collection: How can I specify a single directory for the partials?

I have a collection, #comments, that is heterogeneous but hierarchical. Each comment is either an instance of Comment or some derived class, like ActionComment or InactionComment. I am rendering a different partial for each type of Comment. The View code is:
= render #comments
As all the partials are related, I would like to keep them in a single view directory, i.e.:
app/views/comments/_comment.haml
app/views/comments/_action_comment.haml
app/views/comments/_inaction_comment.haml
But right now in order to use the automatic rendering of the correct partial, I am using separate directories, like:
app/views/comments/_comment.haml
app/views/action_comments/_action_comment.haml
app/views/inaction_comments/_inaction_comment.haml
Rails 3.2 makes a Model#to_partial_path method available which allows you (as its name suggests) to override the partial pathname.
def to_partial_path
self.action.to_s
end
The path it returns does not include the leading underscore and is assumed to be relative to .../views/modelname/. See http://blog.plataformatec.com.br/2012/01/my-five-favorite-hidden-features-in-rails-3-2/ for an overview
You can't do it quite as magically, but you can do it by just rendering each item individually and specifying the partial.
example in haml:
- #comments.each do |c|
= render :partial => "comments/#{c.class.to_s.underscore}", :locals => {:comment => c}

One controller rendering using another controller's views

I have QuestionController
I now have AnotherQuestionController with actions which should render using templates and partials in app/views/question/
Is this possible? Seems like it should be.
I've tried
render :template => "question/answer"
but answer.html.erb includes partials and I get errors like
"Missing template another_question/_my_partial.erb in view path"
So is there a way to tell Rails "treat AnotherQuestionController as if its QuestionController and look for views and partials in app/views/question"?
Or will I have to create app/views/another_question - which will cause duplication (this can't be the Rails way).
Thanks
Template rendering should actually work
render :template => "question/answer"
The problem you were having is from the partials looking in the wrong place. The fix is simple, just make your partials absolute in any shared templates. For example, question/answer.html.erb should have
<%= render :partial => 'question/some_partial' %>
rather than the usual
<%= render :partial => 'some_partial' %>
You can achieve it with:
render 'question/answer'
Rails uses a list of prefixes to resolve templates and partials. While you can explicitly specify a prefix ("question/answer"), as suggested in another answer, this approach will fail if the template itself includes unqualified references to other partials.
Assuming that you have an ApplicationController superclass, and QuestionController inherits from it, then the places Rails would look for templates are, in order, "app/views/question/" and "app/views/application/". (Actually it will also look in a series of view paths, too, but I'm glossing over that for simplicity's sake.)
Given the following:
class QuestionController < ApplicationController
end
class AnotherQuestionController < ApplicationController
end
QuestionController._prefixes
# => ["question", "application"]
AnotherQuestionController._prefixes
# => ["another_question", "application"]
Solution #1. Place the partial under "app/views/application/" instead of "app/views/question/", where it will be available to both controllers.
Solution #2. Inherit from QuestionController, if appropriate.
class AnotherQuestionController < QuestionController
end
=> nil
AnotherQuestionController._prefixes
# => ["another_question", "question", "application"]
Solution #3. Define the class method AnotherQuestionController::local_prefixes
This was added in Rails 4.2.
class AnotherQuestionController < ApplicationController
def self.local_prefixes
super + ['question']
end
end
AnotherQuestionController._prefixes
# => ["another_question", "question", "application"]
You could try the inherit_views plugin (http://github.com/ianwhite/inherit_views/tree/master) I mentioned here in the answer to this question.

Rails - Abstract/Shared Views

I have a few objects that are incredibly similar. In fact, they use STI and store all of the data in the same table in the DB. Currently, every time I add a feature or fix a bug, I have to update it in 3 different places, so I would like to DRY my code a little bit. The most common code duplication is in the views. I have seen people using render :template => 'shared/something' to render their common views, the problem is, I have many of these, but only for one particular object (Shipments), so I would prefer something like render :template => 'shipments/shared/something' or render :template => 'abstract_shipments/something'. More importantly though, I would like any of the actual classes to be able to override the template if it needs to.
Do y'all have any suggestions on how to go about this? Thanks very much for any answers!
The inherit_views plugin might do the job: http://github.com/ianwhite/inherit_views/tree/master. I've used it successfully before.
You could then have a common base controller class for all controllers needing to render shipments and have any common templates in that base controller's views folder with any specific overrides in the individual controllers' views folders.
Classes (models at least) don't - or shouldn't - know anything about how they're displayed. That's fundamental to the separation of concerns provided by the MVC pattern.
I think I'd try something like this:
Use a single controller for all the STI models, ShipmentsController or something similar. Apart from it seeming a logical thing to do, all your views will end up in the same place, which ought to help.
As for views, how about a "main" one for the common parts, and one partial for each set of subclass-specific fields? Use a case in your view, or probably more cleanly, a helper method, something like
def partial_name_for_subclass(obj)
if obj.is_a?(CrudeOilShipment) # or kind_of? or test the type column or use respond_to?
'crude_oil_shipment'
# etc
end
end
DRYer, consider using a convention (Rails loves conventions):
<%= render :partial => #shipment.class.name.downcase, :locals => { :item => #shipment } %>
where you have a partial for each subclass.
Hope some of that helps/makes sense...

Resources