2014-10-11 4 views
1

Представьте, что мы имеем три класса:рубин метапрограммирования получить контейнер класс переменного экземпляра из метода экземпляра

class SomeClass 

    def container_class 
    # ... 
    end 

end 

class Container 
    attr_accessor :property 
end 

class Container2 
    attr_accessor :lalala 
end 

# now I create instances 
p = SomeClass.new 

c = Container.new 
c.property = p 
c.property.container_class # => should return Container 

c2 = Container2.new 
c2.lalala = p 
c2.lalala.container_class # => should return Container2 

Вопрос: Как я могу написать метод container_class с помощью рубинового метапрограммирования так он возвращает класс контейнера (композиция), например откуда этот метод называется?

Вы можете использовать только метапрограммирование. Контейнерные классы не следует изменять вручную.

+0

Что вы просите, невозможно. Рассмотрим этот случай: 'p = SomeClass.new; c.property = p; c2.property = p'. Что бы сделать «container_class» в этом случае? – Max

+0

Макс, возврат Контейнер первый раз и Контейнер2 второй раз – kajamite

+0

@bloodthirsty_ruby_god Это делает * без чувства *. Теперь вы хотите отслеживать, сколько раз метод вызывается и каждый раз возвращать другой результат? – meagar

ответ

0

Я взял этот ответ от here. Вот способ получить вызывающему связывания:

require 'continuation' 

def caller_binding 
    cc = nil  # must be present to work within lambda 
    count = 0 # counter of returns 

    set_trace_func lambda { |event, file, lineno, id, binding, klass| 
    # First return gets to the caller of this method 
    # (which already know its own binding). 
    # Second return gets to the caller of the caller. 
    # That's we want! 
    if count == 2 
     set_trace_func nil 
     # Will return the binding to the callcc below. 
     cc.call binding 
    elsif event == "return" 
     count += 1 
    end 
    } 
    # First time it'll set the cc and return nil to the caller. 
    # So it's important to the caller to return again 
    # if it gets nil, then we get the second return. 
    # Second time it'll return the binding. 
    return callcc { |cont| cc = cont } 
end 

Теперь вы можете определить свой класс SomeClass следующим образом:

class SomeClass 
    def container_class 
    return unless bnd = caller_binding 
    bnd.eval "self.class" 
    end 
end 
+0

Спасибо, я думаю, что это решение, драгоценный камень для него (который работает с разными версиями ruby): https://github.com/banister/binding_of_caller – kajamite

0

Вы можете использовать это, im не используя самый красивый способ, но вы можете написать новый аксессор, который вводит эти методы.

Классы:

class Container 
    def property=(arg) 
    arg.called_from = self.class 
    @arg = arg 
    end 

    def property 
    @arg 
    end 
end 

class SomeClass 
    def container_class 
    @klass 
    end 

    def called_from=(klass) 
    @klass = klass 
    end 
end 

Spec:

require_relative 'container' 

describe Container do 
    let(:container) { Container.new } 

    it 'passes message' do 
    container.property = SomeClass.new 

    expect(container.property.container_class).to eq Container 
    end 
end 
+0

Идея заключается в использовании метапрограммирования. Я не хочу писать дополнительный код в классах контейнеров. – kajamite

Смежные вопросы