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.
Related
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.
Warning: Noob here.
I know this is a trivial subject but I'm having a lot of difficulty in figuring out how exactly I can simplify my views by moving parts of them into helpers. For example, I've always read that conditionals in your views are prime candidates for extraction into helpers, but I couldn't really find examples of this, and my attempts to achieve this failed.
For example, suppose I have:
#index.html.erb
<% for beast in #beasts do -%>
<% if beast.dead? -%>
<%= beast.body %>
<%= link_to "bury", bury_beast_path( :id => beast.id ) %>
<% else -%>
<%= beast.body %>
<%= link_to "kill!", kill_beast_path( :id => beast.id ) %>
<% end -%>
<% end -%>
It annoys me a little to have this in my view, but how exactly could I move this to a helper instead? And further simplify it, if possible. (I've read somewhere that conditionals are bad but it's just beyond me how you could program anything without them.)
Another example: I need to id my body tags with the format controller_action. The best I've got so far is this:
#index.html.erb
<body id="<%= controller_action %>">
…and…
#application_helper.rb
def controller_action
#id = #controller.controller_name + "_" + #controller.action_name
end
I'm no expert, but that's still ugly even to me.
To make things more complicated, Ryan Singer said something I liked: to treat ERB like an image tag, using helpers to "reveal intention". Then in the next breath saying that you should have no HTML in helpers for that is the way to hell. WTF? How are both things compatible? If it's come to the point where you can just declare behaviors in the view, surely there should be a lot of HTML to be rendered behind the scenes? I can't grasp it.
So, that's basically it. I'd appreciate if anyone could share some thoughts on this, or point me to some good in depth reading on the subject – which I've found to have a really weak coverage on the web. I've already googled it to exhaustion but who knows.
Refactoring makes your views easier to maintain. The problem is choosing where the refactored code goes.
Your two choices are partials and helpers. There's no stone-set rules dictating which should be used where. There are a couple of guidelines floating around like the one stating that helpers should not contain HTML.
Generally partials are better suited for refactoring sections that are more HTML/ERB/HAML than ruby. Helpers on the other hand are used for chunks of ruby code with minimal HTML or generating simple HTML from parameters.
However, I don't agree with the sentiment that helpers should contain no HTML at all. A little is ok, just don't over do it. The way helpers are processed hinder their use for producing large amounts of HTML. Which is why it's suggested that your helpers contain minimal amounts of HTML. If you look at the source the helpers that ship with rails you will notice that most of them generate html. The few that don't, are mainly used to generate parameters and evaluate common conditions.
For example, any of the form helpers or link_to variants fit the first form of helpers. While things like url_for and logged_in? as supplied by various authentication models are of the second kind.
This is the decision chain I use to determine whether to factor code from a view into a partial or helper.
Repeating or nearly identical statements producing a single shallow html tag? => helper.
Common expression used as an argument for another helper? => helper.
Long expression (more than 4 terms) used as an argument for another helper? => helper.
4 or more lines of ruby (that is not evaluated into HTML)? => helper.
Pretty much everything else => partial.
I'm going to use the code you're looking to refactor as an example:
I would refactor the view in the question this way:
app/helpers/beast_helper.rb:
def beast_action(beast)
if beast.dead?
link_to "bury", bury_beast_path(beast)
else
link_to "kill!", kill_beast_path(beast)
end
end
app/views/beasts/_beast.html.erb:
<%= beast.body %>
<%= beast_action(beast) %>
app/views/beasts/index.html.erb:
<%= render :partial => "beast", :collection => #beasts %>
It's technically more complicated, because it's 3 files, and 10 lines total as opposed to 1 file and 10 lines. The views are now only 3 lines combined spread over 2 files. The end result is your code is much more DRY. Allowing you to reuse parts or all of it in other controllers/actions/views with minimal added complexity.
As for your body tag id. You should really be using content_for/yield. For that kind of thing.
app/views/layouts/application.html.erb
...
<body id="<%= yield(:body_id) %>">
...
app/views/beasts/index.html.erb
<% content_for :body_id, controller_action %>
...
This will allow you to override the id of the body in any view that requires it. Eg:
app/views/users/preferences.html.erb
<% content_for :body_id, "my_preferences" %>
The first thing I'd do would be this:
#index.html.erb
<%= render #beasts %>
#_beast.html.erb
<%= beast.body %>
<%= link_to_next_beast_action(beast) %>
#beast_helper.rb
def link_to_next_beast_action(beast)
if beast.dead?
link_to "bury", bury_beast_path( :id => beast.id )
else
link_to "kill!", kill_beast_path( :id => beast.id )
end
end
What I've done is separate out the rendering of the beast into a partial which uses collection semantics.
Then I've moved the logic for showing the kill/bury links into a beast helper. This way if you decide to add another action (for example, 'bring back from dead'), you'll only have to change your helper.
Does this help?
A third choice is to use a view model from the Cells gem. This is a very popular framework that brings object-orientation to the view layer in Rails.
# app/cells/beast/cell.rb
class Beast::Cell < Cell::Concept
def show
return dead if model.dead?
kill
end
private
def dead
link_to "bury", bury_beast_path( :id => model.id )
# you could render a view here, too!
end
def kill
link_to "kill!", kill_beast_path( :id => model.id )
end
end
You then render a view model using a helper (in the view or controller).
# app/views/beasts/index.erb
<%= concept(:beast, #beast).call %>
<%-# this returns the link content %>
That's all! You can test this cell isolated in a separate test. Cells also give you view rendering, view inheritance and many more things.
As an example, you could use a view for the kill link.
# app/cells/beast/cell.rb
class Beast::Cell < Cell::Concept
# ..
def kill
render :kill
end
end
This renders the cell's killer view.
# app/cells/beast/views/index.erb
<%= link_to "kill!", kill_beast_path( :id => model.id ) %>
Note the location of the view, it's nicely packaged into the cell directory.
And, yes, cells can do HAML and any other template engine supported by AbstractController.
Another startegy would be to not use templates and helpers at all.
For rendering you could :
render your views directly from your controllers using render(:inline => ). If you still want to keep Views and Controllers formally separated you can create modules / mixins that you include into the controllers.
or create your own view classes and use them to render your response.
The idea behind this is that helpers and rails erb templating system don't take advantage of OOP, so that at the end of the day you can't define general behaviours that you'll specialize according to each controller's/request's needs; more often than not one ends up rewriting very similar looking chunks of code, which is not very nice from a maintenance standpoint.
Then if you still need some helper methods (eg. form_tag, h, raw, ...) you only have to include them in your controller / dedicated view class.
See this : rails-misapprehensions-helpers-are-shit for a fun but useful article.
EDIT: to not sound like a complete douche, I'd say implementing this depends on how big your application is supposed to be, and how often you're going to have to update your code. Plus, if you're delegating the design to a non-programmer, he/she may well be in for some programming courses before digging into your code, which admittedly would be less directly understandable than with templates syntax.
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!
we use
(1..10).each do |i|
p i
end
so that a value is "yield" to i in a block...
but what about
<% form_for #story do |f| %>
<%= f.text_field :name %>
<% end %>
there is no loop at all... why do we need to make it look like a loop? Can't we do it without making it look like a loop? (write in another way)?
Also, must be use a Story instance here? Can't we just use :story and achieve the same result? The #story instance is just newly created and has no data at all -- does it actually help creating the form? Can't :story suffice already? thanks.
Update:
is the idea similar to: (just pseudo code)
with_model_give_form (#story) do |f|
f.begin_form
f.text_field :name
f.end_form
end
so i think the block method will save the begin_form and end_form because it automatically add the begin and end before and after calling the block. is that the main benefit?
I think you have misunderstood the way Ruby works in this case.
It is true that you put everything in a block, but it has nothing to do with a loop.
What you actually do in
(1..10).each do |i|
p i
end
is creating the block {|i| p(i); } and sending it to the function Range.each()
It is the same thing with the form_for. What you actually do is creating the block {|f| puts( f.text_field(:name)); } and passing it to the form_for function. (now it doesn't use puts, but some string concatenation, but you get the idea).
So it's basically not a loop, but a lambda function that will be called several times when called in a loop.
Here is some more information about blocks
Update:
Regarding your update. In a way, yes, that is the main benefit. It is not completely true, but you get the idea. The purpose for that block (and a lot other) are to make the code easier to read and understand. And also to speed up the development.
Dividing code into blocks have always been sought for, just look at the concept of functions, classes and other statements.
for instance, the form_for is mostly for speeding up the development. Yes, it creates the start and end tags, but that is not all.
<% form_for #story do |f| %>
<%= f.text_field :name -%>
<% end %>
Could actually be written something like:
<form action="<%= polymorphic_edit_path(#story) -%>"
id="<%= #story.class_name.underscore -%>">
<%= text_field :story, :name -%>
</form>
of course, it wouldn't exactly be like that, this is a lot simplified and the form_for function can do a lot more than that, but at least you should get the picture.
A "yield" does not always imply a loop - it's more of a "block" concept, it just happens to manifest itself in more of a iteration / loop context. The more you think about yield being a loop construct the more you pigeon hole yourself and prevent yourself from truly learning the awesomeness of blocks.
As for :story, yeah, it is kind of lame. You would think that Rails would just constantize it and infer its model from there. You could also just also Story.new in your view, versus doing that in your controller, but yeah, it's the same thing.
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.