Idiomatic ruby


#1

Very often I have a question method, which, in some cases, the caller
would want to know why as well.

Examples:

def valid?
end

def abort?
end

Ruby does not allow subclassing true and false, so, if these methods
return one of those, they can’t return any additional info. But
sometimes the caller needs additional info, as in:

if !valid? logger.warn “Not valid: #{why not?}”

What is the best way to handle this? I could have those methods set
@instance_variables, but this seems a little hackish, and could
introduce race conditions.

Is there anyway to return false, “reason”, or something of that sort?
What is the preferred, idiomatic way of doing this?


#2

DÅ?a Pondelok 13 Február 2006 00:23 removed_email_address@domain.invalid napísal:

Ruby does not allow subclassing true and false, so, if these methods
return one of those, they can’t return any additional info. But
sometimes the caller needs additional info, as in:

You can subclass TrueClass or FalseClass, it just wouldn’t do you much
good.

if !valid? logger.warn “Not valid: #{why not?}”

What is the best way to handle this? I could have those methods set
@instance_variables, but this seems a little hackish, and could
introduce race conditions.

Have whatever method changes state in the context of the snippet you
posted
return a object describing the result of an operation, including both
those
predicates and the reason?

Besides, the #valid? and #abort? methods would still be subject to the
same
race conditions as any other place you’d store a reason info, I think.
Personally, I smell some design weirdness, but can’t really blame it
with
this little context.

David V.


#3

removed_email_address@domain.invalid wrote:

Ruby does not allow subclassing true and false, so, if these methods
return one of those, they can’t return any additional info. But
sometimes the caller needs additional info, as in:

if !valid? logger.warn “Not valid: #{why not?}”

Use exceptions, which can contain readable messages

def my_meth
validate
# … proceed
rescue => err
logger.warn “Not valid #{err}”
end

if you want a boolean-style method

def valid?
validate && true # assuming validate returns some kind of true value
rescue
false
end

alex


#4

What is the best way to handle this? I could have those methods set
@instance_variables, but this seems a little hackish, and could
introduce race conditions.

I’m not sure I’d agree that it is hackish to set @instance_variables (or
perhaps to use a setter instead) to capture learned info. A naming
convention might help (maybe remove the "?’ and add “_reason”)

class Foo
attr_accessor :valid_reason
def valid?

self.valid_reason = “age is too young”
false
end
end


#5

How about returning multiple values?

def valid?
[ @is_valid_flag, @error_message ]
end


#6

DÅ?a Pondelok 13 Február 2006 01:03 Phrogz napísal:

How about returning multiple values?

def valid?
[ @is_valid_flag, @error_message ]
end

Wouldn’t ever evaluate as false, which means all the predicates would
have to
be true on a failure, which might be a bit confusing - I prefer to code
assertively if possible.

David V.


#7

Brian B. wrote:

attr_accessor :valid_reason
def valid?

self.valid_reason = “age is too young”
false
end
end

IMHO it’s bad practice to store this info in the instance. It really
doesn’t belong there; you run into all sorts of problems (race
conditions
in concurrent apps, consistence - you need to clear this when the
instance
state changes…).

The best solution seems to be to use an exception.

class Foo
attr_accessor :name

def ensure_valid
self.name or raise “Empty name.”
end
end

begin
f=Foo.new
f.ensure_valid
f.do_stuff()
rescue RuntimeError => e
$stderr.puts “Invalid because of: #{e.message}”
end

Kind regards

robert