例如,我有一个模块和一个类:
module SimpleModule
def self.class_hello
puts "hello from #{@@name}"
end
end
class SimpleClass
@@name = 'StackOverFlow'
def self.test
SimpleModule.class_hello
end
end
然后我通过从类调用模块方法进行测试:
SimpleClass.test
我遇到异常:
SimpleModule(NameError)中未初始化的类变量@@ name
我在这里知道,因为模块的范围与类范围不同。所以我的问题是:如何为SimpleModule范围共享SimpleClass范围?
我放了元编程,因为这里只是一个简单的例子,之后我将通过动态类调用动态模块来提升。 (这就是我不想使用某些关键字的原因,例如包含或扩展)
@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
这是我的自定义模块和测试类:
# -------------------------------------------------------------
# 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
这是我的两个测试:
SimpleClass.class_hello_world # work
SimpleClass.class_hello_name # not work
答案 0 :(得分:4)
以下是您的代码的略微修改版本。不需要include
,extend
,append_features
或module_function
。添加具有相同结构的custom_include
并不困难。
更新:请务必阅读@ 7stud的answer,其结构和解释非常好。
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
通常的方式是:
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
但你不想要它。 (为什么?)
在您的代码中,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
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
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
my_ruby_extend SimpleModule
肯定可以使用自定义my_ruby_extend
来完成。您需要显示所需的语法,以及您已经实现的内容。
这样,您可以告诉Ruby SimpleClass
和SimpleModule
已关联。如果在SimpleModule
中找不到方法或变量,可以在SimpleClass
中找到它。
答案 1 :(得分:4)
我只是尝试重新实现extend作为练习。
ruby的extend()
不能像这样工作:
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
例如,以下内容不起作用:
module Dog
def self.greet
puts "hello"
end
end
class Cat
extend Dog
end
Cat.greet
--output:--
`<main>': undefined method `greet' for Cat:Class (NoMethodError)
extend()
的工作原理如下:
module Dog
def greet
puts "hello"
end
end
class Cat
extend Dog
end
Cat.greet
--output:--
hello
换句话说,extend()
将模块实例方法 - 而不是模块方法(例如,前面带有self
的方法名称) - 插入到Cat的单例类中(这是Cat的类方法所在的位置) 。在ruby中,include()
和extend()
与模块方法无关(同样,方法名称前面有self
)。模块在ruby中有两种用途:
def self.method_name
def some_method
include()和extend()处理#2。
以下解决方案不适用于@@variables
,但试图找出@@variables
在红宝石中展示的所有扭曲和转弯并不值得付出努力 - 只是不要使用它们。使用类实例变量,即在任何def之外指定的@variables
:
def my_extend(some_module)
singleton_class.include some_module
end
module Dog
def greet
puts @greeting
end
private
def sayhi
puts "hi"
end
end
class Cat
@greeting = "hello"
my_extend Dog
end
Cat.greet
#Cat.sayhi #=>`<main>': private method `sayhi' called for Cat:Class (NoMethodError)
Cat.class_eval {sayhi} #Change self to the Cat class so the implicit
#self variable that calls sayhi is equal to Cat
--output:--
hello
hi
现在,您只需要实施my_include
并将其替换为include
。 :)
以下是my_include()
的一个镜头:
class Class
def my_include(module_)
#For public and protected methods:
module_.instance_methods(include_super=false).each do |meth_name|
meth = module_.instance_method(meth_name)
define_method(meth_name) do
meth.bind(self).call
end
end
#For private methods:
module_.private_instance_methods(include_super=false).each do |meth_name|
meth = module_.instance_method(meth_name)
define_method(meth_name) do
meth.bind(self).call
end
private :"#{meth_name}"
end
end
end
module Dog
def greet
puts "hello"
end
def go
puts "run, run run"
end
private
def sayhi
puts "hi"
end
end
class Cat
my_include Dog
end
c = Cat.new
c.greet
c.go
c.sayhi
--output:--
hello
run, run run
#=>`<main>': private method `sayhi' called for #<Cat:0x007fc014136f60> (NoMethodError)
使用my_extend()
:
class Class
def my_include(module_)
#For public and protected methods:
module_.instance_methods(include_super=false).each do |meth_name|
meth = module_.instance_method(meth_name)
define_method(meth_name) do
meth.bind(self).call
end
end
#For private methods:
module_.private_instance_methods(include_super=false).each do |meth_name|
meth = module_.instance_method(meth_name)
define_method(meth_name) do
meth.bind(self).call
end
private :"#{meth_name}"
end
end
def my_extend(module_)
singleton_class.my_include module_
end
end
module Dog
def greet
puts @greeting
end
private
def sayhi
puts "hi"
end
end
class Cat
@greeting = "hello"
my_extend Dog
end
Cat.greet
#Cat.sayhi #=>private method `sayhi' called for Cat:Class (NoMethodError)
Cat.class_eval {sayhi}
--output:--
hello
hi
答案 2 :(得分:0)
简化问题
您可以通过修改代码来简单地解决您的问题。
module SimpleModule
def self.class_hello
puts "hello from #{@@name}"
end
end
class SimpleClass
@@name = 'StackOverFlow'
end
SimpleModule.class_hello
# NameError: uninitialized class variable @@name in SimpleModule
哪个班级?
显然,模块的模块方法class_hello
必须被告知需要类变量&&name
的类。因此,我们必须将该课程作为class_hello
的论据。然后我们使用[Module#class_variable_get](https://ruby-doc.org/core-2.2.1/Module.html#method-i-class_variable_get
)提取类变量的值。
module SimpleModule
def self.class_hello(klass)
puts "hello from #{ klass.class_variable_get(:@@name) }"
end
end
SimpleModule.class_hello(SimpleClass)
# hello from StackOverFlow