more idiomatic way to avoid errors when calling method on variable that may be 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?
 
K

Kirk Haines

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

I see nothing wrong with an explicit approach. Here is an alternative, though:

var = hash[key].downcase rescue nil


Kirk Haines
 
J

Josh Cheek

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

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?

--
Charles Calvert
Moderator - alt.computer.consultants.moderated
Submission Address: (e-mail address removed)
Contact Address: (e-mail address removed)
I usually do:
hash = { :key1 => nil , :key2 => "aBcD" }

var = hash[:key1] && hash[:key1].downcase
var # => nil

var = hash[:key2] && hash[:key2].downcase
var # => "abcd"


Notice the nuance that if hash[:key1] is false, that will be assigned to
var.
This also works:

var = hash[:key1].downcase rescue nil
var # => nil

var = hash[:key2].downcase rescue nil
var # => "abcd"


But people seem to really be opposed to doing this. Not entirely sure why.
Maybe because it wouldn't raise an error if, for example, you put some
absurd type of data in your hash?
 
J

Joel VanderWerf

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?

cheat a little?

var = hash[key]; var &&= var.downcase

another idea:

class Object
def and_then
yield self
end
end

class NilClass
def and_then
self
end
end

var = hash[key].and_then {|val| val.downcase}
 
J

Joel VanderWerf

But people seem to really be opposed to doing this. Not entirely sure why.
Maybe because it wouldn't raise an error if, for example, you put some
absurd type of data in your hash?

Absurd? It's an easy to get symbols where strings should be...

key = "a"
hash = {"a" => :Foo}
var = hash[key].downcase rescue nil
p var # ==> nil
 
D

David A. Black

Hi --

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?

It actually will work if var has not been initialized before. But that's
more or less an artifact of how the parser treats local variable
assignment expressions, and is pretty fragile:
hash = {} => {}
var = hash[1].downcase unless hash[1].nil? => nil
var => nil
var2 = "something" => "something"
var2 = hash[1].downcase unless hash[1].nil? => nil
var2
=> "something"


David

--
David A. Black, Senior Developer, Cyrus Innovation Inc.

The Ruby training with Black/Brown/McAnally
Compleat Philadelphia, PA, October 1-2, 2010
Rubyist http://www.compleatrubyist.com
 
S

Scott Gonyea

No! :( Blanket rescue statements are not to be used. It's a trivial
example, sure, but still.

if hash.has_key?:)key) and hash[:key].is_a?(String)
# do stuff
end

Or, your rescue example... I'd at least rewrite it as:

var =3D begin
hash[key].downcase
rescue NoMethodError
nil
end

If you use Ruby Ketsup (aka, Active Support), you can put this everywhere:

val =3D hash[key] if hash[key].present?
#or...
val =3D hash[key] unless hash[key].blank?

So you know why you're rescuing here. Blanket rescue foolishness can
and will cascade throughout your application. Again, trivial
example... But I think it shouldn't really be done in any
circumstance.

Scott

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

I see nothing wrong with an explicit approach. =A0Here is an alternative,= though:

var =3D hash[key].downcase rescue nil


Kirk Haines
 
S

Scott Gonyea

Because I love dead horses (or just low productivity). Two more code exampl=
es:

# Just get a String, rawr
var =3D begin
hash[key].to_s.downcase
rescue NoMethodError
""
end

# Be a good citizen, and tread carefully
var =3D case hash[key]
when String then hash[key].downcase
when Numeric then hash[key].to_s
when Array then hash[key].join.downcase # rescue block for safety=
?
when Hash then hash[key].values.join.downcase # rescue
block for safety?
else puts "LOOK AT WHAT YOU'VE DONE. YOU'VE MADE RUBY =
CRY."
end


No! :( =A0Blanket rescue statements are not to be used. =A0It's a trivial
example, sure, but still.

if hash.has_key?:)key) and hash[:key].is_a?(String)
=A0# do stuff
end

Or, your rescue example... I'd at least rewrite it as:

var =3D begin
=A0 =A0 =A0 =A0hash[key].downcase
=A0 =A0 =A0rescue NoMethodError
=A0 =A0 =A0 =A0nil
=A0 =A0 =A0end

If you use Ruby Ketsup (aka, Active Support), you can put this everywhere= :

val =3D hash[key] if hash[key].present?
#or...
val =3D hash[key] unless hash[key].blank?

So you know why you're rescuing here. =A0Blanket rescue foolishness can
and will cascade throughout your application. =A0Again, trivial
example... =A0But I think it shouldn't really be done in any
circumstance.

