Ruby:扩展模块的类

时间:2009-07-01 03:41:36

标签: ruby class include module extend

我正在尝试定义一个名为“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'

有谁知道如何设法继承模块所有方法的类?

1 个答案:

答案 0 :(得分:10)

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

irb> Nokogiri.instance_methods
#=>  []

但通常情况下,您会使用extend

% ri extend
---------------------------------------------------------- Object#extend
     obj.extend(module, ...)    => obj
------------------------------------------------------------------------
     Adds to obj the instance methods from each module given as a
     parameter.

        module Mod
          def hello
            "Hello from Mod.\n"
          end
        end

        class Klass
          def hello
            "Hello from Klass.\n"
          end        end

        k = Klass.new
        k.hello         #=> "Hello from Klass.\n"
        k.extend(Mod)   #=> #<Klass:0x401b3bc8>
        k.hello         #=> "Hello from Mod.\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
calling Nokogiri directly
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html></html>

calling Nokogiri through wrapper
call for :HTML, <html></html>, and no block
forwarding to Nokogiri
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html></html>

calling non-Nokogiri method with wrapper
call for :scooby_dooby_doo!, , and no block
falling back to default behaviour
["undefined method `scooby_dooby_doo!' for #<NokogiriWrapper:0x581f74>", ["nokogiri_wrapper.rb:12:in `method_missing'", "nokogiri_wrapper.rb:29"]]

这是在ruby中实现委托模式的一种方法(另一种方法是使用其中一个Delegator类)。