On 01/12/11 09:03, Sébastien M. wrote:
> Hello,
>
> This is my first post, and i hope i use the correct topic.
> There are two errors i do not understand in the code below
>
> I've commented out the line that don't work, and the line directly below
> works just fine
> Finally i put the errors when using the problematic lines
>
> I do not see any errors in the commented line, if someone do i'll be
> happy to understand what is wrong
>
> thx in advance
>
> Here is the code
> ----------------------------------------------------------------
> class Item
>    attr_reader :name, :price
>
>    def initialize name, price
>      @name, @price = name, price
>    end
>
>    def + item
> #    price + item.price if item.respond_to? price #### 1
>      price + item.price if item.is_a? Item
>    end
> end
>
> class Items
>    def initialize
>      @items = []
>    end
>
>    def<<  item
>      @items<<  item if item.is_a? Item
>    end
>
>    def price
>      @items.collect { |i| i.price }.inject(:+)
> #    @items.inject(:+) #### 2
>    end
> end
>
> i = Items.new
>
> i<<  Item.new('a', 1)
> i<<  Item.new('b', 2)
> i<<  Item.new('c', 3)
>
> puts i.price
> --------------------------------------------------------------
> The errors
> --------------------------------------------------------------
> ##### 1
> ./test2.rb:11:in `respond_to?': 1 is not a symbol (TypeError)
> ##### 2
> ./test2.rb:25:in `+': Item can't be coerced into Fixnum (TypeError)
>
The first error is pretty straight forward to sort out. The #respond_to? 
method is expecting a symbol, so changing 'price' to ':price' fixes 
that. The other error is due (I believe) to the fact that the inject 
ends up trying to use Numeric#+ instead of your Item#+ method when the 
object on the left of the operator is a number. To fix this you can 
define Item#coerce.

This is likely not the best way to do it, but it appears to work;

class Item
   attr_reader :name, :price

   def initialize name, price
     @name, @price = name, price
   end

   def coerce arg
     if arg.is_a? Numeric
       [arg, price]
     end
   end

   def + item
     price + item.price if item.respond_to? :price
   end
end

class Items
   def initialize
     @items = []
   end

   def << item
     @items << item if item.is_a? Item
   end

   def price
     @items.inject(:+)
   end
end

i = Items.new

i << Item.new('a', 1)
i << Item.new('b', 2)
i << Item.new('c', 3)

puts i.price


Hope that helps
Sam