Ruby DSL 嵌套结构
Ruby DSL nested constructs
我正在使用以下代码来强制执行 DSL 嵌套构造的上下文。实现相同功能的其他方法是什么?
def a &block
p "a"
def b &block
p "b"
def c &block
p "c"
instance_eval &block
end
instance_eval &block
undef :c
end
instance_eval &block
undef :b
end
# Works
a do
b do
c do
end
end
end
# Doesn't Work
b do
end
c do
end
你问的是其他方式,不是最好的方式。所以这里有一些例子:
示例 A
class A
def initialize
p "a"
end
def b &block
B.new.instance_eval &block
end
end
class B
def initialize
p "b"
end
def c &block
C.new.instance_eval &block
end
end
class C
def initialize
p "c"
end
end
def a &block
A.new.instance_eval &block
end
示例 B
短一点:
def a &block
p "a"
A.new.instance_eval &block
end
class A
def b &block
p "b"
B.new.instance_eval &block
end
class B
def c &block
p "c"
C.new.instance_eval &block
end
class C
end
end
end
示例 C
如果您不打算为 A::B::C 对象使用 d 方法:
def a &block
p "a"
A.new.instance_eval &block
end
class A
def b &block
p "b"
B.new.instance_eval &block
end
class B
def c &block
p "c"
instance_eval &block
end
end
end
示例 D
这很有趣:
def new_class_and_method(klass_name, next_klass=Object)
dynamic_klass = Class.new do
define_method(next_klass.name.downcase){|&block| p next_klass.name.downcase; next_klass.new.instance_eval &block}
end
Object.const_set(klass_name, dynamic_klass)
end
new_class_and_method("A", new_class_and_method("B", new_class_and_method("C")))
def a &block
p "a"
A.new.instance_eval &block
end
示例 E
我敢说这看起来还不错:
def new_method_and_class(x)
define_method(x) do |&block|
p x
self.class.const_get(x.capitalize).new.instance_eval &block
end
self.const_set(x.capitalize, Class.new)
end
["a", "b", "c"].inject(Object){|klass,x| klass.instance_eval{new_method_and_class(x)} }
示例 F
更健壮一点:
def new_method_and_class(x, parent_klass = Object)
parent_klass.class_eval do
define_method(x) do |&block|
p x
parent_klass.const_get(x.capitalize).new.instance_eval &block if block
end
end
parent_klass.const_set(x.capitalize, Class.new)
end
["a", "b", "c"].inject(Object){|klass,x| new_method_and_class(x,klass) }
说明
示例 B
在例子B中,我们首先定义:
- a() 方法
- 一个A class
两者都在main中定义,因为我们希望a()可以直接使用。
a() 方法并不期望打印 "a"
并将块传递给 A.
的实例
接着是b()方法。我们不希望它在 main 中可用,所以我们在 A class 中定义它。我们要继续嵌套方法,所以我们定义了一个Bclass,这个Bclass也在A里面定义,Bclass其实就是一个A::Bclass。 A::B#b() 方法还打印 "b"
,并将块传递给 B.
的实例
我们继续在 A::B 中使用 A::B::C,就像我们对 A::B 和 A.
所做的一样
示例 F
示例 F 基本上与示例 B 类似,但是是动态编写的。
在示例 B 中,我们在每个步骤中定义了一个 x 方法和一个 X class,具有完全相同的结构。应该可以使用名为 new_method_and_class(x)
的方法避免代码重复,该方法使用 define_method
、const_set
和 Class.new
:
new_method_and_class("a") # <- Object#a() and A are now defined
a do
puts self.inspect
end
#=> "a"
# <A:0x00000000e58bc0>
现在,我们想定义一个 b() 方法和一个 B class,但它们不应该在 main 中。 new_method_and_class("b")
不行。所以我们传递了一个额外的参数,叫做 parent_klass,默认为 Object :
parent_klass = new_method_and_class("a")
new_method_and_class("b", parent_klass)
a do
b do
puts self.inspect
end
end
# => "a"
# "b"
# <A::B:0x00000000daf368>
b do
puts "Not defined"
end
# => in `<main>': undefined method `b' for main:Object (NoMethodError)
要定义 c 方法,我们只需添加另一行:
parent_klass = new_method_and_class("a")
parent_klass = new_method_and_class("b", parent_klass)
parent_klass = new_method_and_class("c", parent_klass)
等等等等。
为了避免代码重复,我们可以使用 inject 和 parent_klass 作为累加器值:
["a", "b", "c"].inject(Object){|klass,x| new_method_and_class(x,klass) }
奖金 - 示例 G
下面是示例 F 中的修改代码,它使用基本的树结构。
#
def new_method_and_class(x, parent_klass = Object)
parent_klass.class_eval do
define_method(x) do |&block|
p x.to_s
parent_klass.const_get(x.capitalize).new.instance_eval &block if block
end
end
parent_klass.const_set(x.capitalize, Class.new)
end
def create_dsl(branch,parent_klass = Object)
case branch
when Symbol, String
new_method_and_class(branch,parent_klass)
when Array
branch.each do |child|
create_dsl(child, parent_klass)
end
when Hash
branch.each do |key, value|
create_dsl(value, new_method_and_class(key,parent_klass))
end
end
end
methods_tree = {
:a => {
:b => [
:c,
:d
],
:e => :f,
:g => nil
}
}
create_dsl(methods_tree)
a do
b do
c do
puts self.inspect
end
d do
end
end
e do
f do
end
end
g do
puts self.inspect
end
end
# =>
# "a"
# "b"
# "c"
# #<A::B::C:0x0000000243dfa8>
# "d"
# "e"
# "f"
# "g"
# #<A::G:0x0000000243d918>
我正在使用以下代码来强制执行 DSL 嵌套构造的上下文。实现相同功能的其他方法是什么?
def a &block
p "a"
def b &block
p "b"
def c &block
p "c"
instance_eval &block
end
instance_eval &block
undef :c
end
instance_eval &block
undef :b
end
# Works
a do
b do
c do
end
end
end
# Doesn't Work
b do
end
c do
end
你问的是其他方式,不是最好的方式。所以这里有一些例子:
示例 A
class A
def initialize
p "a"
end
def b &block
B.new.instance_eval &block
end
end
class B
def initialize
p "b"
end
def c &block
C.new.instance_eval &block
end
end
class C
def initialize
p "c"
end
end
def a &block
A.new.instance_eval &block
end
示例 B
短一点:
def a &block
p "a"
A.new.instance_eval &block
end
class A
def b &block
p "b"
B.new.instance_eval &block
end
class B
def c &block
p "c"
C.new.instance_eval &block
end
class C
end
end
end
示例 C
如果您不打算为 A::B::C 对象使用 d 方法:
def a &block
p "a"
A.new.instance_eval &block
end
class A
def b &block
p "b"
B.new.instance_eval &block
end
class B
def c &block
p "c"
instance_eval &block
end
end
end
示例 D
这很有趣:
def new_class_and_method(klass_name, next_klass=Object)
dynamic_klass = Class.new do
define_method(next_klass.name.downcase){|&block| p next_klass.name.downcase; next_klass.new.instance_eval &block}
end
Object.const_set(klass_name, dynamic_klass)
end
new_class_and_method("A", new_class_and_method("B", new_class_and_method("C")))
def a &block
p "a"
A.new.instance_eval &block
end
示例 E
我敢说这看起来还不错:
def new_method_and_class(x)
define_method(x) do |&block|
p x
self.class.const_get(x.capitalize).new.instance_eval &block
end
self.const_set(x.capitalize, Class.new)
end
["a", "b", "c"].inject(Object){|klass,x| klass.instance_eval{new_method_and_class(x)} }
示例 F
更健壮一点:
def new_method_and_class(x, parent_klass = Object)
parent_klass.class_eval do
define_method(x) do |&block|
p x
parent_klass.const_get(x.capitalize).new.instance_eval &block if block
end
end
parent_klass.const_set(x.capitalize, Class.new)
end
["a", "b", "c"].inject(Object){|klass,x| new_method_and_class(x,klass) }
说明
示例 B
在例子B中,我们首先定义:
- a() 方法
- 一个A class
两者都在main中定义,因为我们希望a()可以直接使用。
a() 方法并不期望打印 "a"
并将块传递给 A.
接着是b()方法。我们不希望它在 main 中可用,所以我们在 A class 中定义它。我们要继续嵌套方法,所以我们定义了一个Bclass,这个Bclass也在A里面定义,Bclass其实就是一个A::Bclass。 A::B#b() 方法还打印 "b"
,并将块传递给 B.
我们继续在 A::B 中使用 A::B::C,就像我们对 A::B 和 A.
所做的一样示例 F
示例 F 基本上与示例 B 类似,但是是动态编写的。
在示例 B 中,我们在每个步骤中定义了一个 x 方法和一个 X class,具有完全相同的结构。应该可以使用名为 new_method_and_class(x)
的方法避免代码重复,该方法使用 define_method
、const_set
和 Class.new
:
new_method_and_class("a") # <- Object#a() and A are now defined
a do
puts self.inspect
end
#=> "a"
# <A:0x00000000e58bc0>
现在,我们想定义一个 b() 方法和一个 B class,但它们不应该在 main 中。 new_method_and_class("b")
不行。所以我们传递了一个额外的参数,叫做 parent_klass,默认为 Object :
parent_klass = new_method_and_class("a")
new_method_and_class("b", parent_klass)
a do
b do
puts self.inspect
end
end
# => "a"
# "b"
# <A::B:0x00000000daf368>
b do
puts "Not defined"
end
# => in `<main>': undefined method `b' for main:Object (NoMethodError)
要定义 c 方法,我们只需添加另一行:
parent_klass = new_method_and_class("a")
parent_klass = new_method_and_class("b", parent_klass)
parent_klass = new_method_and_class("c", parent_klass)
等等等等。
为了避免代码重复,我们可以使用 inject 和 parent_klass 作为累加器值:
["a", "b", "c"].inject(Object){|klass,x| new_method_and_class(x,klass) }
奖金 - 示例 G
下面是示例 F 中的修改代码,它使用基本的树结构。
#
def new_method_and_class(x, parent_klass = Object)
parent_klass.class_eval do
define_method(x) do |&block|
p x.to_s
parent_klass.const_get(x.capitalize).new.instance_eval &block if block
end
end
parent_klass.const_set(x.capitalize, Class.new)
end
def create_dsl(branch,parent_klass = Object)
case branch
when Symbol, String
new_method_and_class(branch,parent_klass)
when Array
branch.each do |child|
create_dsl(child, parent_klass)
end
when Hash
branch.each do |key, value|
create_dsl(value, new_method_and_class(key,parent_klass))
end
end
end
methods_tree = {
:a => {
:b => [
:c,
:d
],
:e => :f,
:g => nil
}
}
create_dsl(methods_tree)
a do
b do
c do
puts self.inspect
end
d do
end
end
e do
f do
end
end
g do
puts self.inspect
end
end
# =>
# "a"
# "b"
# "c"
# #<A::B::C:0x0000000243dfa8>
# "d"
# "e"
# "f"
# "g"
# #<A::G:0x0000000243d918>