简体   繁体   English

在ruby中,如何将原始源代码加载到Proc的实例?(在类方法中将`before`之后`钩子添加)

[英]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_evalinstance_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工作原理有更好的了解。


My own solution and more detail shown here after reading reply: 我自己的解决方案以及阅读回复后在此处显示的更多细节:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM