more idiomatic way to avoid errors when calling method on variable that may be nil?

R

Robert Klemme

Nobody seems to have mentioned it, I guess since this is the place for
'pure' rubyists, but Rails has the following methods:

So with this you can write simply

=A0 var =3D =A0hash[key].try:)downcase)

That's pretty cool, but I'd prefer it return a proxy object for all non-n= il
objects that just relays the method call, and for nil a proxy that always
returns nil, ala:

hash[key].try.downcase

Like this?

require 'singleton'

class NilProxy
include Singleton
def method_missing(*a,&b) end
end

class Object
def try
self
end
end

class NilClass
def try
NilProxy.instance
end
end

["Foo", nil, "Bar"].each do |x|
p x.try.downcase
end

Kind regards

robert


--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
 
C

Charles Calvert

var = hash[key].downcase if hash[key]

This is a variant of:

var = hash[key].downcase unless hash[key].nil?

which I indicated in my original post won't work, as I want an
explicit assignment of nil to var in the event that hash[key] returns
nil. See David Black's response to my post. I'm not assuming that
this is the first occurrence of var, so it may already have a value
from a previous assignment.

You can get around this by putting ( ) around the whole if clause:

var = (hash[key].downcase if hash[key])

It works because (... if ...) returns nil if the condition is false.

While that would clearly work, it makes me a little nervous as it
isn't immediately obvious, to me at least. Most likely I just haven't
spent enough time with Ruby. :)
 
J

Joel VanderWerf

var = (hash[key].downcase if hash[key])

It works because (... if ...) returns nil if the condition is false.

While that would clearly work, it makes me a little nervous as it
isn't immediately obvious, to me at least. Most likely I just haven't
spent enough time with Ruby. :)

I agree. It reduces readability, and it's easy to forget the parens.
 
T

Tony Arcieri

[Note: parts of this message were removed to make it a legal post.]

You can get around this by putting ( ) around the whole if clause:

var = (hash[key].downcase if hash[key])

It works because (... if ...) returns nil if the condition is false.

The parens are unnecessary. The "var" variable will be implicitly bound to
nil if the if condition isn't true.
 
J

Joel VanderWerf

You can get around this by putting ( ) around the whole if clause:

var = (hash[key].downcase if hash[key])

It works because (... if ...) returns nil if the condition is false.

The parens are unnecessary. The "var" variable will be implicitly bound to
nil if the if condition isn't true.

As David Black pointed out:

var = 1
var = 2 if false
p var # ==> 1
 
T

Tony Arcieri

[Note: parts of this message were removed to make it a legal post.]

As David Black pointed out:

var = 1
var = 2 if false
p var # ==> 1

Well, avoiding destructive assignments is an alternative to wrapping the
whole expression in parens
 
S

Steve Howell

I'm using Ruby 1.8.7 patchlevel 249

Is there a more idiomatic way to do the following?

var = hash[key].nil? ? nil : hash[key].downcase

Note that if hash[key] is nil, I want nil assigned to var, so this
won't work:

var = hash[key].downcase unless hash[key].nil?

Obviously I could do this, but I'm trying to keep it on one line:

var = hash[key]
var = var.downcase unless var.nil?

I'm not sure if this is the most common idiom, but you can write a
helper method like "try" that chains method calls but aborts nicely to
nil as soon as an object dereference (or the primary object itself)
evaluates to nil.

The example below is slightly fancier than you might need, as it
supports multiple dereferences. Scroll down to the bottom to see its
usage.

$ cat foo.rb

def try(obj, *args)
for arg in args
return nil if obj.nil?
obj = obj.send(arg)
end
return obj
end

class Mission
end

class Department
def name
'HR'
end

def mission
return nil
end
end

class Employee
def department
return Department.new()
end

def name
# oops, no name field
return nil
end
end

emp = nil
puts try(emp, :mission, :department, :name)
emp = Employee.new
puts try(emp, :department, :name)

$ ruby foo.rb
nil
HR
 
C

Charles Calvert

I'm using Ruby 1.8.7 patchlevel 249

Is there a more idiomatic way to do the following?

var = hash[key].nil? ? nil : hash[key].downcase

Note that if hash[key] is nil, I want nil assigned to var, so this
won't work:

var = hash[key].downcase unless hash[key].nil?

Obviously I could do this, but I'm trying to keep it on one line:

var = hash[key]
var = var.downcase unless var.nil?

I'm not sure if this is the most common idiom, but you can write a
helper method like "try" that chains method calls but aborts nicely to
nil as soon as an object dereference (or the primary object itself)
evaluates to nil.

[snip example]

I was looking for something that didn't require patching existing
classes, but it's becoming clearer to me that such patches are much
more common than I thought and can yield cleaner code. Thanks for the
example.
 
W

w_a_x_man

I'm using Ruby 1.8.7 patchlevel 249

Is there a more idiomatic way to do the following?

var = hash[key].nil? ? nil : hash[key].downcase

Note that if hash[key] is nil, I want nil assigned to var, so this
won't work:

var = hash[key].downcase unless hash[key].nil?

Obviously I could do this, but I'm trying to keep it on one line:

var = hash[key]
var = var.downcase unless var.nil?

var = (t = h[key] and t.upcase) or nil

var = h[key].tap{|x| x ? x.upcase : nil}
 
C

Charles Calvert

I'm using Ruby 1.8.7 patchlevel 249

Is there a more idiomatic way to do the following?

var = hash[key].nil? ? nil : hash[key].downcase

Note that if hash[key] is nil, I want nil assigned to var, so this
won't work:

var = hash[key].downcase unless hash[key].nil?

Obviously I could do this, but I'm trying to keep it on one line:

var = hash[key]
var = var.downcase unless var.nil?

var = (t = h[key] and t.upcase) or nil

Using short circuit evaluation to avoid the call to upcase if h[key]
is nil, check.
var = h[key].tap{|x| x ? x.upcase : nil}

Unfortunately tap is Ruby 1.9, and as I said above, I'm using 1.8.7.

Thanks for the answers.
 
B

Benoit Daloze

I'm using Ruby 1.8.7 patchlevel 249

Is there a more idiomatic way to do the following?

var =3D hash[key].nil? ? nil : hash[key].downcase

Note that if hash[key] is nil, I want nil assigned to var, so this
won't work:

var =3D hash[key].downcase unless hash[key].nil?

Obviously I could do this, but I'm trying to keep it on one line:

var =3D hash[key]
var =3D var.downcase unless var.nil?

var =3D (t =3D h[key] and t.upcase) or nil

h[key] will return nil if key is not found, so you do not need that "or nil=
"

var =3D (t =3D h[key] and t.upcase)
var =3D h[key].tap{|x| x ? x.upcase : nil}

That is not gonna work, Object#tap return receiver.
So the only way to modify the object "tapped" is to call mutating methods o=
n it:

var =3D h[key].tap { |x| x ? x.upcase! : nil }
 
W

w_a_x_man

I'm using Ruby 1.8.7 patchlevel 249

Is there a more idiomatic way to do the following?

var = hash[key].nil? ? nil : hash[key].downcase

Note that if hash[key] is nil, I want nil assigned to var, so this
won't work:

var = hash[key].downcase unless hash[key].nil?

Obviously I could do this, but I'm trying to keep it on one line:

var = hash[key]
var = var.downcase unless var.nil?


s = hash[:foo] and s.downcase!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,776
Messages
2,569,602
Members
45,182
Latest member
BettinaPol

Latest Threads

Top