Rails partials with single-table inheritance - ruby-on-rails

I want to use partials in rails along with single-table inheritance. I currently have this working:
render partial: #vehicle
# which renders the relevant view, depending on object type, eg:
# views/trucks/_truck.haml
# views/car/_car.haml
I want to leave these default views in place, and create an additional compact view for each object, perhaps like this
# example code only, I want to write something like:
render partial: 'compact', locals: {vehicle: #vehicle}
# and then have this render (for example) with
# views/trucks/_compact.haml
# views/car/_compact.haml
I can happily rename things or change the file names or locations, but what is the simplest way to support two kinds of views (compact and default)?
There will be many more classes later, so looking for very clean, elegant code.
(rails 3.0.5+ on ruby 1.9.2)

To get exactly what you asked for you should do this:
render partial: "#{#vehicle.type.tableize}/#{#vehicle.type.underscore}", object: #vehicle
and you will get rendered:
views/trucks/_truck.html.haml
and the object will be accessible as:
#truck

There might be a better way, but there is always this approach:
render partial: "#{#vehicle.class.to_s.tableize}/compact", locals:{vehicle: #vehicle}
(or it might need to be _compact, instead of just compact, but you get the idea)

I've done something similar, but rather than having the partials in two separate files, I've combined them, and used the locals argument to pass a flag:
# The hash syntax for render is redundant, you can simply pass your instance
# Render the long-form of your partial
render #vehicle
# When using render _instance_, the second argument becomes the locals declaration
# Render the compact form
render #vehicle, compact: true
And then, in my partial...
<% if defined? compact %>
<!-- HTML for compact view -->
<% else %>
<!-- HTML for extended view -->
<% end %>
The advantages of this approach are that you're only maintaining one partial file for each vehicle type, and your code remains pristine.
The disadvantage is that it's a slight departure from the "traditional" usage of partials.

Related

rails rendering partial with collection / other class is in charge?

Somewhat new to rails, longtime programmer. I've got a question about views, controllers and partials really - wondering if I have this setup well.
I've got a pages controller, and on the index page (really the pages index method) I've got a partial in layouts called featured (ie app/views/layouts/_featured.html.erb) -- I've also got a Featured class. I would like basically the index of the featured class to be drawn here. But of course it's not working. SO the question is:
In the page itself I've got the <%= render 'features/index' %> which I'm beginning to think is the wrong way to go..
Do I axe this partial method and just call <%= render 'features/index' %> and let everything progress natively or
What would be the proper way of routing the featured collection to the partial? Since the controller is actually Pages it seems like I'm fighting against the tide.
<%= render 'features/index' %>
Doing this is wrong given your description. This will try to render a partial from app/views/features/_index.html.erb which you haven't mentioned.
To render the partial at app/views/layouts/_featured.html.erb you would do (perhaps a bit more verbose that is necessary)
<%= render partial: "layouts/featured" %>
The best suggestion I can offer is to pass a collection to this partial
<%= render partial: "layouts/featured", locals: { features: #features } %>
Since it seems your intention is for this partial to appear as a piece of a layout I will assume you wish for this partial to appear on multiple pages. This means on multiple actions you will need to have assigned the set of Feature instances this #features instance variable. One way to do this is a before_action.
before_action :setup_features
# ...
private
def setup_features
#features = Feature.all
end
A good place to start learning more about filters is in the Rails Guide
The partial at "app/view/layouts/_featured.html.erb" can only be rendered with
render 'featured'
and not 'featured/index'
render 'featured/index' will render "app/views/layouts/featured/_index.html.erb
Since the pages controller is really rendering the main index page in it's def index, all I had to do was #features = Feature.all and the variable is available for the partial pulled into the index page.
I need to get used to how simple rails is coming from other languages / frameworks.

Ruby on Rails Scoping code block to remove the need for arguments?

I come from PHP and CakePHP background and I'm pretty new to ruby and rails.
I've been creating a helper that could help me with creating some HTML elements that could make me easy to reuse across the web app that I am creating.
Here's how the snippet of my helper looks like
module VehicleHelper
def mileage(vehicle)
render partial: "vehicles/shared/mileage", { locals: vehicle }
end
def manufacturer(vehicle)
render partial: "vehicles/shared/manufacturer", { locals: vehicle }
end
#and etc...
end
And I would use it this way in haml.
%h1= #vehicle.name
= mileage #vehicle
= manufacturer #vehicle
-# and etc…
I want to be able to scope it, so that I don't need to give #vehicle as an argument for every function. Like
%h1= #vehicle.name
- vehicle_block_for #vehicle do
= mileage
= manufacturer
-# and etc…
How do I achieve it? Is that a right pattern that I should use?
Edit : I have thought of using partials straight in the view. However, the problem is that I not only have Vehicle class, I also have Car and Bike classes which are sub-classes of Vehicle, following the STI pattern.
Which means I will have to pass locals all the time, in which case = render partial: 'mileage', { locals: vehicle} or what not becomes configuration codes. And let's say I've renamed the file or moved it somewhere, then I'd have to go and modify all these codes.
About whether this is a "right pattern" for you to use, that's entirely up to you. If you like the way it makes the code read, then great. The simplest implementation would be something like:
module VehicleHelper
def vehicle_block(vehicle)
old,#__vehicle__ = #__vehicle__,vehicle
yield
#__vehicle__ = old
end
def mileage
render partial: "vehicles/shared/mileage", { locals: #__vehicle__ }
end
end
You could just make the render calls directly in your view and it will automatically have access to your instance variable "#vehicle"... I believe those views would have access to "#vehicle" as we'll using the helper methods you currently have, no need to pass it as an argument.
I would also check out the presenter/decorator pattern for this. There is a Railscast for it.
Is that a right pattern that I should use?
I would say no. Yes, you're shortening the code but at the cost of making things harder to follow/understand. A new user to your project would have to search around for the definition of the "mileage" method. Once they find it in the helper they would have to go look at the partial. All that for what, showing less code in the view? I don't think it's worth it.
I would revert it back to something like the following which IMHO is much easier to follow and more idiomatic rails.
%h1= #vehicle.name
render partial: 'mileage', { locals: vehicle }
render partial: 'manufacturer', { locals: vehicle }
Note: I would also explicitly pass in the locals instead of using #vehicle. It makes things, well, more explicit.

What is the difference between render and yield in Rails

Can someone explain the difference between "<%= render %>" and "<%= yield %> with <% content_for :partial do %>/<% end %>"? specifically how the routing changes when switching from one to another, the benefits of using one over the other, when is it practical to use one over the other. THIS is the closest explanation I have found, but isn't quite clear enough for me.
I have been trying for several days to wrap my head around this, but it seems that each configuration I try either comes close, or errors out.
If theres are three views, aaa and bbb and ccc, and each has an index.html.erb, but bbb and ccc have a _content.html.erb partial (signified by the underscore) how can you accomplish getting the bbb or ccc partial in aaa using either render or yield?
The following works:
aaa's index.html.erb :
<div">
<%= render 'bbb/content' %>
</div>
and bbbs _content.html/erb :
<p>Content from bbb.</p>
BUT this does NOT:
aaa's index.html.erb :
<div">
<%= yield :container %>
</div>
and bbbs _content.html/erb :
<% content_for :container do %>
<p>Content from bbb.</p> ### viewed in aaa
<% end>
and cccs _content.html.erb would have nothing, or the content_for, but I still dont get aaa's index.html to be populated with content.
If I use the render, I can explicitly place the content in. But I thought that the benefit of using the yield :whatever would allow me to choose what to populate it with, and I can't get it to populate anything as soon as I change it from render to yield. Do I also have to update the routes file? If so, how do I choose which one to populate it with? Does that mean its in the controller? and needs an action?
I also have though that it depends on which file is initially routed to, but like I said, I think I need to understand the difference between the two before I can begin to use the partials to my advantage.
First of all, yield is ruby, render is rails. Usually one uses a common layout for the application whose inner content changes according to action/context. The problem usually lies in defining where our layout ends and context-specific template begins. Take, for instance, the HTML title tag. Let's say you have an application called Cities. In most cases, you want your page title to be "Cities" all the time. But, if you're for instance, inside Amsterdam page, then you would like the have "Amsterdam" as your page title.
# application.html.erb
<html>
<head>
<%= content_for?(:page_title) ? yield(:page_title) : "Cities" %>
......
# city/index.html.erb
<% content_for :page_title do %>
<%= #city.name %>
<% end %>
<div class="bla"...
Within Rails you usually define your application title in your application layout. One strategy for changing the page title would be to use content_for in the specific cities template and change accordingly.
Render, on the other hand, accomplishes different rendering strategies. Straight. When you call render, it renders. content_for/yield doesn't render automatically, it is stored somewhere and then fills up the missing spots in the due places. So, you can think of it as more as a "store/search/replace" in comparison to render, which just plain renders.
Good rule of thumb to use one over the other is: if the template you are writing needs to present different information per context, strongly consider using content_for.
yield
Ruby code (Proc class) and takes your block and does what it is supposed to do with it. Yield is also fast compared with other Ruby based ways of doing the same thing.
I'd assume (and I only) use it in the layouts because it's quick and I mindlessly do what's normal in Rails. yield is also used to pass content to a specific spot in your layout. I often have <%= yield :head %> in the head, just above the head tag, so that I can pass random weirdness that sometimes comes up.
Common Uses:
Mostly just used in layouts
(if you are fancy/inclined to do so in a Model) as a true Ruby Proc
statement.
render
Rails code that you pass arguments to that, as the docs say, "Renders the content that will be returned to the browser as the response body". partials, actions, text, files...etc.
Common Uses:
Used in both views and the controller.
When your controller method exits, it renders the associated file. So the edit controller renders edit.html.erb. It uses the specified layout or application.html.erb if none is specified.
Within your layout file, when you call yield it will fill in the information from your render. If you call yield with a parameter, it will look for a content_for section in your render file matching that parameter. I'm not completely sure, but I don't think you can call yield from outside of your layout file, and I don't think it will fill in any information except that found in your render file.
Anywhere in your layout file or your rendered file, you can render a partial by calling render with the partial name minus the underscore.
I hope that helps.
Edit to answer question in comment:
yield and render perform similar functions however yield only looks in the render file whereas render specifies which file to render. Also, render outputs the entire file, but yield with a parameter can output just a subsection of the file.
Here's a visual to put them both in perspective:
The render method is called at the end of a controller action and orchestrates what block is passed to the method that is actually rendering the application.html.erb by yielding the passed block.
https://richstone.io/debunk/

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}

How to use partial in views with different alias MIME?

Im using 2 different sets of views for 2 different user's roles.
Im using register_alias :
Mime::Type.register_alias "text/html", :basic
in the controller:
class SomeController < ApplicationController
def index
# …
respond_to do |format|
format.html # index.html.erb (advance)
format.basic # index.basic.erb
end
end
end
In some case I have to use the same code in both views, then I would use a Partial, but because of the MIME alias, I have to use 2 identical partials:
my_partial.html.erb and my_partial.basic.erb
I think there is a solution to DRY the code and use only a partial.
Do you have some solutions ?
thank you,
Alessandro
Old Answer:
I probably tried 50 different things until I figured out the right way of writing the partial once, but it was worth it because it's super simple:
Inside your index view, you normally do:
<%= render "my_partial" %>
This implicitly gets mapped to the partial corresponding to the Mime you requested, so it implies having two partial implementations. If you want a DRY partial, simply explicitly specify the format:
<%= render "my_partial.html" %>
As an added bonus of this observation, if your responds_to block of code is really just to switch based on the format and has no logic inside it, you can entirely remove that block of code and things still work implicitly.
Rails 3.2 update:
Rails has deprecated support for the above and support has been completely removed in the latest version of Rails. The following is the correct way as of Rails 3.2:
<%= render :partial => "my_partial", :formats => [:html] %>

Resources