lib/bundler/runtime.rb



require "digest/sha1"

module Bundler
  class Runtime < Environment
    include SharedHelpers

    def initialize(*)
      super
      if locked?
        write_rb_lock
      end
    end

    def setup(*groups)
      # Has to happen first
      clean_load_path

      specs = groups.any? ? specs_for(groups) : requested_specs

      cripple_rubygems(specs)
      replace_rubygems_paths

      # Activate the specs
      specs.each do |spec|
        unless spec.loaded_from
          raise GemNotFound, "#{spec.full_name} is cached, but not installed."
        end

        Gem.loaded_specs[spec.name] = spec
        spec.load_paths.each do |path|
          $LOAD_PATH.unshift(path) unless $LOAD_PATH.include?(path)
        end
      end
      self
    end

    def require(*groups)
      groups.map! { |g| g.to_sym }
      groups = [:default] if groups.empty?
      autorequires = autorequires_for_groups(*groups)

      groups.each do |group|
        (autorequires[group] || [[]]).each do |path, explicit|
          if explicit
            Kernel.require(path)
          else
            begin
              Kernel.require(path)
            rescue LoadError
            end
          end
        end
      end
    end

    def dependencies
      @definition.dependencies
    end

    def actual_dependencies
      @definition.actual_dependencies
    end

    def lock
      sources.each { |s| s.lock if s.respond_to?(:lock) }
      FileUtils.mkdir_p("#{root}/.bundle")
      write_yml_lock
      write_rb_lock
    end

    def dependencies_for(*groups)
      if groups.empty?
        dependencies
      else
        dependencies.select { |d| (groups & d.groups).any? }
      end
    end

    alias gems specs

    def cache
      FileUtils.mkdir_p(cache_path)

      Bundler.ui.info "Copying .gem files into vendor/cache"
      specs.each do |spec|
        next unless spec.source.is_a?(Source::SystemGems) || spec.source.is_a?(Source::Rubygems)
        possibilities = Gem.path.map { |p| "#{p}/cache/#{spec.full_name}.gem" }
        cached_path = possibilities.find { |p| File.exist? p }
        raise GemNotFound, "Missing gem file '#{spec.full_name}.gem'." unless cached_path
        Bundler.ui.info "  * #{File.basename(cached_path)}"
        next if File.expand_path(File.dirname(cached_path)) == File.expand_path(cache_path)
        FileUtils.cp(cached_path, cache_path)
      end
    end

    def prune_cache
      FileUtils.mkdir_p(cache_path)

      Bundler.ui.info "Removing outdated .gem files from vendor/cache"
      cache_path.children.each do |gemfile|
        spec = Gem::Format.from_file_by_path(gemfile).spec
        gemfile.rmtree unless specs.include?(spec)
      end
    end

  private

    def load_paths
      specs.map { |s| s.load_paths }.flatten
    end

    def cache_path
      root.join("vendor/cache")
    end

    def write_yml_lock
      yml = details.to_yaml
      File.open("#{root}/Gemfile.lock", 'w') do |f|
        f.puts yml
      end
    end

    def details
      details = {}
      details["hash"] = gemfile_fingerprint
      details["sources"] = sources.map { |s| { s.class.name.split("::").last => s.options} }

      details["specs"] = specs.map do |s|
        options = {"version" => s.version.to_s}
        options["source"] = sources.index(s.source) if sources.include?(s.source)
        { s.name => options }
      end

      details["dependencies"] = @definition.dependencies.inject({}) do |h,d|
        info = {"version" => d.requirement.to_s, "group" => d.groups}
        info.merge!("require" => d.autorequire) if d.autorequire
        h.merge(d.name => info)
      end
      details
    end

    def replace_rubygems_paths
      Gem.instance_eval do
        def path
          [Bundler.bundle_path.to_s]
        end

        def source_index
          @source_index ||= Gem::SourceIndex.from_installed_gems
        end
      end
    end

  end
end