Unhappy with the rail way of rendering partials - ruby

my question considers the partial handling in Rails. I just can't get the reason for some design decisions.
If I render a partial like this:
<%= render partial: 'foo/bar', object: #herbie %>
Where #herbie is an object of class Car. Why is the local variable called bar by default instead of car which - at least for me - would be more reasonable in most cases.
If various partials share a common layout I have to add the layout: ... each time I use render partial:. This seems odd to me! Isn't it more likely to have various partials wich share the same layout then the other way around? Wouldn't it be more reasonable to define the layout within the partial, or to add a to_layout_path method to the ActiveRecord Model?
Update:
I get the point that it might be better to use the partial name, as this is well known in the partial context. However the use of the tags :locals and :as screws up this argumentation.
In an actual case I want to use different partials depending on the state of an object (questions that could be open or closed). I moved the decision which partial to render to the Question.to_partial_path method which seems to be quite elegant. However I named the partials open_question and closed_question and this makes the partial code unreadable.
It gets even worse if I consider different types of questions.
I expect that within a partial we always make some presumptions about the model that is passed in. Thus in closed_question I am quit sure that I get a question - thus I would expect the variable to be called question. On the other hand I can imagine situations where we pass in something different that just behaves appropriated (Duck-typing).
My temporal meaning about partials is that within the partial it should be possible to define the name of the given parameter as well as a layout file that is used.
The parameter indeed can be realized by introducing a new variable ( question = open_question) which feels a little bit unclean to me.

