在定义类方法时绕过Ruby的范围门

时间:2014-05-16 17:33:15

标签: ruby

我有一个类A,我想匿名扩展并向子类添加一个类方法。 E.g:

class A
end

Class.new A do
  def self.new_class_method
    puts 'I am a class method'
  end
end.new_class_method

=> I am a class method

上述示例效果很好,除非您想要访问def self.new_class_method块之外的某些变量。 E,G,

greeting = 'hello'

Class.new A do
  def self.new_class_method
    puts greeting + ' I am a class method'
  end
end.new_class_method

=> NameError: undefined local variable or method `greeting'

我正在使用Ruby 1.8.7,这很难过,因为我相信Ruby 1.9+包含一个类似define_method的模拟,它增加了一个类方法。有没有人有1.8.7的解决方法?

4 个答案:

答案 0 :(得分:4)

我在 Ruby 1.8.7 中测试了以下内容: -

greeting = 'hello'

class A
end

Class.new A do 
  meta_klass = class << self; self ;end
  meta_klass.send(:define_method, :new_class_method) do
    puts greeting + ' I am a class method'
  end
end.new_class_method
# >> hello I am a class method

由于Ruby 1.8.7不支持Object#singleton_class,我使用了meta_klass = class << self; self ;end。我认为这个方法可用于 1.9.2

答案 1 :(得分:2)

您也可以使用extend()来打开对象的单例类。调用extend(模块)将模块中的方法添加到调用对象(即接收者)的单例类中。因此,如果你在self = A时调用extend(module),即在类A中调用,那么模块的方法将被插入到A的单例类中,而A的单例类中的方法也被称为A的类方法:

class A
end

greeting = "hello"

Class.new(A) do 

  extend(
    Module.new do
      define_method(:greet) do
        puts greeting
      end
    end
  )

end.greet

--output:--
hello

你可以像这样重写(虽然那时并不那么棘手):

class A
end

greeting = "hello"

Class.new(A) do 

  m =  Module.new do
    define_method(:greet) do
      puts greeting
    end
  end

  extend(m)

end.greet

......与...没什么不同。

class A
end

greeting = "hello"

m =  Module.new do
  define_method(:greet) do
    puts greeting
  end
end

Class.new(A) do 
  extend(m)
end.greet

...它将闭包移出了类,并且看起来并不是很棘手,因为它只打开了两个范围门而不是三个。

另请注意,extend()是一个公共方法,所以它不需要私有方法的技巧,即你不能指定一个显式接收器,所以你必须创建一个自我是要调用私有方法的对象。换句话说,您可以为extend()指定显式接收器。 Class.new(A)返回的类怎么样?

class A
end

greeting = "hello"

Class.new(A).extend(
  Module.new do
    define_method(:greet) do
      puts greeting
    end
  end
).greet


--output:--
hello
嘿,加上“.greet”就行了!哦,哦,有一个衬垫的材料:

class A
end

greeting = "hello"

Class.new(A).extend(Module.new {define_method(:greet) {puts greeting} }).greet

--output:--
hello

Yeech!

答案 2 :(得分:1)

更一般地说,

class A
end

class Object
  def meta_def name, &blk
    (class << self; self; end).instance_eval { define_method name, &blk }
  end
end

greeting = 'hello'

Class.new A do
  meta_def :new_class_method do
    puts greeting + ' I am a class method'
  end
end.new_class_method
  #=> hello I am a class method

如果您觉得这很有用,请不要感谢我,谢谢lucky stiff {我在Jay Fields看到的那些内容)。

答案 3 :(得分:0)

不确定这是否可以解决您的问题,但将Greeting更改为大写(使其成为常量)将有效......

class A
end

Greeting = 'hello'

Class.new A do
  def self.new_class_method
    puts Greeting + ' I am a class method'
  end
end.new_class_method