在ruby中具有环绕体的古怪
我对ruby不熟悉 试着写一篇关于方面的文章。我的代码如下在ruby中具有环绕体的古怪,ruby,metaprogramming,Ruby,Metaprogramming,我对ruby不熟悉 试着写一篇关于方面的文章。我的代码如下 module Utils module Aspects def self.included(base) base.extend(self) end def around_aspect(method_name, before_proc, after_proc) code = %Q[ def #{method_name} *args, &block
module Utils
module Aspects
def self.included(base)
base.extend(self)
end
def around_aspect(method_name, before_proc, after_proc)
code = %Q[
def #{method_name} *args, &block
#{before_proc.call}
old_#{method_name} *args, &block
#{after_proc.call}
end
]
class_eval %Q[
alias_method :old_#{method_name}, :#{method_name}
]
class_eval code
end
# def before_aspect method_name, before_proc
# around_aspect method_name, before_proc, ->(){}
# end
#
# def after_aspect method_name, after_proc
# around_aspect method_name, ->(){}, after_proc
# end
end
end
class Test
include Utils::Aspects
def test
puts 'test'
end
before = ->(){puts 'before'}
after = ->(){puts 'after'}
around_aspect :test,before,after
end
Test.new.test
我的代码如下所示
module Utils
module Aspects
def self.included(base)
base.extend(self)
end
def around_aspect(method_name, before_proc, after_proc)
code = %Q[
def #{method_name} *args, &block
#{before_proc.call}
old_#{method_name} *args, &block
#{after_proc.call}
end
]
class_eval %Q[
alias_method :old_#{method_name}, :#{method_name}
]
class_eval code
end
# def before_aspect method_name, before_proc
# around_aspect method_name, before_proc, ->(){}
# end
#
# def after_aspect method_name, after_proc
# around_aspect method_name, ->(){}, after_proc
# end
end
end
class Test
include Utils::Aspects
def test
puts 'test'
end
before = ->(){puts 'before'}
after = ->(){puts 'after'}
around_aspect :test,before,after
end
Test.new.test
问题是,当我执行Test.new.Test
时,我希望它打印出来
前,测试和后“按顺序。但现在它打印“前,后和测试”
问题是,当我执行Test.new.Test
时,我希望它打印出来
前、测试和后“按顺序”。但现在它打印“之前、之后和测试”
不,没有。调用Test.new.Test
时,它只打印Test
<定义包装方法时(即调用around\u advice
时),会打印之前
和之后
尝试在调用通知
和调用Test.new.Test
(并尝试调用Test
多次)之间放置,以观察以下情况:
puts '______________________'
Test.new.test
Test.new.test
# before
# after
# ______________________
# test
# test
定义方法时,只调用lambda一次:
code = %Q[
def #{method_name} *args, &block
#{before_proc.call}
# ^^^^^^^^^^^^^^^^^^^
old_#{method_name} *args, &block
#{after_proc.call}
# ^^^^^^^^^^^^^^^^^^
end
]
code = %Q[
def #{method_name} *args, &block
before_proc.call
old_#{method_name} *args, &block
after_proc.call
end
]
每次调用方法时都需要调用它们:
code = %Q[
def #{method_name} *args, &block
#{before_proc.call}
# ^^^^^^^^^^^^^^^^^^^
old_#{method_name} *args, &block
#{after_proc.call}
# ^^^^^^^^^^^^^^^^^^
end
]
code = %Q[
def #{method_name} *args, &block
before_proc.call
old_#{method_name} *args, &block
after_proc.call
end
]
然而,只使用模块#prepend
会容易得多,毕竟,这就是它的用途:
module Aspects
refine Module do
def around_aspect(method_name, before_proc, after_proc)
prepend(Module.new do
define_method(method_name) do |*args, &block|
before_proc.()
super(*args, &block)
after_proc.()
end
end)
end
end
end
class Test
using Aspects
def test
puts 'test'
end
before = -> {puts 'before'}
after = -> {puts 'after'}
around_aspect :test, before, after
end
问题是,当我执行Test.new.Test
时,我希望它打印出来
前,测试和后“按顺序。但现在它打印“前,后和测试”
不,不需要。当调用Test.new.Test时,它只在定义包装方法时(即调用环绕_advice
时)打印Test
之前的和之后的
尝试在调用通知
和调用Test.new.Test
(并尝试调用Test
多次)之间放置,以观察以下情况:
puts '______________________'
Test.new.test
Test.new.test
# before
# after
# ______________________
# test
# test
定义方法时,只调用lambda一次:
code = %Q[
def #{method_name} *args, &block
#{before_proc.call}
# ^^^^^^^^^^^^^^^^^^^
old_#{method_name} *args, &block
#{after_proc.call}
# ^^^^^^^^^^^^^^^^^^
end
]
code = %Q[
def #{method_name} *args, &block
before_proc.call
old_#{method_name} *args, &block
after_proc.call
end
]
每次调用方法时都需要调用它们:
code = %Q[
def #{method_name} *args, &block
#{before_proc.call}
# ^^^^^^^^^^^^^^^^^^^
old_#{method_name} *args, &block
#{after_proc.call}
# ^^^^^^^^^^^^^^^^^^
end
]
code = %Q[
def #{method_name} *args, &block
before_proc.call
old_#{method_name} *args, &block
after_proc.call
end
]
然而,只使用模块#prepend
会容易得多,毕竟,这就是它的用途:
module Aspects
refine Module do
def around_aspect(method_name, before_proc, after_proc)
prepend(Module.new do
define_method(method_name) do |*args, &block|
before_proc.()
super(*args, &block)
after_proc.()
end
end)
end
end
end
class Test
using Aspects
def test
puts 'test'
end
before = -> {puts 'before'}
after = -> {puts 'after'}
around_aspect :test, before, after
end
只是把我的代码放在这里。这就是为什么我最终实现了我想做的事情,上面建议的module.prepend是另一种方式
module Utils
module Aspects
def self.included(base)
base.extend(self)
end
def around_aspect(method_name, before_proc, after_proc)
new_method_name = Random.new_seed.to_s
alias_method :"#{new_method_name}", :"#{method_name}"
define_method "#{method_name}" do |*args, &block|
before_proc.call
send(:"#{new_method_name}", *args, &block)
after_proc.call
end
end
def before_aspect method_name, before_proc
around_aspect method_name, before_proc, ->(){}
end
def after_aspect method_name, after_proc
around_aspect method_name, ->(){}, after_proc
end
end
end
class Test
include Utils::Aspects
def test
puts 'test'
end
before = ->(){puts 'before'}
after = ->(){puts 'after'}
before_aspect :test, before
after_aspect :test, after
end
Test.new.test
只是把我的代码放在这里。这就是为什么我最终实现了我想做的事情,上面建议的module.prepend是另一种方式
module Utils
module Aspects
def self.included(base)
base.extend(self)
end
def around_aspect(method_name, before_proc, after_proc)
new_method_name = Random.new_seed.to_s
alias_method :"#{new_method_name}", :"#{method_name}"
define_method "#{method_name}" do |*args, &block|
before_proc.call
send(:"#{new_method_name}", *args, &block)
after_proc.call
end
end
def before_aspect method_name, before_proc
around_aspect method_name, before_proc, ->(){}
end
def after_aspect method_name, after_proc
around_aspect method_name, ->(){}, after_proc
end
end
end
class Test
include Utils::Aspects
def test
puts 'test'
end
before = ->(){puts 'before'}
after = ->(){puts 'after'}
before_aspect :test, before
after_aspect :test, after
end
Test.new.test
为什么要优化模块
而不是将模块.new{def around_aspect…end}
直接添加到测试.singleton_类
?!不确定过程。()
调用表示法在这里是非常传统的。还要记住,这个包装函数的返回值现在已被破坏,需要捕获并返回调用super
的结果。@tadman:这与OP的行为相匹配。一个环绕的建议应该能够更改返回值。但是,在OP的代码中after块无法检查返回值,因此它不能做很多事情。@JörgWMittag您是对的,对before和after的调用是在定义时间而不是在我的代码中的调用时间进行计算的,但我不知道为什么。我正在计算表达式#{before_proc.call}在我的方法定义中,由于我正在使用class_eval,该方法是当前类的一部分,所以当我在类的实例上调用该方法时,不应该执行它。不确定发生了什么on@AbdulRahman:您正在插入表达式{before_proc.call}
转换为代码字符串。字符串插值在解析字符串时发生。并且只发生一次。该插值的结果仅为nil
,使用转换为
(当被插值的对象还不是字符串时,字符串插值始终使用转换为
).nil.to_s
只是一个空字符串,因此在生成的代码中没有建议的痕迹,唯一可以观察到的是插入字符串时的副作用(put
)。为什么要细化模块
而不是预加Module.new{def around\u aspect…end}
直接到测试。singleton_类
?!不确定过程。()
调用表示法在这里是非常传统的。还要记住,这个包装函数的返回值现在已被破坏,需要捕获并返回调用super
的结果。@tadman:这与OP的行为相匹配。一个环绕的建议应该能够更改返回值。但是,在OP的代码中after块无法检查返回值,因此它不能做很多事情。@JörgWMittag您是对的,对before和after的调用是在定义时间而不是在我的代码中的调用时间进行计算的,但我不知道为什么。我正在计算表达式#{before_proc.call}在我的方法定义中,由于我正在使用class_eval,该方法是当前类的一部分,所以当我在类的实例上调用该方法时,不应该执行它。不确定发生了什么on@AbdulRahman:您正在插入表达式{before_proc.call}
转换为代码字符串。字符串插值在解析字符串时发生。并且只发生一次。该插值的结果仅为nil
,使用转换为
(当被插值的对象还不是字符串时,字符串插值始终使用转换为
).nil.to_s
只是一个空字符串,因此在生成的代码中没有通知的痕迹,唯一可以观察到的是副作用(put
)在插入字符串时。如果使用相关的send
或call调用将其转换为define_方法,则可以在此处完全避免eval
。如果使用相关的send
或call将其转换为define_方法,则可以在此处完全避免eval
e> 召唤