Extended maintenance of Ruby 1.9.3 ended on February 23, 2015. Read more

In Files

  • rubygems/dependency_installer.rb

Class/Module Index [+]

Quicksearch

Gem::DependencyInstaller

Installs a gem along with all its dependencies from local and remote gems.

Constants

DEFAULT_OPTIONS

Attributes

gems_to_install[R]
installed_gems[R]

Public Class Methods

new(options = {}) click to toggle source

Creates a new installer instance.

Options are:

:cache_dir

Alternate repository path to store .gem files in.

:domain

:local, :remote, or :both. :local only searches gems in the current directory. :remote searches only gems in Gem.sources. :both searches both.

:env_shebang

See Gem::Installer.new.

:force

See Gem::Installer#install.

:format_executable

See Gem::Installer#initialize.

:ignore_dependencies

Don’t install any dependencies.

:install_dir

See Gem::Installer#install.

:prerelease

Allow prerelease versions. See install.

:security_policy

See Gem::Installer.new and Gem::Security.

:user_install

See Gem::Installer.new

:wrappers

See Gem::Installer.new

 
               # File rubygems/dependency_installer.rb, line 46
def initialize(options = {})
  if options[:install_dir] then
    @gem_home = options[:install_dir]

    Gem::Specification.dirs = @gem_home
    Gem.ensure_gem_subdirectories @gem_home
    options[:install_dir] = @gem_home # FIX: because we suck and reuse below
  end

  options = DEFAULT_OPTIONS.merge options

  @bin_dir             = options[:bin_dir]
  @development         = options[:development]
  @domain              = options[:domain]
  @env_shebang         = options[:env_shebang]
  @force               = options[:force]
  @format_executable   = options[:format_executable]
  @ignore_dependencies = options[:ignore_dependencies]
  @prerelease          = options[:prerelease]
  @security_policy     = options[:security_policy]
  @user_install        = options[:user_install]
  @wrappers            = options[:wrappers]

  @installed_gems = []

  @install_dir = options[:install_dir] || Gem.dir
  @cache_dir = options[:cache_dir] || @install_dir

  # Set with any errors that SpecFetcher finds while search through
  # gemspecs for a dep
  @errors = nil
end
            

Public Instance Methods

add_found_dependencies(to_do, dependency_list) click to toggle source
 
               # File rubygems/dependency_installer.rb, line 159
def add_found_dependencies to_do, dependency_list
  seen = {}
  dependencies = Hash.new { |h, name| h[name] = Gem::Dependency.new name }

  until to_do.empty? do
    spec = to_do.shift
    next if spec.nil? or seen[spec.name]
    seen[spec.name] = true

    deps = spec.runtime_dependencies
    deps |= spec.development_dependencies if @development

    deps.each do |dep|
      dependencies[dep.name] = dependencies[dep.name].merge dep

      results = find_gems_with_sources(dep).reverse

      results.reject! do |dep_spec,|
        to_do.push dep_spec

        # already locally installed
        Gem::Specification.any? do |installed_spec|
          dep.name == installed_spec.name and
            dep.requirement.satisfied_by? installed_spec.version
        end
      end

      results.each do |dep_spec, source_uri|
        @specs_and_sources << [dep_spec, source_uri]

        dependency_list.add dep_spec
      end
    end
  end

  dependency_list.remove_specs_unsatisfied_by dependencies
end
            
find_gems_with_sources(dep) click to toggle source

Returns a list of pairs of gemspecs and source_uris that match Gem::Dependency dep from both local (Dir.pwd) and remote (Gem.sources) sources. Gems are sorted with newer gems preferred over older gems, and local gems preferred over remote gems.

 
               # File rubygems/dependency_installer.rb, line 85
