Forum: Ruby Idiomatic ruby

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
unknown (Guest)
on 2006-02-13 01:23
(Received via mailing list)
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?
David V. (Guest)
on 2006-02-13 01:52
(Received via mailing list)
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.
Phrogz (Guest)
on 2006-02-13 02:04
(Received via mailing list)
How about returning multiple values?

def valid?
   [ @is_valid_flag, @error_message ]
end
Alex F. (Guest)
on 2006-02-13 02:19
(Received via mailing list)
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
David V. (Guest)
on 2006-02-13 02:33
(Received via mailing list)
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.
Brian B. (Guest)
on 2006-02-13 18:04
(Received via mailing list)
>
> 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
Robert K. (Guest)
on 2006-02-13 18:23
(Received via mailing list)
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
This topic is locked and can not be replied to.