[英]In ruby, how to load raw source code to an instance of Proc?(add `before` `after` hook to class methods)
Normally, we can do this with Proc
objects: 通常,我们可以使用
Proc
对象执行此操作:
[15] pry(main)> pr = -> { puts "test message" }
=> #<Proc:0x000000057b8a90@(pry):12 (lambda)>
[16] pry(main)> pr.call
test message
=> nil
[17] pry(main)> define_method("test_method", pr)
=> :test_method
[18] pry(main)> test_method
test message
But, what if I have a raw code string and want to load it into a proc? 但是,如果我有原始代码字符串并将其加载到proc中怎么办? Fake code below:
伪造的代码如下:
raw_code = "puts 'test message'"
pr = -> { load(raw_code) } # how to define the `load` method to get this working?
pr.call # => test message
define_method("test_method", pr}
test_method # => test message
Actually, my original problem is how to write a method hook like this: 实际上,我最初的问题是如何编写这样的方法挂钩:
class TestClass
def test_method
puts url
end
def test_method_a
puts url
end
before :test_method, :test_method_a do
url = __method__.split("_").join("/")
end
end
TestClass.new.test_method # => "test/method"
TestClass.new.test_method_a # => "test/method/a"
My problem is more complicated, this is just a simple example to illustrate the key problem. 我的问题比较复杂,这只是说明关键问题的简单示例。
how to define the
load
method to get this working?如何定义
load
方法以使其正常工作?
By spelling it eval
: 通过将其拼写为
eval
:
raw_code = "puts 'test message'"
pr = -> { eval(raw_code) } # how to define the `load` method to get this working?
pr.call # => test message
define_method("test_method", pr)
test_method # => test message
--output:--
test message
test message
Actually, my original problem is how to write a method hook...
实际上,我最初的问题是如何编写方法挂钩...
class TestClass
def initialize
@url = %q{__method__.to_s.split("_").join("/")}
end
def test_method
puts(eval @url)
end
def test_method_a
puts(eval @url)
end
end
TestClass.new.test_method # => "test/method"
TestClass.new.test_method_a # => "test/method/a"
--output:--
test/method
test/method/a
Actually, my original problem is how to write a method hook like this:
实际上,我最初的问题是如何编写这样的方法挂钩:
Module TestClass def test_method puts url end
The problem is that url
can never refer to a value outside the def. 问题是
url
永远不能引用def之外的值。 A def cuts off the visibility of local variables outside the def. def切断def外部局部变量的可见性。
class TestClass
def test_method
puts @url
end
def test_method_a
puts @url
end
def self.before(*method_names, &block)
method_names.each do |method_name|
alias_method "first_#{method_name}", method_name
define_method(method_name, block) #This changes __method__ inside the block from nil to method_name
alias_method "second_#{method_name}", method_name
define_method(method_name) do
send "second_#{method_name}" #This method executes: @url = __method__.to_s.split(...
send "first_#{method_name}" #This method executes: puts @url
end
end
end
before :test_method, :test_method_a do
@url = __method__.to_s.split("_").join("/")
end
end
TestClass.new.test_method # => "test/method"
TestClass.new.test_method_a # => "test/method/a"
--output:--
test/method
test/method/a
Short version: load
loads code from a file . 短版:
load
从文件加载代码。 If you want to run code that you already have in a string, you can use eval
, or one of it's friends, class_eval
or instance_eval
. 如果要运行字符串中已有的代码,则可以使用
eval
或它的一个朋友class_eval
或instance_eval
。
If you do end up using eval
, however, you need to be very careful so that you won't accidentally run code that could delete your files, install malware or whatever. 但是,如果最终使用
eval
,则需要非常小心,以免意外运行可能删除文件,安装恶意软件或执行任何操作的代码。
Longer version: For a working version (in ruby 2.2.3) with load
, you would need to put your TestClass
class in a file: 较长的版本:对于具有
load
的工作版本(在ruby 2.2.3中),您需要将TestClass
类放入文件中:
class TestClass
def test_method
puts "OHAI"
end
end
Let's save this class in a file called "test_class.rb". 让我们将此类保存在名为“ test_class.rb”的文件中。
With that, the following should just work: 这样,以下内容应该可以正常工作:
pr = -> { load(File.join(__dir__, "test_class.rb")) }
pr.call
TestClass.new.test_method
This will not solve your "original problem", but it should give you a little better understanding on how load
works. 这不会解决您的“原始问题”,但可以使您对
load
工作原理有更好的了解。
Maybe there is a lot of confusion about why does my problem method hook
matter with load raw source code to Proc instance
. 关于为什么我的问题
method hook
与将load raw source code to Proc instance
问题可能引起很多困惑。
And also, I've solved my problem,now. 而且,现在我已经解决了我的问题。 So,let me explain the whole thing in detail:
所以,让我详细解释整个事情:
1, my original problem comes from that I need to extract a bunch of duplicated code at head of some methods of a module just like this: 1,我最初的问题来自于我需要在模块的某些方法的顶部提取一堆重复的代码,如下所示:
module TestClass
extend self
def test_method
url = __method__.to_s.split("_").join("/") # duplicated code,need to extract
puts url
end
def test_method_a
url = __method__.to_s.split("_").join("/") # duplicated code, need to extract
puts url
end
2, Then, after a lot of thinking, I come up with an idea, that's get test_method
test_method_a
's source code, and modify the source code by adding url = __method__.to_s.split("_").join("/")
at the head of it, then redefine the method with new code. 2,然后,经过一番思考,我想到了一个想法,那就是
test_method
test_method_a
的源代码,并通过添加url = __method__.to_s.split("_").join("/")
开头,然后使用新代码重新定义该方法。
3, After lot of hack, I failed with using eval
and then, post here asking help. 3,经过大量的破解后,我使用
eval
失败,然后在此处发布请求帮助。
4, After reading reply, I make sure eval
is definitely what I want. 4,阅读回复后,我确保
eval
绝对是我想要的。
5, Finally, I succeed, code show here: 5,最后,我成功了,代码显示在这里:
module TestClass
extend self
def test_method
puts url
end
def test_method_a
puts url
end
[ :test_method, :test_method_a ].each do |name|
method_source_code_ar = instance_method(name).source.split("\n")
method_source_code_ar.insert(1, 'url = __method__.to_s.split("_").join("/")')
method_source_code = method_source_code_ar[1..-2].join("\n")
define_method(name, -> { eval(method_source_code) })
end
end
TestClass.test_method # => test/method
TestClass.test_method_a # => test/method/a
6, More concise code version show: 6,更简洁的代码版本显示:
module TestClass
extend self
def test_method
puts url
end
def test_method_a
puts url
end
[ :test_method, :test_method_a ].each do |name|
method_source_code_ar = instance_method(name).source.split("\n")
method_source_code_ar.insert(1, 'url = __method__.to_s.split("_").join("/")')
method_source_code = method_source_code_ar.join("\n")
eval(method_source_code)
end
end
7, As eval
,for me, I think the key to understand it is,thinking not as if you're in writing mode, but as if code was running line by line until reaching the eval
line code, by that time, what do you want the code to do? 7,对于
eval
,对我来说,我认为理解它的关键是,不认为您好像处于编写模式,而是好像代码逐行运行,直到到达eval
行代码为止,到那时,该怎么办?您想要代码做什么? Sure, just eval
the method_source_code
string. 当然,只需
eval
method_source_code
字符串即可。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.