Ruby:扩展模块的类

时间:2022-06-24 23:20:48

I'm trying to to define a class called "HTML" to extend Nokogiri - which uses modules.

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

I tried the following:

我尝试了以下方法:

require 'nokogiri'

class HTML
    include Nokogiri
end

and

require 'nokogiri'

class HTML
    extend Nokogiri
end

but so far it's been impossible that the class HTML inherit of all the functions in nokogiri. So stuff like are not working:

但到目前为止,类HTML仍然无法继承nokogiri中的所有函数。所以像这样的东西不起作用:

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

undefined method `HTML' for # (NoMethodError)

用于#的未定义方法`HTML'(NoMethodError)

Does anyone know how I could manage to program a class that inherits of all the methods of a module?

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

1 个解决方案

#1


Nokogiri doesn't have any instance methods to inherit:

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

irb> Nokogiri.instance_methods
#=>  []

But normally, you would use extend

但通常情况下,你会使用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"

%

What you want to do is use all the class methods of the Nokogiri module as instance methods of your class. Which is a bit nonstandard, which is why the syntax doesn't support it. Most programmers use ruby modules for the Singleton pattern - there only needs to be one Nokogiri, so other things shouldn't be able to use its methods.

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

You could do some hacking with UndefinedMethods to get around this, but considering that Nokogiri has some compiled code in the backend, this may produce undefined bugs.

您可以使用UndefinedMethods进行一些黑客攻击,但考虑到Nokogiri在后端有一些编译代码,这可能会产生未定义的错误。

Which isn't to say you can't forward calls to 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"]]

This is one way to implement the delegator pattern in ruby (another way is to use one of the Delegator classes).

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

#1


Nokogiri doesn't have any instance methods to inherit:

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

irb> Nokogiri.instance_methods
#=>  []

But normally, you would use extend

但通常情况下,你会使用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"

%

What you want to do is use all the class methods of the Nokogiri module as instance methods of your class. Which is a bit nonstandard, which is why the syntax doesn't support it. Most programmers use ruby modules for the Singleton pattern - there only needs to be one Nokogiri, so other things shouldn't be able to use its methods.

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

You could do some hacking with UndefinedMethods to get around this, but considering that Nokogiri has some compiled code in the backend, this may produce undefined bugs.

您可以使用UndefinedMethods进行一些黑客攻击,但考虑到Nokogiri在后端有一些编译代码,这可能会产生未定义的错误。

Which isn't to say you can't forward calls to 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"]]

This is one way to implement the delegator pattern in ruby (another way is to use one of the Delegator classes).

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