Scott

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

I see nothing wrong with an explicit approach. =A0Here is an alternative= , though:

var =3D hash[key].downcase rescue nil


Kirk Haines
 
D

David A. Black

Hi --

Because I love dead horses (or just low productivity). Two more code
examples:

# Just get a String, rawr
var = begin
hash[key].to_s.downcase
rescue NoMethodError
""
end

You should never get a NoMethodError on #to_s; it's defined on Object.
So you could write that as:

var = hash[key].to_s.downcase

(though in the original post, var was supposed to be set to nil if
hash[key] was nil).


David

--
David A. Black, Senior Developer, Cyrus Innovation Inc.

The Ruby training with Black/Brown/McAnally
Compleat Stay tuned for next event!
Rubyist http://www.compleatrubyist.com
 
K

KOTP

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

This is identical to saying this:
var = hash[key] && hash[key].to_s.downcase

:: Hope that helps.
 
T

Tony Arcieri

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

Haskell solves the "may be nil" problem with the Maybe monad (from Haskell).
There's many ways to implement something similar... such as implementing
Object#maybe whose method_missing (e.g. Maybe#method_missing) proxies to the
original object. NilClass can also implement #maybe, and always return nil.

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?

--
Charles Calvert
Moderator - alt.computer.consultants.moderated
Submission Address: (e-mail address removed)
Contact Address: (e-mail address removed)
 
C

Charles Calvert

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

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 usually do:
hash = { :key1 => nil , :key2 => "aBcD" }

var = hash[:key1] && hash[:key1].downcase
var # => nil

var = hash[:key2] && hash[:key2].downcase
var # => "abcd"

Using the short-circuit evaluation of && to avoid calling downcase.
Interesting.
Notice the nuance that if hash[:key1] is false, that will be assigned to
var.

Yes, that's a weakness of the technique, though it wouldn't matter in
the use case at hand.
This also works:

var = hash[:key1].downcase rescue nil
var # => nil

var = hash[:key2].downcase rescue nil
var # => "abcd"

But people seem to really be opposed to doing this. Not entirely sure why.
Maybe because it wouldn't raise an error if, for example, you put some
absurd type of data in your hash?

I don't have enough experience with exception handling in Ruby to know
for sure, but I suspect that it's considered a bad practice because it
might accidentally catch exceptions other than the intended one. It's
being discussed in another subthread, so I'll watch that.
 
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?

cheat a little?

var = hash[key]; var &&= var.downcase

Well, yes, I suppose that I could do that. :) I think that the
trinary operator is a bit more obvious, but then I'm used to it.
another idea:

class Object
def and_then
yield self
end
end

class NilClass
def and_then
self
end
end

var = hash[key].and_then {|val| val.downcase}

Clever, but requires patching core classes, which I'd like to avoid.
 
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?

It actually will work if var has not been initialized before. But that's
more or less an artifact of how the parser treats local variable
assignment expressions, and is pretty fragile:

Exactly. Plus, it's programming by side effect, which gives me the
willies. I wondered if someone would bring it up, though. :)

[snip proof]
 
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

This is identical to saying this:
var = hash[key] && hash[key].to_s.downcase

In my use case, true, because the type of the value will either be
NilClass or String. As Josh Cheek pointed out, though, if hash[key]
returns false, var would be false, rather than "false".

Thanks for the suggestion.
 
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.
 
J

Joel VanderWerf

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.
 
R

Rick DeNatale

Haskell solves the "may be nil" problem with the Maybe monad (from Haskell).
There's many ways to implement something similar... such as implementing
Object#maybe whose method_missing (e.g. Maybe#method_missing) proxies to the
original object. NilClass can also implement #maybe, and always return nil.

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?

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

class Object
def try(method, *args, &block)
send(method, *args, &block)
end
end

class NilClass
def try(*args)
nil
end
end

So with this you can write simply

var = hash[key].try:)downcase)

--
Rick DeNatale

Help fund my talk at Ruby Conf 2010:http://pledgie.com/campaigns/13677
Blog: http://talklikeaduck.denhaven2.com/
Github: http://github.com/rubyredrick
Twitter: @RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale
 
T

Tony Arcieri

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

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

var = hash[key].try:)downcase)

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

hash[key].try.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

Forum statistics

Threads
473,999
Messages
2,570,246
Members
46,839
Latest member
MartinaBur

Latest Threads

Top