Can you enable coercion between types?

I have a ruby class called Duration that represents a unit of time.
It's main attribute is a number that represents the duration in
seconds.

If I try to save it to a DB using Rails, I get a "Duration can not be
coerced into Float" error. Is there a way to modify the definition of
Duration to enable automatic coercion into a float? (I'm thinking of
something like adding a "to_float" method to the class.)

thanks in advance
larry

Did you try #to_f?

T.

try this:

     harp:~ > cat a.rb
     class Duration
       def initialize seconds
         @seconds = seconds
       end
       def coerce other
         if other.class == @seconds.class
           [@seconds, other]
         else
           [Float(@seconds), Float(other)]
         end
       end
     end

     p(40.0 + Duration::new(2.0))
     p(40 + Duration::new(2))

     harp:~ > ruby a.rb
     42.0
     42

hth.

-a

···

On Sun, 19 Mar 2006, larry wrote:

I have a ruby class called Duration that represents a unit of time.
It's main attribute is a number that represents the duration in
seconds.

If I try to save it to a DB using Rails, I get a "Duration can not be
coerced into Float" error. Is there a way to modify the definition of
Duration to enable automatic coercion into a float? (I'm thinking of
something like adding a "to_float" method to the class.)

thanks in advance
larry

--
share your knowledge. it's a way to achieve immortality.
- h.h. the 14th dali lama

Could you explain what this is doing? I don't get the effect.

Thanks,
T.

···

ara.t.howard@noaa.gov wrote:

try this:

     harp:~ > cat a.rb
     class Duration
       def initialize seconds
         @seconds = seconds
       end
       def coerce other
         if other.class == @seconds.class
           [@seconds, other]
         else
           [Float(@seconds), Float(other)]
         end
       end
     end

There's a gottcha here. Coerce is used to implement double dispatch and
so needs to return the coerced values in the opposite order (see Pick
Axe)--(other,self), not (self,other).

The way you wrote it works fine for addition & multiplication, but
you'll pull your hair out the first time you try to subtract or divide.

--MarkusQ

P.S. And I think (as Tom noted) that "to_f" was the answer to the
original question.

···

On Sat, 2006-03-18 at 17:11, ara.t.howard@noaa.gov wrote:

try this:

     harp:~ > cat a.rb
     class Duration
       def initialize seconds
         @seconds = seconds
       end
       def coerce other
         if other.class == @seconds.class
           [@seconds, other]
         else
           [Float(@seconds), Float(other)]
         end
       end

my understanding of coerce is that it's used to produce a pair of operands -
myself and other - which can be used to perform operations.

so, in the case of Fixnum#+ we might see

   def + other
     x, y = other.coerce self
     x + y
   end

which is to say Fixnum need not know about how to add every possible class -
rather it leaves that knowledge up to those classes to export via thier coerce
method. as i understand it that method should return two new objects which
interoperate so, in the case above (which is made up) we might have

   duration = Duration::new 42.0

   42 + duration

here would call duration(42) and expect back an x and y which could be added
together. so, if @seconds happens to be a Fixnum the result will be another
Fixnum. if @seconds happens to a Float the result would be a Float.

i admit that i'm not crystal clear on this, but it sure seems to work:

     harp:~ > cat a.rb
     class Duration
       def initialize seconds
         @seconds = seconds
       end
       def coerce other
         if other.class == @seconds.class
           [@seconds, other]
         else
           [Float(@seconds), Float(other)]
         end
       end
       def + other
         x, y = coerce(other)
         x + y
       end
     end
     def Duration(*a, &b); Duration::new(*a, &b); end

     p( 40 + Duration(2.0) ) # Float 42.0
     p( 40 + Duration(2) ) # Fixnum 42
     p( 40.0 + Duration(2.0) ) # Float 42.0
     p( 40.0 + Duration(2) ) # Float 42.0
     p( Duration(40) + 2 ) # Fixnum 42
     p( Duration(40) + 2.0 ) # Float 42.0
     p( Duration(40.0) + 2) # Float 42.0
     p( Duration(40.0) + 2.0 ) # Float 42.0

     harp:~ > ruby a.rb
     42.0
     42
     42.0
     42
     42.0

i assume one would do something nice with the coerce method using Time, Date,
and DateTime objects too...

regards.

-a

···

On Sun, 19 Mar 2006, Trans wrote:

ara.t.howard@noaa.gov wrote:

try this:

     harp:~ > cat a.rb
     class Duration
       def initialize seconds
         @seconds = seconds
       end
       def coerce other
         if other.class == @seconds.class
           [@seconds, other]
         else
           [Float(@seconds), Float(other)]
         end
       end
     end

Could you explain what this is doing? I don't get the effect.

--
share your knowledge. it's a way to achieve immortality.
- h.h. the 14th dali lama

indeed. good catch.

cheers.
-a

···

On Sun, 19 Mar 2006, Markus wrote:

On Sat, 2006-03-18 at 17:11, ara.t.howard@noaa.gov wrote:

try this:

     harp:~ > cat a.rb
     class Duration
       def initialize seconds
         @seconds = seconds
       end
       def coerce other
         if other.class == @seconds.class
           [@seconds, other]
         else
           [Float(@seconds), Float(other)]
         end
       end

There's a gottcha here. Coerce is used to implement double dispatch and
so needs to return the coerced values in the opposite order (see Pick
Axe)--(other,self), not (self,other).

The way you wrote it works fine for addition & multiplication, but
you'll pull your hair out the first time you try to subtract or divide.

--
share your knowledge. it's a way to achieve immortality.
- h.h. the 14th dali lama