defined? method in Ruby and Rails

Honza picture Honza · Oct 26, 2008 · Viewed 48.6k times · Source

I have a quite old templating system written on top of ERB. It relies on ERB templates stored in database. Those are read and rendered. When I want to pass data from one template to another I use the :locals parameter to Rails render method. For setting default variables of those variables in some templates I use the defined? method which simply tells me if local variable has been defined and if not I initialize it with default value like this:

unless defined?(perex)
  perex = true
end

I am upgrading the app to latest Rails and I see some weird behavior. Basically this sometimes works (sometimes perex is undefined) and sometimes it does not (perex is defined and set to nil). This happens without anything else changing.

I have two questions: Is there any better way other than using defined? which is proving unreliable (was reliable for several years on top Rails 1.6)? Such a way should not result in me rewriting all the templates. I have been going through Ruby docs and was not able to find anything about defined? method. Was it deprecated or am I just plain blind?

Edit: The actual issue was caused by what seems to be a Ruby/eRB bug. Sometimes the unless statement would work, but sometimes not. The weird thing is that even if the second line got executed perex stil stayed nil to the rest of the world. Removing defined? resolved that.

Answer

Rômulo Ceccon picture Rômulo Ceccon · Oct 26, 2008

First: actually, defined? is an operator.

Second: if I understand your question correctly, the way to do it is with this Ruby idiom:

perex ||= true

That'll assign true to perex if it's undefined or nil. It's not exactly what your example does, since yours doesn't evaluate the assignment when the value is nil, but if you are relying on that then, in my opinion, without seeing it, you're not writing clear code.

Edit: As Honza noted, the statement above will replace the value of perex when it's false. Then I propose the following to rewrite the minimum number of lines:

perex ||= perex.nil?  # Assign true only when perex is undefined or nil