views:

715

answers:

3

I've been a bad kid and used the following syntax in my partial templates to set default values for local variables if a value wasn't explicitly defined in the :locals hash when rendering the partial --

<% foo = default_value unless (defined? foo) %>

This seemed to work fine until recently, when (for no reason I could discern) non-passed variables started behaving as if they had been defined to nil (rather than undefined).

As has been pointed by various helpful people on SO, http://api.rubyonrails.org/classes/ActionView/Base.html says not to use

defined? foo

and instead to use

local_assigns.has_key? :foo

I'm trying to amend my ways, but that means changing a lot of templates.

Can/should I just charge ahead and make this change in all the templates? Is there any trickiness I need to watch for? How diligently do I need to test each one?

+5  A: 

How about

<% foo ||= default_value %>

This says "use foo if it is not nil or true. Otherwise assign default_value to foo"

hgimenez
I'm not sure this works as foo isn't defined unless it's passed in through the locals hash.
jonnii
Yeah, it works. There is some magic that allows you to do this with undefined locals.
mckeed
This works, but if you have default values like this, maybe it's a sign that you should use a helper?
psyho
hgimenez
I really like this version since the syntax is so compact. I suppose the big downside is that it means you can't pass nil or false as a value for the local, though, since it will be overwritten by the default.
brahn
@brahn, that's a good point. In fact, this should be avoided if `foo` is a boolean. It may rightfully have the value of `false`, and be overridden by `default_value` accidentally.
hgimenez
+3  A: 

I do this:

<% some_local = default_value if local_assigns[:some_local].nil? %>

I hope this helps...

jonnii
Though I really like the compact syntax of hgimenez's suggestion (above), this approach has the advantage of being very clear re: what's going on. (Still has the downside of not letting you pass nil as a value for the local)
brahn
What's your use case for wanting to pass nil?
jonnii
Oh, I don't have a specific case in mind. Just trying to understand the full implications. This reminded me to accept this answer :-)
brahn
+1  A: 

A helper can be created to look like this:

somearg = opt(:somearg) { :defaultvalue }

Implemented like:

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

See my blog for details on how and why.

Note that this solution does allow you to pass nil or false as the value without it being overridden.

Jaime Cham