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

On Wed, Oct 13, 2010 at 7:45 AM, Tony A. [email protected]
wrote:

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

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

On Mon, 11 Oct 2010 23:04:59 -0500, Joel VanderWerf
[email protected] wrote in [email protected]:

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

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

On 10/13/2010 10:30 AM, Charles C. wrote:

On Mon, 11 Oct 2010 23:04:59 -0500, 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. :slight_smile:

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

On Mon, Oct 11, 2010 at 10:04 PM, Joel VanderWerf
[email protected]wrote:

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.

On 10/13/2010 04:26 PM, Tony A. wrote:

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

On Wed, Oct 13, 2010 at 6:13 PM, Joel VanderWerf
[email protected]wrote:

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

On Wed, Oct 13, 2010 at 3:18 AM, Robert K.
[email protected]wrote:

Like this?

Yes :slight_smile:

On Oct 11, 1:02pm, Charles C. [email protected] wrote:

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

On Wed, 13 Oct 2010 22:11:17 -0700 (PDT), Steve H.
[email protected] wrote in
[email protected]:

On Oct 11, 3:02pm, Charles C. [email protected] wrote:

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}

is World Sight Day, 14th October 2010 get your eyes tested by your
nearest optician and get the best possible discount. For more details
check Redirecting...

On Wed, 20 Oct 2010 22:04:49 -0700 (PDT), w_a_x_man
[email protected] wrote in
[email protected]:

On 21 October 2010 07:05, w_a_x_man [email protected] wrote:

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

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

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

var = 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 on it:

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

On Oct 11, 3:02pm, Charles C. [email protected] wrote:

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!