Ruby可继承的类宏

时间:2016-08-31 02:37:50

标签: ruby inheritance macros

假设我想在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- |

好的,我一直在谷歌搜索这个,我没有看到任何有用的东西。我尝试了一些尝试阅读超类选项(如果已定义)但却无处可寻。我想我也可以问。

你们任何人都知道怎么做?或者它是不可能的......

3 个答案:

答案 0 :(得分: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}

答案 1 :(得分: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}

答案 2 :(得分: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。这样,每个类都有自己独立的实例变量。