2017-08-30 39 views
3

我做了一些(太)花哨的元編程,我也很難理解爲什麼範圍是在以下兩種情況下的不同:爲什麼我的define_method內容在類作用域中被評估?

案例1:

class TesterA 

    def the_method 
    puts "I'm an instance_method!" 
    end 

    def self.the_method 
    puts "I'm a class_method!" 
    end 

    def self.define_my_methods *method_names 
    method_names.each do |name| 
     define_method("method_#{name}") do 
     the_method 
     end 
    end 
    end 

    define_my_methods :a, :b, :c 
end 

t = TesterA.new 
t.method_a #=> I'm an instance_method! 
t.method_b #=> I'm an instance_method! 
t.method_C#=> I'm an instance_method! 

案例2

class TesterB 

    def the_method 
    puts "I'm an instance_method!" 
    end 

    def self.the_method 
    puts "I'm a class_method!" 
    end 

    def self.define_the_method attr 
    define_method("method_#{attr}") do 
     begin 
     yield 
     rescue 
     raise $!, "method_#{attr} was called: #$!", [email protected] 
     end 
    end 
    end 

    def self.define_my_methods *method_names 
    method_names.each do |name| 
     define_the_method(name) do 
     the_method 
     end 
    end 
    end 

    define_my_methods :a, :b, :c 
end 

t = TesterB.new 
t.method_a #=> I'm a class_method! 
t.method_b #=> I'm a class_method! 
t.method_C#=> I'm a class_method! 

I I介紹了一種「輔助-評判」 define_the_method我使用用於限定米第二示例方法而不是define_method它自己。原因是,我想將動態方法的名稱附加到可能在這些方法內發生的任何異常消息。然而,問題在於,內容(使用後一種情況時)似乎是在類範圍內進行評估的。

這是爲什麼,我該如何讓它在實例範圍內得到評估?

回答

2

這是因爲procdefine_method定義將被使用instance_eval調用。

在第一個樣品,它是:

do 
    the_method 
end 

在第二:

do 
    begin 
    yield 
    rescue 
    raise $!, "method_#{attr} was called: #$!", [email protected] 
    end 
end 

但是yield將具有父範圍從它被定義的地方。

這裏是我的建議:

def self.define_the_method attr, &block 
    define_method("method_#{attr}") do 
    begin 
     instance_eval(&block) 
    rescue 
     raise $!, "method_#{attr} was called: #$!", [email protected] 
    end 
    end 
end 
+0

我喜歡這個!然而,在我的真實世界用例中,我需要能夠將方法傳遞給方法,所以我最終只使用'instance_exec'來代替。謝謝你的提示! –

4

此塊:

do 
    the_method 
end 

它定義並在類作用域作用域。沒有理由期望它會以某種方式注入實例範圍。要解決這個問題,只是明確地傳遞接收器:

yield(self) 

和:

def self.define_my_methods *method_names 
    method_names.each do |name| 
    define_the_method(name) do |receiver| 
     receiver.the_method 
    end 
    end 
end 
+0

Buuuuut,它在第一個片段是相同的。沒有? (範圍) –

+0

@SergioTulentsev nope,'define_method'管理範圍。 – mudasobwa

+0

這兩個片段中存在(並從類方法調用) –

4

這是因爲你在類方法self.define_my_methods,其中self是一類的範圍內提供的塊,不一個實例。所以你可以做的是yielddefine_method本身的範圍:

def self.define_the_method attr 
    define_method("method_#{attr}") do 
    begin 
     yield self 
    rescue 
     raise $!, "method_#{attr} was called: #$!", [email protected] 
    end 
    end 
end 

def self.define_my_methods *method_names 
    method_names.each do |name| 
    define_the_method(name) do |scope| 
     scope.send(:the_method) 
    end 
    end 
end 
+0

「你在類方法的範圍內產生了」-nope,它是在實例範圍內產生的,因爲'define_method'在引擎蓋下使用'instance_eval'。該塊是類範圍的,這就是爲什麼。 – mudasobwa

+0

@mudasobwa:我很抱歉,這就是我的意思,我一直沒有回答,表情也有點生疏。 – potashin

+0

感謝你的回答,我用'instance_eval'來代替,因爲如果我需要顯式調用範圍中的所有方法,我必須更改很多代碼。 –

相關問題