1

我正在尝试定义一个名为“HTML”的类来扩展Nokogiri - 它使用模块。

我尝试了以下方法:

require 'nokogiri'

class HTML
    include Nokogiri
end

require 'nokogiri'

class HTML
    extend Nokogiri
end

但到目前为止,HTML 类不可能继承 nokogiri 中的所有功能。所以像这样的东西不起作用:

doc = HTML.new
doc.HTML(open('http://www.google.com/search?q=tenderlove'))

# (NoMethodError) 的未定义方法“HTML”

有谁知道我如何设法编写一个继承模块所有方法的类?

4

1 回答 1

10

Nokogiri 没有任何实例方法可以继承:

irb> Nokogiri.instance_methods
#=>  []

但通常,你会使用extend

% ri 扩展
-------------------------------------------------- -------- 对象#extend
     obj.extend(module, ...) => obj
-------------------------------------------------- ----------------------
     将每个模块的实例方法添加到 obj 中,作为
     范围。

        模组模组
          你好
            “来自 Mod 的你好。\n”
          结尾
        结尾

        类
          你好
            “来自克拉斯的你好。\n”
          结束结束

        k = Klass.new
        k.hello #=> "来自克拉斯的你好。\n"
        k.extend(Mod) #=> #<Klass:0x401b3bc8>
        k.hello #=> "来自模组的你好。\n"

%

您要做的是使用 Nokogiri 模块的所有类方法作为类的实例方法。这有点不标准,这就是语法不支持它的原因。大多数程序员将 ruby​​ 模块用于 Singleton 模式——只需要一个 Nokogiri,因此其他东西不应该能够使用它的方法。

你可以用 UndefinedMethods 做一些修改来解决这个问题,但考虑到 Nokogiri 在后端有一些编译的代码,这可能会产生未定义的错误。

这并不是说您不能将呼叫转接到 Nokogiri:

# nokogiri_wrapper.rb
require 'rubygems'
require 'nokogiri'

class NokogiriWrapper
  def method_missing(meth, *args, &blk)
    puts "call for #{meth.inspect}, #{args}, #{blk ? "with block" : "and no block"}"
    if Nokogiri.methods.include? meth.to_s
      puts "forwarding to Nokogiri"
      Nokogiri.send(meth, *args, &blk)
    else
      puts "falling back to default behaviour"
      super
    end
  end
end

html = "<html></html>"

puts "calling Nokogiri directly"
p Nokogiri.HTML(html)

wrapper = NokogiriWrapper.new

puts "calling Nokogiri through wrapper"
p wrapper.HTML(html)

puts "calling non-Nokogiri method with wrapper"
p(begin
    wrapper.scooby_dooby_doo!
  rescue NoMethodError => e
    [e.message, e.backtrace]
  end)
% ruby​​ nokogiri_wrapper.rb
直接打电话给Nokogiri
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 过渡//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html></html>

通过包装器调用 Nokogiri
调用 :HTML、<html></html> 和无块
转发到 Nokogiri
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 过渡//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html></html>

使用包装器调用非 Nokogiri 方法
要求 :scooby_dooby_doo!, , 并且没有阻塞
回退到默认行为
[“未定义的方法`scooby_dooby_doo!' 对于 #<NokogiriWrapper:0x581f74>", ["nokogiri_wrapper.rb:12:in `method_missing'", "nokogiri_wrapper.rb:29"]]

这是在 ruby​​ 中实现委托者模式的一种方法(另一种方法是使用委托者类之一)。

于 2009-07-01T03:54:46.660 回答