More idiomatic way to avoid errors when calling method on variable that may be 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: accm@celticwolf.net
Contact Address: accm_mod@celticwolf.net

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

var = hash[key].downcase rescue nil

Kirk Haines

···

On Mon, Oct 11, 2010 at 2:05 PM, Charles Calvert <cbciv@yahoo.com> wrote:

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 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?

···

On Mon, Oct 11, 2010 at 3:05 PM, Charles Calvert <cbciv@yahoo.com> wrote:

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: accm@celticwolf.net
Contact Address: accm_mod@celticwolf.net

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}

···

On 10/11/2010 01:05 PM, Charles Calvert wrote:

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?

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

···

On Tue, 12 Oct 2010, Charles Calvert wrote:

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

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

:: Hope that helps.

···

On Oct 11, 3:02 pm, Charles Calvert <cb...@yahoo.com> wrote:

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

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.

···

On Mon, Oct 11, 2010 at 2:05 PM, Charles Calvert <cbciv@yahoo.com> wrote:

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: accm@celticwolf.net
Contact Address: accm_mod@celticwolf.net

--
Tony Arcieri
Medioh! A Kudelski Brand

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

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

···

On Oct 11, 1:02 pm, Charles Calvert <cb...@yahoo.com> wrote:

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}

···

On Oct 11, 3:02 pm, Charles Calvert <cb...@yahoo.com> wrote:

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!

···

On Oct 11, 3:02 pm, Charles Calvert <cb...@yahoo.com> wrote:

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?

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

···

On 10/11/2010 01:19 PM, Josh Cheek wrote:

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?

No! :frowning: 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 = begin
        hash[key].downcase
      rescue NoMethodError
        nil
      end

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

val = hash[key] if hash[key].present?
#or...
val = 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

···

On Mon, Oct 11, 2010 at 1:17 PM, Kirk Haines <wyhaines@gmail.com> wrote:

On Mon, Oct 11, 2010 at 2:05 PM, Charles Calvert <cbciv@yahoo.com> wrote:

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

wrote in
<AANLkTik1Ob=uNRQrDF+fRftzY=dj0zp6r2fRNNTL-n0k@mail.gmail.com>:

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

···

On Mon, 11 Oct 2010 15:19:00 -0500, Josh Cheek <josh.cheek@gmail.com>

On Mon, Oct 11, 2010 at 3:05 PM, Charles Calvert <cbciv@yahoo.com> wrote:

--
Charles Calvert
Moderator - alt.computer.consultants.moderated
Submission Address: accm@celticwolf.net
Contact Address: accm_mod@celticwolf.net

On Mon, 11 Oct 2010 15:48:08 -0500, Joel VanderWerf
<joelvanderwerf@gmail.com> wrote in <4CB37804.40709@gmail.com>:

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. :slight_smile: 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.

···

On 10/11/2010 01:05 PM, Charles Calvert wrote:

--
Charles Calvert
Moderator - alt.computer.consultants.moderated
Submission Address: accm@celticwolf.net
Contact Address: accm_mod@celticwolf.net

On Mon, 11 Oct 2010 16:33:22 -0500, "David A. Black"
<dblack@rubypal.com> wrote in
<alpine.LFD.2.00.1010111728350.31411@rubyclinic.com>:

···

On Tue, 12 Oct 2010, Charles Calvert wrote:

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. :slight_smile:

[snip proof]
--
Charles Calvert
Moderator - alt.computer.consultants.moderated
Submission Address: accm@celticwolf.net
Contact Address: accm_mod@celticwolf.net

On Mon, 11 Oct 2010 19:26:46 -0700 (PDT), KOTP
<keeperotphones@gmail.com> wrote in
<517830db-a8e6-48ef-b8bb-dd9707cb35e7@c10g2000yqh.googlegroups.com>:

···

On Oct 11, 3:02 pm, Charles Calvert <cb...@yahoo.com> wrote:

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.
--
Charles Calvert
Moderator - alt.computer.consultants.moderated
Submission Address: accm@celticwolf.net
Contact Address: accm_mod@celticwolf.net

On Mon, 11 Oct 2010 22:01:43 -0500, "ara.t.howard"
<ara.t.howard@gmail.com> wrote in
<0b78a0a6-c2f2-4321-92bc-d7e6acc5c869@d25g2000yqc.googlegroups.com>:

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.

···

--
Charles Calvert
Moderator - alt.computer.consultants.moderated
Submission Address: accm@celticwolf.net
Contact Address: accm_mod@celticwolf.net

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)

···

On Mon, Oct 11, 2010 at 10:59 PM, Tony Arcieri <tony.arcieri@medioh.com> wrote:

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.

On Mon, Oct 11, 2010 at 2:05 PM, Charles Calvert <cbciv@yahoo.com> wrote:

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?

--
Rick DeNatale

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

On Wed, 13 Oct 2010 22:11:17 -0700 (PDT), Steve Howell
<showell30@yahoo.com> wrote in
<f37c8bdd-02c4-4b93-a5b6-d72b804074c4@l20g2000yqm.googlegroups.com>:

···

On Oct 11, 1:02 pm, Charles Calvert <cb...@yahoo.com> wrote:

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.

--
Charles Calvert
Moderator - alt.computer.consultants.moderated
Submission Address: accm@celticwolf.net
Contact Address: accm_mod@celticwolf.net