summaryrefslogtreecommitdiff
path: root/tool/downloader.rb
blob: 49205f5c26a478d9a026f38f1336c3ed15b49119 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
require 'open-uri'

class Downloader
  def self.gnu(name)
    "http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=#{name};hb=HEAD"
  end

  def self.rubygems(name)
    "https://rubygems.org/downloads/#{name}"
  end

  def self.uri_to_download(url, name)
    from, url = url
    case from
    when :gnu
      url = gnu(url || name)
    when :rubygems, :gems
      url = rubygems(url || name)
    when Symbol
      raise ArgumentError, "unkonwn site - #{from}"
    else
      url = from
    end
    URI(url)
  end

  def self.mode_for(data)
    data.start_with?("#!") ? 0755 : 0644
  end

  def self.http_options(file, since)
    options = {}
    if since
      case since
      when true
        since = (File.mtime(file).httpdate rescue nil)
      when Time
        since = since.httpdate
      end
      if since
        options['If-Modified-Since'] = since
      end
    end
    options
  end

  # Downloader.download(url, name, [dir, [ims]])
  #
  # Update a file from url if newer version is available.
  # Creates the file if the file doesn't yet exist; however, the
  # directory where the file is being created has to exist already.
  # If +ims+ is false, always download url regardless of its last
  # modified time.
  #
  # Example usage:
  #   download 'http://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt',
  #           'enc/unicode/data/UnicodeData.txt'
  def self.download(url, name, dir = nil, ims = true)
    file = dir ? File.join(dir, name) : name
    url = uri_to_download(url, name)
    begin
      data = url.read(http_options(file, ims))
    rescue OpenURI::HTTPError => http_error
      return http_error.message =~ /^304 / # 304 Not Modified
      raise
    end
    mtime = nil
    open(file, "wb", 0600) do |f|
      f.write(data)
      f.chmod(mode_for(data))
      mtime = data.meta["last-modified"]
    end
    if mtime
      mtime = Time.httpdate(mtime)
      File.utime(mtime, mtime, file)
    end
    true
  rescue => e
    raise e.class, "failed to download #{name}\n#{e.message}: #{url}", e.backtrace
  end

  def self.download_if_modified_since(url, name, dir = nil)
    download(url, name, dir)
  end
end