Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

defined? method in Ruby and Rails

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.

like image 596
Honza Avatar asked Oct 26 '08 21:10

Honza


People also ask

What is a defined method in Ruby?

A method in Ruby is a set of expressions that returns a value. Within a method, you can organize your code into subroutines which can be easily invoked from other areas of their program. A method name must start a letter or a character with the eight-bit set.

Can you define a method within a method in Ruby?

In short: no, Ruby does not support nested methods.

How do you call a method in Ruby?

We call (or invoke) the method by typing its name and passing in arguments. You'll notice that there's a (words) after say in the method definition. This is what's called a parameter. Parameters are used when you have data outside of a method definition's scope, but you need access to it within the method definition.


2 Answers

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 
like image 184
Rômulo Ceccon Avatar answered Oct 03 '22 16:10

Rômulo Ceccon


The safest way of testing if a local is defined in a Rails template is:

local_assigns[:perex] 

This is documented in the Rails API together with the explanation that defined? cannot be used because of a implementation restriction.

like image 23
mislav Avatar answered Oct 03 '22 15:10

mislav