diff options
author | Jari Vetoniemi <jari.vetoniemi@indooratlas.com> | 2020-03-16 18:49:26 +0900 |
---|---|---|
committer | Jari Vetoniemi <jari.vetoniemi@indooratlas.com> | 2020-03-30 00:39:06 +0900 |
commit | fcbf63e62c627deae76c1b8cb8c0876c536ed811 (patch) | |
tree | 64cb17de3f41a2b6fef2368028fbd00349946994 /jni/ruby/lib/rubygems/source.rb |
Fresh start
Diffstat (limited to 'jni/ruby/lib/rubygems/source.rb')
-rw-r--r-- | jni/ruby/lib/rubygems/source.rb | 234 |
1 files changed, 234 insertions, 0 deletions
diff --git a/jni/ruby/lib/rubygems/source.rb b/jni/ruby/lib/rubygems/source.rb new file mode 100644 index 0000000..e5995f0 --- /dev/null +++ b/jni/ruby/lib/rubygems/source.rb @@ -0,0 +1,234 @@ +require 'uri' +require 'fileutils' + +## +# 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. + +class Gem::Source + + include Comparable + + FILES = { # :nodoc: + :released => 'specs', + :latest => 'latest_specs', + :prerelease => 'prerelease_specs', + } + + ## + # The URI this source will fetch gems from. + + attr_reader :uri + + ## + # Creates a new Source which will use the index located at +uri+. + + 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 + @api_uri = nil + end + + ## + # Use an SRV record on the host to look up the true endpoint for the index. + + def api_uri # :nodoc: + require 'rubygems/remote_fetcher' + @api_uri ||= Gem::RemoteFetcher.fetcher.api_endpoint uri + end + + ## + # Sources are ordered by installation preference. + + 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 + + @uri.to_s <=> other.uri.to_s + else + nil + end + end + + def == other # :nodoc: + self.class === other and @uri == other.uri + end + + alias_method :eql?, :== # :nodoc: + + ## + # Returns a Set that can fetch specifications from this source. + + def dependency_resolver_set # :nodoc: + return Gem::Resolver::IndexSet.new self if 'file' == api_uri.scheme + + bundler_api_uri = api_uri + './api/v1/dependencies' + + begin + fetcher = Gem::RemoteFetcher.fetcher + response = fetcher.fetch_path bundler_api_uri, nil, true + rescue Gem::RemoteFetcher::FetchError + Gem::Resolver::IndexSet.new self + else + if response.respond_to? :uri then + Gem::Resolver::APISet.new response.uri + else + Gem::Resolver::APISet.new bundler_api_uri + end + end + end + + def hash # :nodoc: + @uri.hash + end + + ## + # Returns the local directory to write +uri+ to. + + def cache_dir(uri) + # Correct for windows paths + escaped_path = uri.path.sub(/^\/([a-z]):\//i, '/\\1-/') + escaped_path.untaint + + File.join Gem.spec_cache_dir, "#{uri.host}%#{uri.port}", File.dirname(escaped_path) + end + + ## + # Returns true when it is possible and safe to update the cache directory. + + def update_cache? + @update_cache ||= + begin + File.stat(Gem.user_home).uid == Process.uid + rescue Errno::ENOENT + false + end + end + + ## + # Fetches a specification for the given +name_tuple+. + + def fetch_spec name_tuple + fetcher = Gem::RemoteFetcher.fetcher + + spec_file_name = name_tuple.spec_name + + uri = api_uri + "#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}" + + cache_dir = cache_dir uri + + local_spec = File.join cache_dir, spec_file_name + + if File.exist? local_spec then + spec = Gem.read_binary local_spec + spec = Marshal.load(spec) rescue nil + return spec if spec + end + + uri.path << '.rz' + + spec = fetcher.fetch_path uri + spec = Gem.inflate spec + + if update_cache? then + FileUtils.mkdir_p cache_dir + + 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 + + ## + # 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 + # + + def load_specs(type) + file = FILES[type] + fetcher = Gem::RemoteFetcher.fetcher + file_name = "#{file}.#{Gem.marshal_version}" + spec_path = api_uri + "#{file_name}.gz" + cache_dir = cache_dir spec_path + local_file = File.join(cache_dir, file_name) + retried = false + + FileUtils.mkdir_p cache_dir if update_cache? + + 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 + + ## + # Downloads +spec+ and writes it to +dir+. See also + # Gem::RemoteFetcher#download. + + def download(spec, dir=Dir.pwd) + fetcher = Gem::RemoteFetcher.fetcher + fetcher.download spec, api_uri.to_s, dir + end + + def pretty_print q # :nodoc: + q.group 2, '[Remote:', ']' do + q.breakable + q.text @uri.to_s + + if api = api_uri + q.breakable + q.text 'API URI: ' + q.text api.to_s + end + end + end + +end + +require 'rubygems/source/git' +require 'rubygems/source/installed' +require 'rubygems/source/specific_file' +require 'rubygems/source/local' +require 'rubygems/source/lock' +require 'rubygems/source/vendor' + |