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?
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.
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
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
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.
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
This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.