Reputation: 590
I have this RoR snippet in a view:
<%= link_to_unless(@posts_pages[:previous].nil?,
"Previous",
blog_with_page_path(:page_num => @posts_pages[:previous])) %>
Here blog_with_page is a named route. The snippet works if @posts_pages[:previous].nil?
is false
(as expected) and the link is generated correctly. However, when @posts_pages[:previous].nil?
is true
, instead of simply getting the "Previous"
string back, I get an error telling me that the route couldn't be generated using :page_num=>nil
. Is this the expected behavior? If the condition is met, the route code shouldn't be evaluated, should it?
Here's the complete error:
blog_with_page_url failed to generate from {:page_num=>nil, :action=>"show", :controller=>"pages"}, expected: {:action=>"show", :controller=>"pages"}, diff: {:page_num=>nil}
I've been looking at the link_to_unless
code and I don't understand why I get the error since it should be returning simply the name:
# File actionpack/lib/action_view/helpers/url_helper.rb, line 394
def link_to_unless(condition, name, options = {}, html_options = {}, &block)
if condition
if block_given?
block.arity <= 1 ? capture(name, &block) : capture(name, options, html_options, &block)
else
name
end
else
link_to(name, options, html_options)
end
end
I'm using Rails 2.3.11 and Ruby 1.8.7
Cheers!
Upvotes: 2
Views: 733
Reputation: 43113
Given the various other answers which explain your problem, why not try this as your solution:
link_to( "Previous", blog_with_page_path(:page_num => @posts_pages[:previous]) ) unless @posts_pages[:previous].nil?
This will evaluate the "unless" condition first, sparing you the bogus link_to when @post_pages[:previous]
is nil.
-- EDIT --
As pointed out in the comment, since you need the string back maybe the simplest way is just a ternary:
@posts_pages[:previous].nil? ? "Previous" : link_to( "Previous", blog_with_page_path(:page_num => @posts_pages[:previous]) )
Upvotes: 1
Reputation: 5477
Because Ruby is not a lazy language, blog_with_page_path(:page_num => @posts_pages[:previous])
gets evaluated as soon as you call it, regardless of whether the value ever gets used by link_to_unless
.
Upvotes: 2
Reputation: 2010
Function arguments are always evaluated before the function runs, whether or not they are needed:
ree-1.8.7-2011.03 :005 > def print_unless(condition, thing_to_print)
ree-1.8.7-2011.03 :006?> puts "I started executing"
ree-1.8.7-2011.03 :007?> puts thing_to_print unless condition
ree-1.8.7-2011.03 :008?> end
=> nil
ree-1.8.7-2011.03 :009 > print_unless(true, 1/0)
ZeroDivisionError: divided by 0
from (irb):9:in `/'
from (irb):9
from :0
Upvotes: 0
Reputation: 83680
It looks like a bug. This code is not "lazy" so it executes all statements. So you can go three ways:
if .. else
Like this
<%= link_to_unless(@posts_pages[:previous].nil?,
"Previous",
blog_with_page_path(:page_num => @posts_pages[:previous] || 0)) %>
Instead of 0
you can set any number, it will never be setted
Upvotes: 0