2016-08-31 119 views
1

假設我想在Ruby中創建一個宏。Ruby可繼承的類宏

class Base 
    def self.option(name,val) 
    options[name] = val 
    end 

    def self.options 
    @options ||= {} 
    end 
end 

class Foo < Base 
    option :one, 1 
    option :two, 2 
end 

Foo.options #=> {:one => 1, :two => 2} 

好的,很簡單。

但是如果我想繼承Foo呢?

class Bar < Foo 
end 

Bar.options #=> {} 

這很糟糕。

所以很明顯,問題是類實例變量是每個類唯一的,即。 @options裏面的Bar@options裏面的Foo不一樣。

所以也許一個類變量?我從來沒有能夠找出其中一個有效的用途,讓我們試試看。

# the rest of the code unchanged 
class Base 
    def self.options 
    @@options ||= {} 
    end 
end 

Bar.options #=> {:one => 1, :two => 2} 

嘿,工作! ...不是嗎?

class Baz < Foo 
    option :three, 3 
end 

Foo.options #=> {:one => 1, :two => 2, :three => 3} 
Bar.options #=> {:one => 1, :two => 2, :three => 3} 
Baz.options #=> {:one => 1, :two => 2, :three => 3} 

X- |

好吧,我一直在搜索這個,我沒有看到任何有用的東西。我嘗試了一些嘗試讀取超類選項(如果已定義)的變體,但無處可查。我想我可能也會問。

你們誰都知道如何做到這一點?還是僅僅是不可能的......

回答

1

我相信這是你所需要的:

class Base 
    def self.option(name, val) 
    options[name] = val 
    end 

    def self.options 
    @options ||= if self.superclass.respond_to?(:options) 
        self.superclass.options.dup 
       else 
        {} 
       end 
    end 
end 

class Foo < Base 
    option :one, 1 
    option :two, 2 
end 

class Bar < Foo 
    option :three, 3 
end 

class Hello < Bar 
    option :world, 4 
end 

puts Foo.options # {:one=>1, :two=>2} 
puts Bar.options # {:one=>1, :two=>2, :three=>3} 
puts Hello.options #{:one=>1, :two=>2, :three=>3, :world=>4} 
+0

在這裏相當接近,但'.dup'調用意味着你不知道超類在事後做出的任何改變。 – tadman

+0

問題沒有提到關於觀察原始宏的內容。 –

+0

是的,在這種情況下dup是好的。這是一個很好的清潔解決方案。我嘗試了類似的東西,但沒有成功,但老實說,我不知道爲什麼。我猜想必須輸入錯誤的內容。謝謝你在任何情況下爲我整理它:) – Andrew

0
class Base 
    def self.option(name,val) 
    options[name] = val 
    end 

    def self.options 
    @options ||= {} 
    end 
end 

class Foo < Base  
    option :one, 1 
    option :two, 2 
end 

class Bar < Foo 
    @options = superclass.options.clone 
    option :three, 3 
end 

class Baz < Foo 
    @options = superclass.options.clone 
    option :four, 4 
end 

puts Foo.options #=> {:one => 1, :two => 2} 
puts Bar.options #=> {:one => 1, :two => 2, :three => 3} 
puts Baz.options #=> {:one => 1, :two => 2, :four => 4} 

這是我能想到的,將工作的唯一的事情,你只需克隆@options從超。這樣,每個類都會有自己的實例變量。

+0

我嘗試了幾種不同的方式,無法使用它......你測試過了嗎? – Andrew

+0

是的,我做到了。你可以看到它[這裏](https://repl.it/DEp1) – davidhu2000

+0

好吧,我的測試出現了錯誤,我猜是因爲我看到了你的repl作品。謝謝:) – Andrew

1

如果你想要一個子類已經從它衍生出來後,這是通用的,足以適應在基類中所做的更改:

class Base 
    def self.option(name,val) 
    @options ||= { } 

    @options[name] = val 
    end 

    def self.options 
    @options ||= { } 

    if (self == Base) 
     @options 
    else 
     @options.merge(self.superclass.options) 
    end 
    end 
end 

class Foo < Base 
    option :one, 1 
    option :two, 2 
end 

class SubFoo < Foo 
    option :three, 3 
end 

Foo.options 
#=> {:one => 1, :two => 2} 
SubFoo.options 
#=> {:three=>3, :one=>1, :two=>2} 

class Foo 
    option :four, 4 
end 

Foo.options 
#=> {:one=>1, :two=>2, :four=>4} 
SubFoo.options 
#=> {:three=>3, :one=>1, :two=>2, :four=>4}