2016-05-17 128 views
1

因此,尽管我完全反对扩大现有类这样,有时(黑客rspec)有必要做这样的事情类变量:可以访问一个模块中定义

module MyModule 
    module ClassMethods 
    def define_something(name) 
     @@names ||= [] 
     @@names << name 
    end 
    end 
    def self.included(base) 
    base.extend ClassMethods 
    end 
    def all_names 
    @@names 
    end 
end 
class Example 
    include MyModule 
    define_something "one" 
    define_something "two" 
end 
Example.new.all_names 

,然后它产生这个错误:

NameError: uninitialized class variable @@names in MyModule 

而且据我所知,因为在写MyModule::ClassMethods的时间 - 我们在示例中未类(不self.)工作,所以我尝试:

module MyModule 
    module ClassMethods 
    def define_something(name) 
     @names ||= [] 
     @names << name 
    end 
    end 
    def self.included(base) 
    base.extend ClassMethods 
    end 
    def all_names 
    @@names 
    end 
end 
class Example 
    include MyModule 
    define_something "one" 
    define_something "two" 
end 
Example.new.all_names 

它不工作,要么,我终于结束了:

module MyModule 
    module ClassMethods 
    def define_something(name) 
     @names << name 
    end 
    end 
    def self.included(base) 
    base.instance_variable_set(:@names, []) 
    base.send(:define_method, :all_names) { base.instance_variable_get(:@names) } 
    base.extend ClassMethods 
    end 
end 
class Example 
    include MyModule 
    define_something "one" 
    define_something "two" 
end 
Example.new.all_names 

有没有更好的方式来做到这一点?

+0

'@@'和'@'是完全不同的。无论您处于类还是实例上下文中,都不能使用@ @访问“@@”变量,反之亦然。 – meagar

回答

4

从设计的角度来看,避免使用实例变量引用穿过类/实例行通常会更好。这通常更加清晰:

module MyModule 
    module ClassMethods 
    def define_something(name) 
     self.defined_somethings << name 
    end 

    def defined_somethings 
     @_my_module_names ||= [] 
    end 
    end 

    def self.included(base) 
    base.extend ClassMethods 
    end 

    def all_names 
    self.class.defined_somethings 
    end 
end 

class Example 
    include MyModule 
    define_something "one" 
    define_something "two" 
end 

Example.new.all_names.inspect 
#=> ["one","two"] 

我在这里已经注意到创建一个带有详细名称的类级实例变量。调用它@name可能会使其与由包含此模块的类定义的变量发生冲突。请记住,在设计mixin代码时,你是客人,你需要格外礼貌。

@@name这样的类型实例变量有时会遇到麻烦,因为它们最终可能会跨越继承链,这取决于它们的使用方式。定义一个方法意味着你可以在链中的任何位置覆盖它,这对于一个共享变量来说是不可能的。

换句话说,将实例的类作为一个单独的对象来处理,并明确定义明确的方法调用以维持该分离。

1

尝试使用以下

module MyModule 
    module ClassMethods 
    def define_something(name) 
     @names ||= [] 
     @names << name 
    end 
    end 
    def self.included(base) 
    base.extend ClassMethods 
    end 
    def all_names 
    self.class.instance_variable_get(:@names) 
    end 
end 
class Example 
    include MyModule 
    define_something "one" 
    define_something "two" 
end 

Example.new.all_names 

类变量可使用操作者@@从类水平的方法即self.方法进行访问。

@ tadman的回答有类似问题的一般方法(以提供的模块来实现的功能通常宝石发现)

相关问题