从Ruby Core类中调用自定义方法



tl; dr;
如何从红宝石核心类中调用方法(写在茫茫荒野中)?


我正在编写一个管理文本文件的脚本。这是我的代码:

File.open("file.txt", "w").each do |f|
  f.puts "Text to be inserted"
  f.puts text_generated
  f.puts "Some other text" if some_condition?
  f.puts ""
end

我想通过引入一种方法来清理代码:

File.open("file.txt", "w").each do |f|
  f.puts_content(text_generated, some_condition?
  # -> Generates an error: private method called for <File> (NoMethodError)
end
def puts_content(text, cond)
  puts "Text to be inserted"
  puts text
  puts "Some other text" if cond
  puts ""
end

,实际上,由于私有方法访问,此方法在File类中不可coll。

任何人都可以解释这个错误,以及我该怎么做?


我的解决方法是在从File继承的自定义MyFile类中写下这些方法:

MyFile.open("file.txt", "w").each do |f|
  f.puts_content  # Seems to work
end
class MyFile < File
  def puts_content(cond)
    puts "Text to be inserted"
    puts text_generated_elsewhere
    puts "Some other text" if cond
    puts ""
  end
end

我可以将这些东西直接放在File中,但是在触摸语言核心库时,我胆小。

我想知道这是否是这样做的好方法。


可以从其他核心模块/类调用Ruby Core方法。这是否意味着所有核心模块/类都包括或需要彼此?它如何在引擎盖下工作?

当您在顶级定义方法时,它将在对象上添加一个实例方法,因此可以访问下降类(大多数其他核心类)

def foo
  1
end
method(:foo)
# => #<Method: Object#foo>

但是,此方法的访问级别在IRB/pry中似乎与运行脚本时不同。

in irb:

puts [].foo
# => 1

在脚本中:

puts [].foo
# => NoMethodError (private method called...)

当然,您总是可以使用send调用私人方法:

[].send(:foo)
# or, in your case, f.send(:puts_content, text_generated, some_condition?)

另外,在任何情况下,如果已经定义了后代类上的方法:

def length
  1
end
puts [].length
# => 0

您的第二种方法(直接修补核心类)将起作用,如果PUTS_CONTENT已在文件中定义(不是),则将覆盖PUTS_CONTENT。但是,如果您想避免修补核心类,我建议有两种方法:

  1. 使用静态(类)方法,并将文件对象作为参数

    传递
    class FileUtils
      def self.puts_content(file, text, cond)
        file.puts "Text to be inserted"
        file.puts text
        file.puts "Some other text" if cond
        file.puts ""
      end
    end
    File.open("file.txt", "w").each do |f|
      FileUtils.puts_content(f, text_generated, some_condition?)
    end
    
  2. 使用改进:

    module FileUtils
      refine File do
        def puts_content(text, cond)
          puts "Text to be inserted"
          puts text
          puts "Some other text" if cond
          puts ""
        end
      end
    end
    # elsewhere ...
    using FileUtils
    File.open("file.txt", "w").each do |f|
      f.puts_content(f, text_generated, some_condition?)
    end
    

    您可以在此处阅读有关改进的信息,但是从本质上讲,它们仅在某个文件或类中修补核心类。这为您带来了那种精美的,简短的猴子捕捉语法的好处,而更改其他地方定义的行为的风险较小。

关于您的第一个问题。您遇到了错误,因为该方法未在File类中定义。因此,您无法像此f.puts_content一样称呼它。

您可以定义接收File为参数puts_content(file, ...)的方法。

关于您问题的第二部分,我是一个很好的解决方案(以对象为导向)。

最新更新