views:

45

answers:

3

I have a test class and a box class, in the test class i have a var called boxHolder, which is an array, i want to override the << method for this array. Inside the singleton how can i access moski_call ?

class Test
  attr_accessor :boxHolder

  def initialize()
   super
   self.boxHolder = Array.new

   class << @boxHolder
     def <<(box)
       box.setPositionWithinColumn(moski_call)
       super(box)
     end
   end
  end   

  def moski_call
    "YAAAAAAAAAAAAAAAAAAAA"
  end
end

class Box
  def initialize
  end

  def setPositionWithinColumn(str)
    puts "got a string #{str}"
  end
end

# test
box = Box.new
test = Test.new
test.boxHolder 
A: 

What about:

def self.boxHolder.<< (box)
   box.setPositionWithinColumn(moski_call)
   super(box)
end     

This would declare a method for your instance boxHolder. But boxHolder does not have access to the method moski_call

Edu
it seems the syntax is not correct, i am getting "syntax error, unexpected '.', expecting '\n' or ';'"
moski
A: 

like this:

# need this or else `moski_call` method is looked up in context of @boxholder
moski_call_output = moski_call

class << @boxholder; self; end.send(:define_method, :<<) { |box| 
     box.setPositionWithinColumn(moski_call_output)
     super(box)
}
banister
i am getting the following error: "`<<': undefined local variable or method `moski_call' for []:Array (NameError)"
moski
Ah, this works, many thanks ... can you please explain why this works, defining moski_call_output solved the problem, but whats the difference between moski_call and moski_call_output ?
moski
@moski, `moski_call` is a method and `moski_call_output` is a local variable. In a `define_method` block methods are looked up in the object the method is defined on - in this case `@boxholder`. However @boxholder does not have such a method and so it will error. local variables on the other hand are captured by the `define_method` block (closure) and will be accessible.
banister
`moski_call_output` won't get updated if `moski_call` changes... I posted a solution I prefer.
Marc-André Lafortune
A: 

You need to maintain access to the "parent" Test object. This can be done using the fact that blocks are closures:

parent = self # to be accessible in the closure

@boxHolder.define_singleton_method(:<<) do |box|
  box.setPositionWithinColumn(parent.moski_call)
  super(box)
end

Note: define_singleton_method is new in Ruby 1.9, so either upgrade, require 'backports' or do class << @boxHolder; define_method(:<<){ "..." } end if using an older Ruby.

Marc-André Lafortune