簡體   English   中英

在ruby中,如何將原始源代碼加載到Proc的實例?(在類方法中將`before`之后`鈎子添加)

[英]In ruby, how to load raw source code to an instance of Proc?(add `before` `after` hook to class methods)

通常,我們可以使用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

但是,如果我有原始代碼字符串並將其加載到proc中怎么辦? 偽造的代碼如下:

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

實際上,我最初的問題是如何編寫這樣的方法掛鈎:

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"

我的問題比較復雜,這只是說明關鍵問題的簡單示例。

如何定義load方法以使其正常工作?

通過將其拼寫為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

實際上,我最初的問題是如何編寫方法掛鈎...

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

實際上,我最初的問題是如何編寫這樣的方法掛鈎:

 Module TestClass def test_method puts url end 

問題是url永遠不能引用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

短版: load文件加載代碼。 如果要運行字符串中已有的代碼,則可以使用eval或它的一個朋友class_evalinstance_eval

但是,如果最終使用eval ,則需要非常小心,以免意外運行可能刪除文件,安裝惡意軟件或執行任何操作的代碼。

較長的版本:對於具有load的工作版本(在ruby 2.2.3中),您需要將TestClass類放入文件中:

class TestClass
  def test_method
    puts "OHAI"
  end
end

讓我們將此類保存在名為“ test_class.rb”的文件中。

這樣,以下內容應該可以正常工作:

pr = -> { load(File.join(__dir__, "test_class.rb")) }
pr.call
TestClass.new.test_method

這不會解決您的“原始問題”,但可以使您對load工作原理有更好的了解。


我自己的解決方案以及閱讀回復后在此處顯示的更多細節:

關於為什么我的問題method hook與將load raw source code to Proc instance問題可能引起很多困惑。

而且,現在我已經解決了我的問題。 所以,讓我詳細解釋整個事情:

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,然后,經過一番思考,我想到了一個想法,那就是test_method test_method_a的源代碼,並通過添加url = __method__.to_s.split("_").join("/")開頭,然后使用新代碼重新定義該方法。

3,經過大量的破解后,我使用eval失敗,然后在此處發布請求幫助。

4,閱讀回復后,我確保eval絕對是我想要的。

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,更簡潔的代碼版本顯示:

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,對於eval ,對我來說,我認為理解它的關鍵是,不認為您好像處於編寫模式,而是好像代碼逐行運行,直到到達eval行代碼為止,到那時,該怎么辦?您想要代碼做什么? 當然,只需eval method_source_code字符串即可。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM