如何向URI模块添加方法

时间:2018-03-25 14:57:39

标签: ruby

我想从httphttps网址下载二进制文件,如:

URI('https://www.google.com/favicon.ico').download('google.ico')

我为此编写了这样的方法:

module URI
  def download(file)
    File.open(file, 'wb') {|f| f.write(open(self).read)}
  end
end

此方法最终出现错误ArgumentError: extra arguments,而以下代码正在运行。

file = 'google.ico'
url = 'https://www.google.com/favicon.ico')
File.open(file, 'wb') {|f| f.write(open(url).read)}

我做错了什么?我该如何解决?

2 个答案:

答案 0 :(得分:1)

URI module doesn't download file. File class doesn't either. open comes from open-uri stdlib which is why it works in your 2nd example. If you have curl in your system this should work:

module URI
  def self.download(file_url)
    filename = file_url.split('/').last
    `curl -O #{file_url}`
  end
end

If you DON'T have curl use open-uri

require 'open-uri'
module URI
  def self.download(file_url)
    filename = file_url.split('/').last
    File.open(filename, "wb") do |saved_file|
      open(file_url, "rb") do |read_file|
        saved_file.write(read_file.read)
      end
    end
  end
end

And call it like this

URI.download('https://www.google.com/favicon.ico')

Note, URI behaves like more like a class so you need to define the method on the base object self otherwise you'll need to create an instance, but since it's just a module, use def self.some_method(some_arg) to be able to call URL.some_method(some_arg)

While this works, it is not recommended for production. Why do you wanna monkey patch URI when you can simply write your own module which does this?

You're better off doing something like this:

module SimpleFileDownload
  require 'open-uri'
  def self.download(file_url)
    filename = file_url.split('/').last
    File.open(filename, "wb") do |saved_file|
      open(file_url, "rb") do |read_file|
        saved_file.write(read_file.read)
      end
    end
  end
end

and call it like:

SimpleFileDownload.download('https://www.google.com/favicon.ico')

答案 1 :(得分:1)

我以为我是从Kernel.open拨打open-uri,但是在URI OpenURI::OpenRead模块内部被调用。

首先我添加了binding.pry

module URI
  def download(file)
    binding.pry
    File.open(file, 'wb') {|f| f.write(open(self).read)}
  end
end

并检查调用哪个方法:

pry(#<URI::HTTPS>)> show-method open

From: /Users/ironsand/.rbenv/versions/2.4.3/lib/ruby/2.4.0/open-uri.rb @ line 720:
Owner: OpenURI::OpenRead
Visibility: public
Number of lines: 3

def open(*rest, &block)
  OpenURI.open_uri(self, *rest, &block)
end

pry(#<URI::HTTPS>)> exit
pry(main)> show-method open

From: /Users/ironsand/.rbenv/versions/2.4.3/lib/ruby/2.4.0/open-uri.rb @ line 29:
Owner: Kernel
Visibility: private
Number of lines: 11

def open(name, *rest, &block) # :doc:
  if name.respond_to?(:open)
    name.open(*rest, &block)
  elsif name.respond_to?(:to_str) &&
        %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://} =~ name &&
        (uri = URI.parse(name)).respond_to?(:open)
    uri.open(*rest, &block)
  else
    open_uri_original_open(name, *rest, &block)
  end
end

要使用正确的方法,我应该明确调用该方法。

module URI
  def download(file)
    File.open(file, 'wb') {|f| f.write(OpenURI.open_uri(self).read)}
  end
end

上面的代码按预期工作。