2016-03-15 3 views
2

Я нашел этот аккуратный доверителя на основе «тройник» реализации на SO:переменной экземпляра в методе класса

https://stackoverflow.com/a/6410202/2379703

И мне очень интересно, что это значит для @targets (переменная экземпляра) означает в контексте метода класса:

require 'logger' 

class MultiDelegator 
    def initialize(*targets) 
    @targets = targets 
    end 

    def self.delegate(*methods) 
    methods.each do |m| 
     define_method(m) do |*args| 
     @targets.map { |t| t.send(m, *args) } 
     end 
    end 
    self 
    end 

    class <<self 
    alias to new 
    end 
end 

log_file = File.open("debug.log", "a") 
log = Logger.new MultiDelegator.delegate(:write, :close).to(STDOUT, log_file) 

Я понимаю, что это определение методов записи/закрытия, но @targets даже не определена в этой точке, так как .TO (псевдоним нового) до сих пор не называется, так что я бы Предположим, что @targets равно нулю.

Можно ли дать объяснение относительно логистики того, как работает этот код? Неужели рубин даже не пытается получить доступ/разрешить @targets до тех пор, пока не будет вызван тот метод, который будет вызван регистратором после его создания?

ответ

1

Метод define_method вызывается классу для создания метода экземпляра. Внутри этого метода self (и переменная экземпляра) являются экземплярами класса.

Например:

class Foo 
    @bar = "CLASS" 
    def initialize 
    @bar = "INSTANCE" 
    end 
    def self.make_method 
    define_method :whee do 
     p @bar 
    end 
    end 
end 

begin 
    Foo.new.whee 
rescue NoMethodError=>e 
    puts e 
end 
    #=> undefined method `whee' for #<Foo:0x007fc0719794b8 @bar="INSTANCE"> 

Foo.make_method 
Foo.new.whee 
#=> "INSTANCE" 

Это правильно, что вы можете спросить о переменных экземпляра, которые никогда не были созданы, в любое время:

class Bar 
    def who_dat 
    puts "@dat is #{@dat.inspect}" 
    end 
end 

Bar.new.who_dat 
#=> dat is nil 

То же самое можно сказать и о других аспектах язык. До тех пор, как код в методе является синтаксически правильным, она может быть определена, даже если применение это приводит к ошибке во время выполнения:

class Jim 
    def say_stuff 
    stuff! 
    end 
end 
puts "Good so far!" 
#=> Good so far! 

j = Jim.new 
begin 
    j.say_stuff 
rescue Exception=>e 
    puts e 
end 
#=> undefined method `stuff!' for #<Jim:0x007f9c498852d8> 

# Let's add the method now, by re-opening the class 
class Jim # this is not a new class 
    def stuff! 
    puts "Hello, World!" 
    end 
end 

j.say_stuff 
#=> "Hello, World!" 

В приведенном выше я определяю say_stuff метод, который синтаксически действует, но вызывает метод, который не существует. Это находка. Метод создается, но не вызывается.

Затем я пытаюсь вызвать метод, и это вызывает ошибку (которую мы ловим и обрабатываем чисто).

Затем я добавляю метод stuff! к классу. Теперь я могу запустить метод say_stuff (в том же экземпляре, что и раньше!), И он работает отлично.

Этот последний пример показывает, как определение метода не запускает его, или требует, чтобы он работал даже при его запуске. Он динамически оценивается каждый раз, когда он вызывается (и только в это время).

+0

Ahh define_method определяет метод экземпляра, несмотря на то, что он вызван в методе класса. Я читаю немного больше, и кажется, что instance_eval используется под обложками, поэтому я вижу, как @targets - это переменная экземпляра вызывающего экземпляра. – jshort

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