Idiomatic ruby

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?

I can think of two directions you could go real quickly.

You could go the Rails validations way of your question method having a
side-effect that populates some other member. eg:

flash[:warning] = user.errors.get_full_messages unless user.valid?

Of you could go with the Perlish way the Regex library works:

"Cows are Cool" =~ /(\w+)/
puts $1

I prefer the side-effect method.

use a block to return info:

     harp:~ > cat a.rb
     class C
       def initialize(x) @x = x end
       def valid?()
         if @x == 42
           true
         else
           yield "x is not 42" rescue nil
           false
         end
       end
     end

     c = C::new 43
     unless c.valid?{|reason| warn "not valid : #{ reason }" }
       # do something
     end

     c = C::new 42
     c.valid?{|reason| warn "not valid : #{ reason }" }

     harp:~ > ruby a.rb
     not valid : x is not 42

or invert your logic so info can be added:

   harp:~ > cat a.rb
   class C
     def initialize(x) @x = x end
     def invalid?() @x == 42 ? false : "x is not 42" end
   end

   c = C::new 43
   if((reason = c.invalid?))
     puts reason
   end

   c = C::new 42
   if((reason = c.invalid?))
     puts reason
   end

   harp:~ > ruby a.rb
   x is not 42

hth.

-a

···

On Mon, 13 Feb 2006 eastcoastcoder@gmail.com wrote:

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?

--
happiness is not something ready-made. it comes from your own actions.
- h.h. the 14th dali lama

eastcoastcoder@gmail.com wrote:

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?

def valid?( n )
  return n%2==0, "It's odd."
end

f, why = valid? 9

I like this solution the best. Unlike the solutions involving exceptions or multiple return values, it still allows valid? to be used as an ordinary boolean function when you don't care about the reason why something's not valid, yet doesn't have the race conditions involved in the side-effect solutions.

That said, I think part of the reason there's no agreed upon idiom for this is because it would have been more idiomatic in Ruby to not even have the valid? method. Instead an exception would have been thrown as soon as the object became invalid, and the exception would have contained the reason. Having your class have a valid? method seems like how you'd design things in a language which doesn't have exceptions.

Adam

···

ara.t.howard@noaa.gov wrote:

    harp:~ > cat a.rb
    class C
      def initialize(x) @x = x end
      def valid?()
        if @x == 42
          true
        else
          yield "x is not 42" rescue nil
          false
        end
      end
    end

    c = C::new 43
    unless c.valid?{|reason| warn "not valid : #{ reason }" }
      # do something
    end

    c = C::new 42
    c.valid?{|reason| warn "not valid : #{ reason }" }

    harp:~ > ruby a.rb
    not valid : x is not 42

still - some people prefer not to do control flow with exceptions. as with
most things, both things can be good. i tend to use things like

   def validate!
     raise unless valid?
   end

and use either/both where it makes sense.

regards.

-a

···

On Tue, 14 Feb 2006, Adam P. Jenkins wrote:

ara.t.howard@noaa.gov wrote:

    harp:~ > cat a.rb
    class C
      def initialize(x) @x = x end
      def valid?()
        if @x == 42
          true
        else
          yield "x is not 42" rescue nil
          false
        end
      end
    end

    c = C::new 43
    unless c.valid?{|reason| warn "not valid : #{ reason }" }
      # do something
    end

    c = C::new 42
    c.valid?{|reason| warn "not valid : #{ reason }" }

    harp:~ > ruby a.rb
    not valid : x is not 42

I like this solution the best. Unlike the solutions involving exceptions or
multiple return values, it still allows valid? to be used as an ordinary
boolean function when you don't care about the reason why something's not
valid, yet doesn't have the race conditions involved in the side-effect
solutions.

That said, I think part of the reason there's no agreed upon idiom for this
is because it would have been more idiomatic in Ruby to not even have the
valid? method. Instead an exception would have been thrown as soon as the
object became invalid, and the exception would have contained the reason.
Having your class have a valid? method seems like how you'd design things in
a language which doesn't have exceptions.

--
judge your success by what you had to give up in order to get it.
- h.h. the 14th dali lama