在运行时将 class 添加到 ruby 模块
Add class to ruby module in runtime
我需要在运行时将 class 添加到 class(或模块)中。例如:我有 模块 M,Class A.
我需要添加一个新的 Class B 到 Module M。
我试过:
M.module_eval do
class B
end
end
但是没用。
我知道(例如)如果我想添加一个方法到现有的class A,我愿意
class << A
def method
end
end
但是如何将 Class 添加到模块或 Class?
这个问题特别关注在运行时添加 class。 class 将在现有模块中创建,但是,正如将会看到的那样,这几乎是偶然的并且实用性值得怀疑。
在运行时向模块添加 class
为了在运行时协助在给定模块内构建 classes,我们可能会构建一个方法 class_factory
。
def class_factory(mod, class_name, consts, meths, instance_meths,
accessors)
class_obj = mod.const_set(class_name, Class.new)
consts.each { |const,val| class_obj.const_set(const,val) }
meths.each do |name,body|
class_obj.singleton_class.
instance_eval("define_method(:#{name}) #{body}")
end
instance_meths.each do |name,body|
class_obj.instance_eval("define_method(:#{name}) #{body}")
end
accessors.each do |accessor,inst_var|
class_obj.public_send(accessor, inst_var)
end
class_obj
end
我们来试试吧。
module M
end
class_obj = class_factory(
M,
"B",
{ 'A'=>7, 'D'=>'cat' },
{ greeting: '{ |n| "Cat\'s have #{n} lives" }' },
{ initialize: '{ |name| @name = name }',
say_name: '{ "My name is #{@name}" }' },
{ attr_accessor: "name" }
)
#=> M::B
class_obj == M::B
#=> true
M::B.constants
#=> [:A, :D]
class_obj.methods(false)
#=> [:greeting]
M::B.instance_methods(false)
#=> [:say_name, :name=, :name]
class_obj.greeting(9)
#=> "Cat's have 9 lives"
M::B.greeting(5)
#=> "Cat's have 5 lives"
instance = M::B.new "Lola" # or class_obj.new "Lola"
#=> #<M::B:0x000056cb6e766840 @name="Lola">
instance.say_name
#=> "My name is Lola"
instance.name
#=> "Lola"
instance.name = "Lo"
#=> "Lo"
instance.name
#=> "Lo"
您的代码可能包含诸如此类的静态表达式,唯一的动态部分是 class.
的构造
另一方面,class 也可以动态使用。例如:
mod = "M"
cl = "B"
name = "Lola"
meth = "say_name"
然后:
Object.const_get("#{mod}::#{cl}").new(name).public_send(meth)
#=> "My name is Lola"
或
class_obj.new(name).public_send(meth)
#=> "My name is Lola"
如何最好地引用动态创建的 classes
我们刚刚看到了引用动态创建的 class 的各种方法。根据要求,M::B
与 class_obj
和 Object.const_get("#{mod}::#{cl}")
与 class_obj
。显然,class_obj
的使用在这两种情况下都是最简单的,并且具有额外的优势,即如果将来 M
或 B
中的 class_obj
引用不需要更改 M::B
已更改。
动态创建作为模块成员的 class 是否有优势?
回想一下,在模块中创建 class 的主要原因是创建一个命名空间,因此,例如,M1::C
和 M2::C
不会产生名称冲突。但是,如果我们通过变量(此处为 class_obj
)持有的(唯一)对象(而不是其名称,常量)引用动态创建的 class,则不需要命名空间.所以我在这部分提出的问题的答案是 "no".
此外,如果我们通过其对象引用动态创建的 class,则没有理由为 class 分配名称。因此我们可以修改 class_factory
如下:
def class_factory(consts, meths, instance_meths, accessors)
Class.new do
consts.each { |const,val| const_set(const,val) }
meths.each do |name,body|
singleton_class.
instance_eval("define_method(:#{name}) #{body}")
end
instance_meths.each do |name,body|
instance_eval("define_method(:#{name}) #{body}")
end
accessors.each do |accessor,inst_var|
public_send(accessor, inst_var)
end
end
end
class_obj = class_factory(
{ 'A'=>7, 'D'=>'cat' },
{ greeting: '{ |n| "Cat\'s have #{n} lives" }' },
{ initialize: '{ |name| @name = name }',
say_name: '{ "My name is #{@name}" }' },
{ attr_accessor: "name" }
)
#=> #<Class:0x000056cb6eaeefd0>
class_obj
持有的对象被称为匿名class,因为它没有名字(这是一个常量)。
class_obj.constants
#=> [:A, :D]
class_obj.methods(false)
#=> [:greeting]
class_obj.instance_methods(false)
#=> [:say_name, :name=, :name]
instance = class_obj.new "Billy-Bob"
#=> #<#<Class:0x000056cb6eaeefd0>:
# 0x000056cb6eb183d0 @name="Billy-Bob">
instance.say_name
#=> "My name is Billy-Bob"
instance.name
#=> "Billy-Bob"
instance.name = "BB"
#=> "BB"
我需要在运行时将 class 添加到 class(或模块)中。例如:我有 模块 M,Class A. 我需要添加一个新的 Class B 到 Module M。 我试过:
M.module_eval do
class B
end
end
但是没用。
我知道(例如)如果我想添加一个方法到现有的class A,我愿意
class << A
def method
end
end
但是如何将 Class 添加到模块或 Class?
这个问题特别关注在运行时添加 class。 class 将在现有模块中创建,但是,正如将会看到的那样,这几乎是偶然的并且实用性值得怀疑。
在运行时向模块添加 class
为了在运行时协助在给定模块内构建 classes,我们可能会构建一个方法 class_factory
。
def class_factory(mod, class_name, consts, meths, instance_meths,
accessors)
class_obj = mod.const_set(class_name, Class.new)
consts.each { |const,val| class_obj.const_set(const,val) }
meths.each do |name,body|
class_obj.singleton_class.
instance_eval("define_method(:#{name}) #{body}")
end
instance_meths.each do |name,body|
class_obj.instance_eval("define_method(:#{name}) #{body}")
end
accessors.each do |accessor,inst_var|
class_obj.public_send(accessor, inst_var)
end
class_obj
end
我们来试试吧。
module M
end
class_obj = class_factory(
M,
"B",
{ 'A'=>7, 'D'=>'cat' },
{ greeting: '{ |n| "Cat\'s have #{n} lives" }' },
{ initialize: '{ |name| @name = name }',
say_name: '{ "My name is #{@name}" }' },
{ attr_accessor: "name" }
)
#=> M::B
class_obj == M::B
#=> true
M::B.constants
#=> [:A, :D]
class_obj.methods(false)
#=> [:greeting]
M::B.instance_methods(false)
#=> [:say_name, :name=, :name]
class_obj.greeting(9)
#=> "Cat's have 9 lives"
M::B.greeting(5)
#=> "Cat's have 5 lives"
instance = M::B.new "Lola" # or class_obj.new "Lola"
#=> #<M::B:0x000056cb6e766840 @name="Lola">
instance.say_name
#=> "My name is Lola"
instance.name
#=> "Lola"
instance.name = "Lo"
#=> "Lo"
instance.name
#=> "Lo"
您的代码可能包含诸如此类的静态表达式,唯一的动态部分是 class.
的构造另一方面,class 也可以动态使用。例如:
mod = "M"
cl = "B"
name = "Lola"
meth = "say_name"
然后:
Object.const_get("#{mod}::#{cl}").new(name).public_send(meth)
#=> "My name is Lola"
或
class_obj.new(name).public_send(meth)
#=> "My name is Lola"
如何最好地引用动态创建的 classes
我们刚刚看到了引用动态创建的 class 的各种方法。根据要求,M::B
与 class_obj
和 Object.const_get("#{mod}::#{cl}")
与 class_obj
。显然,class_obj
的使用在这两种情况下都是最简单的,并且具有额外的优势,即如果将来 M
或 B
中的 class_obj
引用不需要更改 M::B
已更改。
动态创建作为模块成员的 class 是否有优势?
回想一下,在模块中创建 class 的主要原因是创建一个命名空间,因此,例如,M1::C
和 M2::C
不会产生名称冲突。但是,如果我们通过变量(此处为 class_obj
)持有的(唯一)对象(而不是其名称,常量)引用动态创建的 class,则不需要命名空间.所以我在这部分提出的问题的答案是 "no".
此外,如果我们通过其对象引用动态创建的 class,则没有理由为 class 分配名称。因此我们可以修改 class_factory
如下:
def class_factory(consts, meths, instance_meths, accessors)
Class.new do
consts.each { |const,val| const_set(const,val) }
meths.each do |name,body|
singleton_class.
instance_eval("define_method(:#{name}) #{body}")
end
instance_meths.each do |name,body|
instance_eval("define_method(:#{name}) #{body}")
end
accessors.each do |accessor,inst_var|
public_send(accessor, inst_var)
end
end
end
class_obj = class_factory(
{ 'A'=>7, 'D'=>'cat' },
{ greeting: '{ |n| "Cat\'s have #{n} lives" }' },
{ initialize: '{ |name| @name = name }',
say_name: '{ "My name is #{@name}" }' },
{ attr_accessor: "name" }
)
#=> #<Class:0x000056cb6eaeefd0>
class_obj
持有的对象被称为匿名class,因为它没有名字(这是一个常量)。
class_obj.constants
#=> [:A, :D]
class_obj.methods(false)
#=> [:greeting]
class_obj.instance_methods(false)
#=> [:say_name, :name=, :name]
instance = class_obj.new "Billy-Bob"
#=> #<#<Class:0x000056cb6eaeefd0>:
# 0x000056cb6eb183d0 @name="Billy-Bob">
instance.say_name
#=> "My name is Billy-Bob"
instance.name
#=> "Billy-Bob"
instance.name = "BB"
#=> "BB"