When you pass in an argument to a method there's no way of knowing what the "name" of that variable is. The only thing the partial rendering method here has at its disposal is the foo/bar name, the object attribute, and some model.
That your model originated in a variable called #car is completely lost in the process of making the call.
It's convention in Rails that the variable used within the partial is the same as the name of the partial itself. You can override this if you want to have non-standard behaviour:
render(partial: 'foo/bar', locals: { car: #car })
As a note, though, going down the non-standard path is usually a bad idea. Whenever possible, just rename your partial to better match your expectations.

This is related: render partial :object vs :locals
The documentation is pretty good: http://api.rubyonrails.org/classes/ActionView/PartialRenderer.html
The default is to name the object after the partial. If you want to be explicit, you can use :as or locals.
render partial: 'foo/bar', locals: {car: #car}
render partial: 'foo/bar', object: #car, as: 'car'

Related

Is there a usable rendering context in sinatra/padrino?

I am attempting to use the Exhibit pattern in a padrino application but to do so properly I need to access the rendering context. That is, I need to have some object context such that calling something along the lines of context.render('accounts/index') will produce identical output to when render 'accounts/index' is called from inside a controller (assuming that all variables are set the same and that app/views/accounts/index.erb is a template file).
I can't find anything that does this within the padrino public API, so I attempted to dig deeper. The existing render method in the controller wraps sinatra's render method, which is a private instance method. So it's not obviously not defined on the class MyApp (i.e. the Padrino::Application subclass). MyApp.new results in an instance of Rack::Session::Cookie rather than of itself, and none of the padrino methods that I can find return the existing instance of the app, which would be a suitable candidate for the context object if I called its private render method.
Is this something that's just inherently too difficult to bother with given sinatra's and padrino's designs, or is there something that I'm missing here?
I realise that the answer was in the Objects on Rails book if only I'd kept reading. I needed to pass the context explicitly from within the template: exhibit(object, self) inside the template causes the template's rendering context to be passed as the second argument. Of course.
I guess this really wasn't specific to padrino after all and I was looking for something complicated when the answer was very simple.

How do you handle single table inheritance in SimpleForm so a single helper handles all models?

We have a model "EventSession" which has several subtypes via STI, including "NetworkingEventSession" and "DiningEventSession"... we want to be able to handle all of them from one controller and one view in some cases, but simple form is looking at the objects when iterating through in simple_form_for #session and trying to use the networking_event_session_path helper, which we don't currently have defined, instead of the regular event_session_helper path, which would work fine and is what we want.
I could define new routes to get helpers for each subtype, all directing to the same path, but that will be very unDRY, and we may not always want them going to the main event session path... is there some way to override simple_form_for in this particular view to tell it explicitly what model/class to use?
Specify :url=> networking_event_session_path in the simple_form_for.
Something like this:
<%= simple_form_form #session, :url => networking_event_session_path %>

Rails partial for different controllers

So I have different controllers that share a lot of functionality and I'm wondering if there is a way to share that similar functionality between them.
For example this:
= link_to 'Edit', dvd_path(dvd), :class => 'btn'
Where the only thing that changes between controllers is the object, instead of dvd it will be books or games. So I'm thinking there has to be a way to create a partial that uses that controller's correct path without having to rewrite each one. And without having to send the object.
Ideas are appreciated.
Edit
#defaye: what I'm trying to do is reuse the code that I have above and apply it to 7 or so different controllers.
The above code is just an example of a bunch of links and paths that are always the same and the only thing that changes is that dvd_path(dvd) becomes book_path(book), etc.
I'd like the simplest, more straightforward way of doing it.
In fact, I am now wondering if it's possible to have the same view that handles multiple controllers since they are so similar. This would be fantastic!
Thanks.
If your code is just a couple of methods, you don't need a partial at all. Helpers can help you.
Here's an example. You can define it in ApplicationHelper.
# application_helper.rb
def link_to_edit(object)
link_to('Edit', polymorphic_path(object, :action => 'edit'), :class => 'btn')
end
And in your view
= link_to_edit(object)

Ruby/Rails: How to specify an arbitrary route path via a string in a View, or a route URL in a Model

This bit has been frustrating me all night and I can't find a helped or clean way to do it.
I had a non-ActiveRecord Model (so not stored in the DB) that reflects a dynamic navigation menu, and one attribute of the model's menu items is naturally a route link. (the Model in question essentially has a class with links/routes stored in it, to other Model routes) I don't like the idea of storing URLs, or any navigation data for that matter, in a model, but I can't see a better way to design this.
The link_to attribute could be a route path (e.g. user_path) as a string which would be used in the View to determine the URL. Or, it could be stored in the Model as the URL by retrieving it from the routes via the url_helper. (I think)
So, does anyone have any suggestions on a helper or trick that could solve the problem? url_helper and url_for kind of expect a model with a matching route. In this case the non-ActiveRecord Model is not a normal Model, but a representation of the site's Navigation Menu, linking to other parts of the site.
I really just want a helper or function to say, route_to 'user_path'. Does anyone know if this is possible?
Any help would be appreciated. Thanks!
The answer, beside the fact I'm probably abusing MVC models, is Rails.application.routes.url_helpers. you can call it with the route path as a direct method and it'll return the url, but the trick that I found is the send method allows calling it with a string representation of the route path. This makes coding for it much easier. Note saying this works as is. It's just an example:
class Something
attr_accessor :somethings
def all()
#somethings = Array.new
somethings << Something.new('user_root_path')
somethings << Something.new('edit_user_path')
somethings << Something.new('edit_pets_path')
end
...
def initialize(link)
#link = Rails.application.routes.url_helpers.send link
end
end

Sinatra Sub-Directory Views

I want to be able to get Sinatra views from sub-directories of ./views (such as ./views/admin). I know you can set the views like so:
set :views, Proc.new { File.join(root, "templates") }
But how would I be able to set this for only part of the file?
I'm not sure exactly what you're asking, but you can render a view in views/admin by doing this:
erb :"admin/report"
If you're asking how to automatically look in subdirectories of views when you call erb :report, I'm not sure how to do that, and I don't think you'd want to (what happens if two views in different dirs have the same name?).
This is supposed to be an oversized comment. The answer provided by Alex is the correct one, but to be sure, this is a quote from official documentation:
One important thing to remember is that you always have to reference templates with symbols, even if they’re in a subdirectory (in this case, use: :'subdir/template' or 'subdir/template'.to_sym). You must use a symbol because otherwise rendering methods will render any strings passed to them directly.

Resources