His solution is to check the parameters thusly:
title = nil if local_assigns[:title].nil?This can be improved by checking instead whether the value key is there, to handle the case in which you actually want to set the value to nil:
title = :defaultvalue unless local_assigns.has_key? :titleSince this use of local_assigns is a bit
Ah, but there is a back door! eval allows code to be executed in another context (and hence access to its local variables) provided we have a block to extract a binding from. So, we can make our helper interface be used like this:
title = opt(:title) { :defaultvalue }
Then we implement our helper thusly:module OptHelper
def opt(name, &block)
if eval("local_assigns.has_key? :#{name}", block.binding)
eval name.to_s, block.binding
else
yield
end
end
end
The first eval checks to see whether the variable was assigned, second eval is so that we can return that value. The yield executes the passed-in block to get the default value. This implementation also has the advantage that the default value can be an expensive operation that only gets executed if it's needed.It turns out however that eval is a rather slow call (the string must be parsed and compiled), so a few benchmarks show that it is better to preemptively lump the two evals together:
module OptHelper
def opt(name, &block)
was_assigned, value = eval(
"[ local_assigns.has_key?(:#{name}), local_assigns[:#{name}] ]",
block.binding)
if was_assigned
value
else
yield
end
end
end
Jaime -- why do you say that local_assigns.has_key is an "undocumented hack?"
ReplyDeleteIt is described in
http://api.rubyonrails.org/classes/ActionView/Base.html
Thanks! (Trying to get to the bottom of this over at Stack Overflow )
Ah, good catch, I think I searched for it at the time and didn't find it. Good to know, thanks!
ReplyDeletewhy wouldn't you do something like:
ReplyDeletelocal_assigns = eval('local_assigns', block.binding), and then look at the local assigns directly? I think that would be be more straight forward than was_assigns, value method.