Переопределение операторов массива переменных экземпляра в Ruby и определение области видимости

У меня есть тестовый класс и класс коробки, в тестовом классе у меня есть переменная с именем boxHolder, которая представляет собой массив, я хочу переопределить метод ‹* для этого массива. Как я могу получить доступ к 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 

person Moski Doski    schedule 22.07.2010    source источник
comment
По соглашению рубисты не используют camelCase; это box_holder, а не boxHolder и т. д ...   -  person Marc-André Lafortune    schedule 22.07.2010


Ответы (3)


нравится:

# 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)
}
person horseyguy    schedule 22.07.2010
comment
я получаю следующую ошибку: <<': undefined local variable or method moski_call 'for []: Array (NameError) - person Moski Doski; 22.07.2010
comment
Ах, это работает, большое спасибо ... не могли бы вы объяснить, почему это работает, определение moski_call_output решило проблему, но в чем разница между moski_call и moski_call_output? - person Moski Doski; 22.07.2010
comment
@moski, moski_call - это метод, а moski_call_output - локальная переменная. В блоке define_method поиск методов выполняется в объекте, для которого этот метод определен - в данном случае @boxholder. Однако у @boxholder нет такого метода, поэтому произойдет ошибка. локальные переменные, с другой стороны, захватываются блоком define_method (закрытие) и будут доступны. - person horseyguy; 22.07.2010
comment
moski_call_output не будет обновляться, если moski_call изменится ... Я опубликовал решение, которое предпочитаю. - person Marc-André Lafortune; 22.07.2010

Как насчет:

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

Это объявит метод для вашего экземпляра boxHolder. Но boxHolder не имеет доступа к методу moski_call

person Edu    schedule 22.07.2010
comment
кажется, что синтаксис неправильный, я получаю синтаксическую ошибку, неожиданный '.', ожидая '\ n' или ';' - person Moski Doski; 22.07.2010

Вам необходимо сохранить доступ к «родительскому» объекту Test. Это можно сделать, используя тот факт, что блоки являются замыканиями:

parent = self # to be accessible in the closure

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

Примечание: define_singleton_method является новым в Ruby 1.9, поэтому либо обновите, require 'backports/1.9.1/kernel/define_singleton_method', либо сделайте class << @boxHolder; define_method(:<<){ "..." } end при использовании более старого Ruby.

person Marc-André Lafortune    schedule 22.07.2010