Class: Gem::Source

Inherits:
Object
  • Object
show all
Includes:
Comparable, Text
Defined in:
lib/rubygems/source.rb

Overview

A Source knows how to list and fetch gems from a RubyGems marshal index.

There are other Source subclasses for installed gems, local gems, the bundler dependency API and so-forth.

Direct Known Subclasses

Git, Installed, Local, Lock, SpecificFile

Defined Under Namespace

Classes: Git, Installed, Local, Lock, SpecificFile, Vendor

Constant Summary collapse

FILES =

:nodoc:

{ # :nodoc:
  :released   => 'specs',
  :latest     => 'latest_specs',
  :prerelease => 'prerelease_specs',
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Text

#clean_text, #format_text, #levenshtein_distance, #min3, #truncate_text

Constructor Details

#initialize(uri) ⇒ Source

Creates a new Source which will use the index located at uri.


28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/rubygems/source.rb', line 28

def initialize(uri)
  begin
    unless uri.kind_of? URI
      uri = URI.parse(uri.to_s)
    end
  rescue URI::InvalidURIError
    raise if Gem::Source == self.class
  end

  @uri = uri
  @update_cache = nil
end

Instance Attribute Details

#uriObject (readonly)

The URI this source will fetch gems from.


23
24
25
# File 'lib/rubygems/source.rb', line 23

def uri
  @uri
end

Instance Method Details

#<=>(other) ⇒ Object

Sources are ordered by installation preference.


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
# File 'lib/rubygems/source.rb', line 44

def <=>(other)
  case other
  when Gem::Source::Installed,
       Gem::Source::Local,
       Gem::Source::Lock,
       Gem::Source::SpecificFile,
       Gem::Source::Git,
       Gem::Source::Vendor then
    -1
  when Gem::Source then
    if !@uri
      return 0 unless other.uri
      return 1
    end

    return -1 if !other.uri

    # Returning 1 here ensures that when sorting a list of sources, the
    # original ordering of sources supplied by the user is preserved.
    return 1 unless @uri.to_s == other.uri.to_s

    0
  else
    nil
  end
end

#==(other) ⇒ Object Also known as: eql?

:nodoc:


71
72
73
# File 'lib/rubygems/source.rb', line 71

def ==(other) # :nodoc:
  self.class === other and @uri == other.uri
end

#cache_dir(uri) ⇒ Object

Returns the local directory to write uri to.


110
111
112
113
114
115
116
# File 'lib/rubygems/source.rb', line 110

def cache_dir(uri)
  # Correct for windows paths
  escaped_path = uri.path.sub(/^\/([a-z]):\//i, '/\\1-/')
  escaped_path.tap(&Gem::UNTAINT)

  File.join Gem.spec_cache_dir, "#{uri.host}%#{uri.port}", File.dirname(escaped_path)
end

#dependency_resolver_setObject

Returns a Set that can fetch specifications from this source.


80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/rubygems/source.rb', line 80

def dependency_resolver_set # :nodoc:
  return Gem::Resolver::IndexSet.new self if 'file' == uri.scheme

  fetch_uri = if uri.host == "rubygems.org"
                index_uri = uri.dup
                index_uri.host = "index.rubygems.org"
                index_uri
              else
                uri
              end

  bundler_api_uri = enforce_trailing_slash(fetch_uri)

  begin
    fetcher = Gem::RemoteFetcher.fetcher
    response = fetcher.fetch_path bundler_api_uri, nil, true
  rescue Gem::RemoteFetcher::FetchError
    Gem::Resolver::IndexSet.new self
  else
    Gem::Resolver::APISet.new response.uri + "./info/"
  end
end

#download(spec, dir = Dir.pwd) ⇒ Object

Downloads spec and writes it to dir. See also Gem::RemoteFetcher#download.


213
214
215
216
# File 'lib/rubygems/source.rb', line 213

def download(spec, dir=Dir.pwd)
  fetcher = Gem::RemoteFetcher.fetcher
  fetcher.download spec, uri.to_s, dir
end

#fetch_spec(name_tuple) ⇒ Object

Fetches a specification for the given name_tuple.


134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/rubygems/source.rb', line 134

def fetch_spec(name_tuple)
  fetcher = Gem::RemoteFetcher.fetcher

  spec_file_name = name_tuple.spec_name

  source_uri = enforce_trailing_slash(uri) + "#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}"

  cache_dir = cache_dir source_uri

  local_spec = File.join cache_dir, spec_file_name

  if File.exist? local_spec
    spec = Gem.read_binary local_spec
    spec = Marshal.load(spec) rescue nil
    return spec if spec
  end

  source_uri.path << '.rz'

  spec = fetcher.fetch_path source_uri
  spec = Gem::Util.inflate spec

  if update_cache?
    require "fileutils"
    FileUtils.mkdir_p cache_dir

    File.open local_spec, 'wb' do |io|
      io.write spec
    end
  end

  # TODO: Investigate setting Gem::Specification#loaded_from to a URI
  Marshal.load spec
end

#hashObject

:nodoc:


103
104
105
# File 'lib/rubygems/source.rb', line 103

def hash # :nodoc:
  @uri.hash
end

#load_specs(type) ⇒ Object

Loads type kind of specs fetching from @uri if the on-disk cache is out of date.

type is one of the following:

:released => Return the list of all released specs :latest => Return the list of only the highest version of each gem :prerelease => Return the list of all prerelease only specs


180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/rubygems/source.rb', line 180

def load_specs(type)
  file       = FILES[type]
  fetcher    = Gem::RemoteFetcher.fetcher
  file_name  = "#{file}.#{Gem.marshal_version}"
  spec_path  = enforce_trailing_slash(uri) + "#{file_name}.gz"
  cache_dir  = cache_dir spec_path
  local_file = File.join(cache_dir, file_name)
  retried    = false

  if update_cache?
    require "fileutils"
    FileUtils.mkdir_p cache_dir
  end

  spec_dump = fetcher.cache_update_path spec_path, local_file, update_cache?

  begin
    Gem::NameTuple.from_list Marshal.load(spec_dump)
  rescue ArgumentError
    if update_cache? && !retried
      FileUtils.rm local_file
      retried = true
      retry
    else
      raise Gem::Exception.new("Invalid spec cache file in #{local_file}")
    end
  end
end

#pretty_print(q) ⇒ Object

:nodoc:


218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/rubygems/source.rb', line 218

def pretty_print(q) # :nodoc:
  q.group 2, '[Remote:', ']' do
    q.breakable
    q.text @uri.to_s

    if api = uri
      q.breakable
      q.text 'API URI: '
      q.text api.to_s
    end
  end
end

#typo_squatting?(host, distance_threshold = 4) ⇒ Boolean

Returns:

  • (Boolean)

231
232
233
234
# File 'lib/rubygems/source.rb', line 231

def typo_squatting?(host, distance_threshold=4)
  return if @uri.host.nil?
  levenshtein_distance(@uri.host, host).between? 1, distance_threshold
end

#update_cache?Boolean

Returns true when it is possible and safe to update the cache directory.

Returns:

  • (Boolean)

121
122
123
124
125
126
127
128
129
# File 'lib/rubygems/source.rb', line 121

def update_cache?
  return @update_cache unless @update_cache.nil?
  @update_cache =
    begin
      File.stat(Gem.user_home).uid == Process.uid
    rescue Errno::ENOENT
      false
    end
end