如何从模块访问类变量?

时间:2015-05-04 03:41:50

标签: ruby

我想知道如何从模块中访问类变量

module Entity
  def foo
    # puts @@rules
  end
end

class Person
  include Entity

  attr_accessor :id, :name

  @@rules = [[:id, :int, :not_null],
             [:name, :string, :not_null]]
end

class Car
  include Entity

  attr_accessor :id, :year

  @@rules = [[:id, :string, :not_null],
             [:year:, :int, :not_null]]
end

p = Person.new
c = Car.new

p.foo # [[:id, :int, :not_null], [:name, :string, :not_null]]
c.foo # [[:id, :string, :not_null], [:year, :int, :not_null]]

我查看了cattr_accessor中的mattr_accessorActiveSupport,但仍无法找到解决此问题的方法。

4 个答案:

答案 0 :(得分:12)

Ruby中的类变量在继承方面很奇怪。除非你确切地知道你在那里搞砸了什么,否则最好避免它们。在这种情况下,您可能认为自己没有使用继承,但include实际上做的是将Entity插入Person的祖先。参见:

Person.ancestors
# [Person, Entity, Object, Kernel, BasicObject]

特定行为难以描述,但简短版本基本上@@rulesEntity {{1}之间共享Person }!看:

Car

你可能不想要那个!

最好在这里使用类实例变量,实际上每个类都是独立的。

Entity.class_variable_set(:@@rules, 'foo')
puts Car.class_variable_get(:@@rules)
# foo
puts Person.class_variable_get(:@@rules)
# foo

答案 1 :(得分:1)

这不是最优雅的解决方案,但class_eval有效:

module Entity
  def foo
    self.class.class_eval('@@rules')
  end
end

编辑:实际上稍微清洁可能是使用class_variable_get

module Entity
  def foo
    self.class.class_variable_get(:@@rules)
  end
end

答案 2 :(得分:0)

除了已经给出的答案之外,这里有一些我在某个时候发现的东西。

module MyModule
  @@my_variable = 5
  define_singleton_method(:my_variable) do
    @@my_variable
  end
end

现在,您可以通过两种方式访问​​类变量: MyModule::my_variableMyModule.my_variable

这现在就像attr_reader一样。您可以定义第二个单例分配方法。

答案 3 :(得分:0)

这不是我的答案,它是@ Max答案的变体,只是没有暴露@rules变量(参见@Quarktum的评论)。 / p>

这里的不同之处在于我使用#module_exec method,它允许实例变量访问(与#module_eval不同)。

另外,我在include类的范围内定义了.foo#foo方法,因此方法是类方法而不是模块的方法(使用Car.methods false进行测试,以查看没有继承的Car方法。

module Entity 
  # create the class instance variable methods when this is included
  def self.included klass
    klass.module_exec do
      @rules ||= []
      def self.foo
        puts @rules
      end
      def foo
        self.class.foo
      end
    end
  end
end

class Person
  include Entity

  attr_accessor :id, :name

  @rules = [[:id, :int, :not_null],
                [:name, :string, :not_null]]
end

class Car
  include Entity

  attr_accessor :id, :year

  @rules = [[:id, :string, :not_null],
                [:year, :int, :not_null]]
end