def find_gems_with_sources(dep)
  # Reset the errors
  @errors = nil
  gems_and_sources = []

  if @domain == :both or @domain == :local then
    Dir[File.join(Dir.pwd, "#{dep.name}-[0-9]*.gem")].each do |gem_file|
      spec = Gem::Format.from_file_by_path(gem_file).spec
      gems_and_sources << [spec, gem_file] if spec.name == dep.name
    end
  end

  if @domain == :both or @domain == :remote then
    begin
      # REFACTOR: all = dep.requirement.needs_all?
      requirements = dep.requirement.requirements.map do |req, ver|
        req
      end

      all = !dep.prerelease? &&
            # we only need latest if there's one requirement and it is
            # guaranteed to match the newest specs
            (requirements.length > 1 or
              (requirements.first != ">=" and requirements.first != ">"))

      found, @errors = Gem::SpecFetcher.fetcher.fetch_with_errors dep, all, true, dep.prerelease?

      gems_and_sources.push(*found)

    rescue Gem::RemoteFetcher::FetchError => e
      if Gem.configuration.really_verbose then
        say "Error fetching remote data:\t\t#{e.message}"
        say "Falling back to local-only install"
      end
      @domain = :local
    end
  end

  gems_and_sources.sort_by do |gem, source|
    [gem, source =~ /^http:\/\// ? 0 : 1] # local gems win
  end
end
            
find_spec_by_name_and_version(gem_name, version = Gem::Requirement.default, prerelease = false) click to toggle source

Finds a spec and the source_uri it came from for gem gem_name and version. Returns an Array of specs and sources required for installation of the gem.

 
               # File rubygems/dependency_installer.rb, line 202
def find_spec_by_name_and_version(gem_name,
                                  version = Gem::Requirement.default,
                                  prerelease = false)
  spec_and_source = nil

  glob = if File::ALT_SEPARATOR then
           gem_name.gsub File::ALT_SEPARATOR, File::SEPARATOR
         else
           gem_name
         end

  local_gems = Dir["#{glob}*"].sort.reverse

  local_gems.each do |gem_file|
    next unless gem_file =~ /gem$/
    begin
      spec = Gem::Format.from_file_by_path(gem_file).spec
      spec_and_source = [spec, gem_file]
      break
    rescue SystemCallError, Gem::Package::FormatError
    end
  end

  unless spec_and_source then
    dep = Gem::Dependency.new gem_name, version
    dep.prerelease = true if prerelease
    spec_and_sources = find_gems_with_sources(dep).reverse
    spec_and_source = spec_and_sources.find { |spec, source|
      Gem::Platform.match spec.platform
    }
  end

  if spec_and_source.nil? then
    raise Gem::GemNotFoundException.new(
      "Could not find a valid gem '#{gem_name}' (#{version}) locally or in a repository",
      gem_name, version, @errors)
  end

  @specs_and_sources = [spec_and_source]
end
            
gather_dependencies() click to toggle source

Gathers all dependencies necessary for the installation from local and remote sources unless the ignore_dependencies was given.

 
               # File rubygems/dependency_installer.rb, line 132
def gather_dependencies
  specs = @specs_and_sources.map { |spec,_| spec }

  # these gems were listed by the user, always install them
  keep_names = specs.map { |spec| spec.full_name }

  dependency_list = Gem::DependencyList.new @development
  dependency_list.add(*specs)
  to_do = specs.dup

  add_found_dependencies to_do, dependency_list unless @ignore_dependencies

  dependency_list.specs.reject! { |spec|
    not keep_names.include?(spec.full_name) and
    Gem::Specification.include?(spec)
  }

  unless dependency_list.ok? or @ignore_dependencies or @force then
    reason = dependency_list.why_not_ok?.map { |k,v|
      "#{k} requires #{v.join(", ")}"
    }.join("; ")
    raise Gem::DependencyError, "Unable to resolve dependencies: #{reason}"
  end

  @gems_to_install = dependency_list.dependency_order.reverse
end
            
install(dep_or_name, version = Gem::Requirement.default) click to toggle source

Installs the gem dep_or_name and all its dependencies. Returns an Array of installed gem specifications.

If the :prerelease option is set and there is a prerelease for dep_or_name the prerelease version will be installed.

Unless explicitly specified as a prerelease dependency, prerelease gems that dep_or_name depend on will not be installed.

If c-1.a depends on b-1 and a-1.a and there is a gem b-1.a available then c-1.a, b-1 and a-1.a will be installed. b-1.a will need to be installed separately.

 
               # File rubygems/dependency_installer.rb, line 257
def install dep_or_name, version = Gem::Requirement.default
  if String === dep_or_name then
    find_spec_by_name_and_version dep_or_name, version, @prerelease
  else
    dep_or_name.prerelease = @prerelease
    @specs_and_sources = [find_gems_with_sources(dep_or_name).last]
  end

  @installed_gems = []

  gather_dependencies

  last = @gems_to_install.size - 1
  @gems_to_install.each_with_index do |spec, index|
    next if Gem::Specification.include?(spec) and index != last

    # TODO: make this sorta_verbose so other users can benefit from it
    say "Installing gem #{spec.full_name}" if Gem.configuration.really_verbose

    _, source_uri = @specs_and_sources.assoc spec
    begin
      local_gem_path = Gem::RemoteFetcher.fetcher.download spec, source_uri,
                                                           @cache_dir
    rescue Gem::RemoteFetcher::FetchError
      next if @force
      raise
    end

    inst = Gem::Installer.new local_gem_path,
                              :bin_dir             => @bin_dir,
                              :development         => @development,
                              :env_shebang         => @env_shebang,
                              :force               => @force,
                              :format_executable   => @format_executable,
                              :ignore_dependencies => @ignore_dependencies,
                              :install_dir         => @install_dir,
                              :security_policy     => @security_policy,
                              :user_install        => @user_install,
                              :wrappers            => @wrappers

    spec = inst.install

    @installed_gems << spec
  end

  @installed_gems
end
            

Commenting is here to help enhance the documentation. For example, code samples, or clarification of the documentation.

If you have questions about Ruby or the documentation, please post to one of the Ruby mailing lists. You will get better, faster, help that way.

If you wish to post a correction of the docs, please do so, but also file bug report so that it can be corrected for the next release. Thank you.

If you want to help improve the Ruby documentation, please visit Documenting-ruby.org.