2017-02-04 51 views
1

例如我具有以下的自定義類和模塊:紅寶石元編程:無法發送的方法的模塊

module SimpleModule 
    def hello_world 
    puts 'i am a SimpleModule method' 
    end 

    def self.class_hello_world 
    puts 'i am a SimpleModule class method' 
    end 
end 

class SimpleClass 
    def hello_world 
    puts 'i am SimpleClass method' 
    end 

    def self.class_hello_world 
    puts 'i am a SimpleClass class method' 
    end 
end 

我試圖通過使用方法send

SimpleClass.send(class_hello_world) # work 
SimpleClass.new.send(hello_world) # work 
SimpleModule.send(class_hello_world) # work 
SimpleModule.new.send(hello_world) # not work 
SimpleModule.send(hello_world)  # not work 
到稱爲內類和模塊的那些方法

換句話說,我不知道如何從SimpleModule調用hello_world。如果這種方法是用自己定義的,那麼這是可能的。

我需要這樣做,因爲我想實現一個「自定義包含」:包括從模塊到另一個類的所有方法。

請告訴我如何做到這一點。

+0

'SimpleModule.new.send(hello_world)'不起作用,因爲您不能擁有模塊的實例。 –

+0

@EddeAlmeida謝謝。所以Ruby通過其語言實現關鍵字「包含」,或者我可以使用元編程來做類似的事情?如果元編程可能的話,應該有一個來自模塊的調用方法。 –

回答

2

模塊不能有實例方法,因爲它們不是類。如果你在一個模塊中定義了一個實例方法,它就被稱爲一個mixin。這些「mixins」可以包含在另一個類中,然後可供使用。

完整的解釋是這裏的docs

編輯:

例如,你可以做這樣的事情:

module SimpleModule 
    def hello_world 
    p 'hello world' 
    end 
end 

class Example 
    include SimpleModule 
end 

Example.new.send(:hello_world)

這是調用一個方法混合模塊。

+0

謝謝。我知道「包含」關鍵字。我可以自己實施「包含」嗎?沒有真實世界的應用程序,只是我想展示概念,但我堅持從模塊調用方法。謝謝 –

+0

我添加了一個如何將模塊包含在類中的示例,然後調用該類上的send來調用模塊方法。 – trueinViso

5

五個陳述

讓我們看看這五個語句一次一個(但以不同的順序不是提交)。請注意,send的參數必須是以字符串或符號表示的方法的名稱。

SimpleModule.send("class_hello_world") 
    # i am a SimpleModule class method 

這是正常的,雖然這樣的方法通常稱爲模塊方法。一些常見的內置模塊(如Math)僅包含模塊方法。

SimpleClass.send(:class_hello_world) 
    # i am a SimpleClass class method 

由於類是一個模塊,所以其行爲與上述相同。 class_hello_world通常被稱爲類方法

SimpleClass.new.send(:hello_world) 
    # i am SimpleClass method 

這是一個實例方法的正常調用。

SimpleModule.send("hello_world") 
    #=> NoMethodError: undefined method `hello_world' for SimpleModule:Module 

沒有模塊方法hello_world

SimpleModule.new.send(hello_world) 
    #=> NoMethodError: undefined method `new' for SimpleModule:Module 

無法創建模塊的實例。

include VS prepend

假設一書中寫道

SimpleClass.include SimpleModule 
    #=> SimpleClass 
SimpleClass.new.hello_world 
    # i am SimpleClass method 

所以SimpleClass「原來的方法hello_world沒有同名由模塊的方法覆蓋。考慮SimpleClass的祖先。

SimpleClass.ancestors 
    #=> [SimpleClass, SimpleModule, Object, Kernel, BasicObject] 

紅寶石會尋找hello_worldSimpleClass --and找到它 - 考慮SimpleModule之前。

但是,可以使用Module#prependSimpleModule#hello_world放在SimpleClass#hello_world之前。

SimpleClass.prepend SimpleModule 
    #=> SimpleClass 
SimpleClass.new.hello_world 
    # i am a SimpleModule method 
SimpleClass.ancestors 
    #=> [SimpleModule, SimpleClass, Object, Kernel, BasicObject] 

綁定綁定方法

有你做一兩件事。 SimpleModule的實例方法(這裏只有一個)沒有綁定。您可以使用UnboundMethod#bind將每個綁定到SimpleClass的實例,然後使用callsend執行。

sc = SimpleClass.new 
    #=> #<SimpleClass:0x007fcbc2046010> 
um = SimpleModule.instance_method(:hello_world) 
    #=> #<UnboundMethod: SimpleModule#hello_world> 
bm = um.bind(sc) 
    #=> #<Method: SimpleModule#hello_world> 
bm.call 
    #=> i am a SimpleModule method 
sc.send(:hello_world) 
    #=> i am a SimpleModule method 
+0

謝謝。我明白我現在遇到的問題是因爲我無法初始化模塊。我嘗試再次實現Ruby關鍵字**包含**的概念證明。所以如果我不能像這樣在模塊上調用方法,我可以通過某種方式嗎? –

+0

我已經做了一個編輯來解決你的問題。 –

+0

感謝** prepend **關鍵字。我以前不知道這一點:D但無論如何,他們仍然很少像**包括**在語言爲基礎的關鍵字方面。 (所以我不能自己實現):D –

2

因爲您似乎想創建自己的include版本。

看看Module.append_features文檔

當該模塊包含在另一個,紅寶石 調用append_features這個模塊,通過它接收模塊的MOD。 Ruby的默認 實現方式是將此模塊的常量,方法和模塊變量 添加到mod,如果此模塊尚未添加到mod 或其祖先之一。另請參閱模塊#include。

所以這就是你要做的,如果你想自己重寫include

既然你冒險,也許你會喜歡閱讀Ruby的源代碼,看看這是如何實現內部。請參見https://github.com/ruby/ruby...class.c#L853:L934

+0

感謝您鏈接到源代碼。這很棒。關於追加,我看到我看起來像「包含」,只是在行爲上有點不同而已。所以我不認爲使用append是一種重寫包括:D –

+1

關於鏈接到源代碼的方法。你怎麼能找到這個?你是Ruby的貢獻者嗎?我只是有點好奇。所以我可以知道我將來如何做同樣的事情。謝謝 –

+0

我使用'pry'和'pry-docs' gem來定位源代碼,'$'命令顯示源代碼。然後在https://github.com/ruby/ruby使用搜索進一步挖掘 – akuhn