Ruby元编程:如何使模块方法看到类的变量

Trầ*_* Dự 4 ruby metaprogramming

例如,我有一个模块和一个类:

module SimpleModule
  def self.class_hello
    puts "hello from #{@@name}"
  end
end

class SimpleClass
  @@name = 'StackOverFlow'
  def self.test
    SimpleModule.class_hello
  end
end
Run Code Online (Sandbox Code Playgroud)

然后我通过从类调用模块方法进行测试:

SimpleClass.test
Run Code Online (Sandbox Code Playgroud)

我遇到例外:

SimpleModule中未初始化的类变量@@ name(NameError)

我在这里知道是因为模块的作用域与类作用域不同。所以我的问题是:如何为SimpleModule范围共享SimpleClass范围?

我之所以使用元编程,是因为这只是一个简单的示例,此后,我将通过从动态类调用动态模块来进行高级处理。(这就是为什么我不想使用诸如includeextend之类的关键字的原因)

@Edit 实际上,我想自己实现Ruby扩展。这是我已经开发的版本:

# implementation
class Class
  def custom_extend(module_name)
    module_name.methods(false).each do |method|
      define_singleton_method(method) do |*args, &block|
        module_name.send(method, *args, &block)
      end
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

这是我的自定义模块和测试类:

# -------------------------------------------------------------
# Demonstration
module SimpleModule
  def self.class_hello_world
    puts 'i am a simple module boss'
  end

  def self.class_hello_name
    puts "hello from #{@@name}"
  end
end

class SimpleClass
  custom_extend  SimpleModule
  @@name = 'StackOverFlow'
end
Run Code Online (Sandbox Code Playgroud)

这是我的两个测试:

SimpleClass.class_hello_world  # work
SimpleClass.class_hello_name   # not work
Run Code Online (Sandbox Code Playgroud)

Eri*_*nil 5

更新的答案

这是您的代码的略微修改版本。不includeextendappend_features或者module_function是必要的。添加custom_include相同的结构并不难。

更新:确保阅读@ 7stud的答案,其结构类似,解释也很好。

class Class
  def custom_extend(module_name)
    module_name.instance_methods(false).each do |method|
      define_singleton_method(method) do |*args, &block|
        module_name.instance_method(method).bind(self).call(*args, &block)
      end
    end
  end
end

module SimpleModule
  def class_hello
    puts "hello from #{@name}"
  end
end

class SimpleClass
  @name = 'class'
  custom_extend SimpleModule

  def self.test
    class_hello
  end
end

SimpleClass.test
#=> hello from class
Run Code Online (Sandbox Code Playgroud)

原始答案

平常的方式

通常的方式是:

module SimpleModule
  def class_hello
    puts "hello from #{@name}"
  end
end

class SimpleClass
  @name = 'StackOverFlow'
  extend SimpleModule

  def self.test
    class_hello
  end
end

SimpleClass.class_hello
Run Code Online (Sandbox Code Playgroud)

但是你不想要 (为什么?)

你的方式

在您的代码中,SimpleClass并且SimpleModule彼此完全独立。很明显,您得到了NameError。您需要以某种方式传递name信息。

作为名称参数:

module SimpleModule
  def self.class_hello(name='')
    puts "hello from #{name}"
  end
end

class SimpleClass
  @@name = 'StackOverFlow'
  def self.test
    SimpleModule.class_hello(@@name)
  end
end
Run Code Online (Sandbox Code Playgroud)

使用klass参数:

module SimpleModule
  def self.class_hello(calling_class=self)
    calling_class.class_eval{
      puts "hello from #{@name}"
    }
  end
end

class SimpleClass
  @name = 'StackOverFlow'
  def self.test
    SimpleModule.class_hello(self)
  end
end

SimpleClass.test
Run Code Online (Sandbox Code Playgroud)

binding参数:

module SimpleModule
  def self.class_hello(b)
    puts "hello from #{b.eval('@@name')}"
  end
end

class SimpleClass
  @@name = 'StackOverFlow'
  def self.test
    SimpleModule.class_hello(binding)
  end
end

SimpleClass.test
Run Code Online (Sandbox Code Playgroud)

my_ruby_extend SimpleModule

当然可以通过自定义完成my_ruby_extend。不过,您需要显示所需的语法以及已经实现的内容。

这样,您可以告诉Ruby SimpleClass并且SimpleModule已链接。如果找不到方法或变量SimpleModule,则可以在其中找到SimpleClass