Is it best practice to achieve recursion via a partial? - ruby-on-rails

I have the need to display a nested set structure in HTML. I am doing it with the following partial:
<ul<%= ' id="tree"' if depth == 0 %>>
<% items.each do |item| %>
<li id="node_<%= item.id %>"><a><%= item.name %></a>
<% if item.has_children? %>
<%= render :partial => 'tree_level', :locals => {:items => item.children, :depth => depth + 1} %>
<% end %>
</li>
<% end %>
</ul>
Is this the best place to have the code? I "feel" like there should be a to_html method on the object which dumps the entire tree structure for me, though this works.

I am not sure whether it is best practice but I used similar code for rendering project tree.
Faster alternative is to create helper method doing the same job (recursively traversing tree and adding partial strings into result string). It is a little bit PHP style :( but for such a small amount of HTML is it OK, I guess :)
Helper looks like:
def render_node(node)
res = "<ul>"
...
node.items.each {|n| res << render_node(n)}
...
res << "</ul>"
res
end
Then it is used like this:
<%=render_node ProjectTree.new%>

Well, you should realize there's a (small) overhead for using partials, so if performance is an issue, you may not want to use them this much. Otherwise I see little problem with using this.
However, you might want to use the collection-variant of partials (see "Rendering a collection of partials" on this API page, it could clean up your code a bit.

Related

If else statements in .html.erb in views

In rails, I often run into the situation where inside the views I'll do something like
<% if #some_condition_previusly_established_in_a_controller %>
<div class="one">123</div>
<% else %>
<div class="two">something else</div>
<% end %>
It looks a bit cluttery. Is this an acceptable way of working with views or not?
Unless you can think of a way to re-write this as a helper method, you're basically stuck with it looking kind of ugly. That's just how ERB is, as it was intended to be a minimal way of injecting Ruby into an otherwise plain-text template, not as something necessarily streamlined or elegant.
The good news is a syntax-highlighting editor will usually make your <% ... %> ERB blocks look visually different from your HTML so that can dramatically improve readability.
It's also why other representations like HAML have been created where that syntax is a lot less cluttered:
- if some_condition_previusly_established_in_a_controller
.one 123
- else
.two something else
For one or two such conditional logic in your views, I guess its fine but when your code gets bigger and you have multiple if..else..end and looks "cluttery", I think you should look at implementing "Presenter Pattern" which greatly cleans up your views by separating your logic to Presenters.
Here is a great tutorial I followed from Ryan Bates in his Rails Casts series on "Presenter Patterns from scratch". http://railscasts.com/episodes/287-presenters-from-scratch.
Have you tried?
<% #some_condition_previusly_established_in_a_controller ? <div class="one">123</div> : <div class="two">something else</div> %>
If your view contains lots of tags and HTML elements, you can put them into partials and logic into model
View:
<%= render :partial => #model.status %>
<%= render :partial => "file/path/#{#model.status}" %> # if your partial is in some different folder
If your status is one, then it would render the file _one.html.erb
If it is two, then it would render the file _two.html.erb automatically.
Model:
def status
if #some_condition
"one"
else
"two"
end
end
Yes, that is the standard (and yes, it looks cluttery).
If you're looking for a possibly cleaner alternative, check out: Conditional tag wrapping in Rails / ERB
You can always move the logic to the controller and leave the view clean(er).
Controller:
if #some_condition
#div_class = :one
#div_content = 123
else
#div_class = :two
#div_content = 'something else'
end
View:
<div class="<%= #div_class %>"><%= #div_content %></div>
Or using a helper:
<%= content_tag :div, #div_content, class: #div_class %>

Highly conditional Rails 3 views

I've got a Rails 3 application where I'm using quite a few conditional statements to change the design of the page. What is the best practice for keeping the logic out of the view for having such drastic amounts of conditionals?
Hypothetical Example:
<% unless #ethos.blank? %>
<%= unless #work.nil? do %>
<%= link_to "Add Work", work_path %>
<% end %>
<%= #ethos.tagline %>
<% end %>
I've got many more conditionals inside of other conditionals. What is the best way to manage this inside of one view?
You should avoid complex conditionals (and most conditionals) in views. Extract them to a Helper, or better yet, to some kind of "presenter" so that you can work with a receiver instead of those "global looking/feeling helpers"
SomeHelper
module SomeHelper
def work_link
(#ethos.present? && #work) ? link_to("Add Work", work_path) : nil
end
end
View
<%= work_link %>
<%= #ethos.tagline if #ethos.present? %>
If #ethos is likely to be nil as opposed to an empty [] array, you could instead use:
<%= #ethos.try :tagline %>
Also note that in your original view <%= unless #work.nil? do %> should have been using a - and not a =.
Oh, and I encourage you to use HAML over ERB. With HAML, the view looks like this (easier to read, isn't it) :
= work_link %>
= #ethos. try :tagline
Your original view would look like this in HAML (remember, avoid conditionals in views as much as possible!)
- unless #ethos.blank?
- unless #work.nil? do
= link_to "Add Work", work_path
= #ethos.tagline
If the code works, what is your concern? Is it aesthetics or are you having difficulty reasoning about what the code is doing because there is so much nesting?.
The simplest solution is probably just to move the conditionals inline.
<%= link_to("Add Work", work_path) if #ethos.present? && #work %>
<%= #ethos.tagline if #ethos.present? %>
This will improve readability (and therefore maintainability), though it may not go far enough to keep the Rails purists happy. Zabba's answer presents several great choices that go further down the rabbit hole.

How efficient is the current_page? helper method?

I'm curious about the efficiency of the Rails current_page? helper method. I'm using it in a view, roughly as shown below:
<% if current_page?(:action => "foo") %>
<dt>Label 1:</dt>
<% else %>
<dt>Label 2:</dt>
<% end %>
and
<% if current_page?(:action => "foo") || current_page(:action => "bar") %>
<dt>Label 1:</dt>
<% else %>
<dt>Label 2:</dt>
<% end %>
But would it be more efficient to make this switch some other way? For instance, would it be more efficient to set an instance variable in my controller actions for foo and bar, then check <% if #foo || #bar %>?
And is there any difference in the efficiency between Rails 2 and Rails 3?
I can't speak to the difference between Rails 2 and Rails 3, but looking at the source code in ActionPack, it doesn't look like the method is doing anything particularly complicated. url_for could be slightly inefficient (I don't know offhand), but unless you're doing a huge number of loops over this partial, optimizing this is probably not going to save you a noticeable amount of time.
That said, it would be easy to do some basic benchmarks -- t = Time.now; loop 1000 times over one version; v1_time = t - Time.now; t = Time.now; rinse and repeat with the other version. If you do it, let me know what you find.
All that said, it seems to me that it would be probably be cleaner conceptually to have your controller methods set appropriate flags, if the flags could be expressed as concepts not directly related to the view. I'd be curious what others think.

Rails: display #cars as a comma-separated list

Based on this query:
#cars = Car.where("manufacturer_id IN ?", #mfts.select("id")).limit(30).select("id")
How can I display the cars' IDs in the view like this (or do I need to rewrite my query)?
3,2,5,12,15,24,34,63,64,65,66,85
Thanks a lot - I've looked for this but couldn't find the right question/answer.
One solution is to do:
#view
<% #cars.each do |c| %><%= c.id %>,<% end %>
I don't know if there's a better way to go about it - this obviously leaves a stray comma at the end of the list (which isn't a dealbreaker). Any more elegant solutions?
One line:
<%= #cars.map(&:id).join(",") %>
If writing &:id seems confusing, there's another way that's a little more readable.. If y'all want to access a method or attribute, it might look better to inline a block.
<%= #cars.map { |car| car.id }.join(", ") %>
P.S... another name for map is collect.. that's what it's called in Smalltalk.
Lookin' good!
With Rails 3.0+ you can now write:
<%= #cars.map { |car| car.id }.to_sentence %>
Rails will appropriately add the comments and the word 'and' between the last two elements.

Ruby and Rails: Statement Modifiers in Views?

I have this code
<% if approved %>
<td>Flow Number</td>
<% end %>
and I'd like to shorten it using statement modifiers. Of course I can use
<%="<td>Flow Number</td>" if approved -%>
but is there a shorter way? I'd also like to get the markup out of quotes.
You could use "content_tag", which isn't actually shorter, but may be more appealing, keeping HTML out of your ruby blocks:
<%= content_tag :td, "Flow Number" if approved %>
Otherwise, you could consider writing a helper - which may be appealing if you need to reuse similar logic throughout the page (or over several pages).
Maybe HAML?
That'd be:
- if approved?
%td Flow Number
Not exactly what you're after I know.
Yeah, I think a helper method using content_tag internally would be the best short way.
Using a helper method, you could also yield to the desired output like this:
# in view helper
def show_if(condition, wrapper_tag)
condition ? content_tag(wrapper_tag, yield) : ''
end
# in view
<%= show_if(approved, :td) {'Flow Number'} %>
or
# in view helper
def show_if(condition)
condition ? yield : ''
end
# in view
<% show_if(approved) do %>
<td>Flow Number</td>
<% end %>
I like this last method for a nice generic way to show or hide whole blocks based on a condition. Hope that helps!

Resources