module Bundler::Plugin::API::Source

def ==(other)

The same applies for `eql?` and `hash`

lockfile. To converge the sources, it is necessary that they match.
The sources objects are constructed from Gemfile as well as from

sources should compare on branch and tag but not on commit hash)
that are used to pin down the gem to specific version (e.g. Git
inferred from the options passed from Gemfile and not on attributes
The comparison shall take place only on the attribute that can be

This shall check if two source object represent the same source.
def ==(other)
  other.is_a?(self.class) && uri == other.uri
end

def add_dependency_names(names)

Note: Do not override if you don't know what you are doing.

Used by definition.
def add_dependency_names(names)
  @dependencies |= Array(names)
end

def app_cache_dirname

This is used by `app_cache_path`

Also this name is matched against the directories in cache for pruning

#cache is called.
Name of directory where plugin the is expected to cache the gems when
def app_cache_dirname
  base_name = File.basename(Bundler::URI.parse(uri).normalize.path)
  "#{base_name}-#{uri_hash}"
end

def app_cache_path(custom_path = nil)

Note: Do not override if you don't know what you are doing.

installed latter.
The full path where the plugin should cache the gem so that it can be
def app_cache_path(custom_path = nil)
  @app_cache_path ||= Bundler.app_cache(custom_path).join(app_cache_dirname)
end

def cache(spec, custom_path = nil)

by bundler.
at sub paths of `cache_path` (from API). This can be though as caching
This cache is different from the internal caching that can be done

specs and install only from this cache when `cached!` is called.
This is stored with the app and source plugins should try to provide

alternative to remote)
be reinstalled from the cache without querying the remote (i.e. an
source can resolve to path provided by `app_cache_app`so that they can
This method is called while caching to save copy of the gems that the
def cache(spec, custom_path = nil)
  new_cache_path = app_cache_path(custom_path)
  FileUtils.rm_rf(new_cache_path)
  FileUtils.cp_r(install_path, new_cache_path)
  FileUtils.touch(app_cache_path.join(".bundlecache"))
end

def cached!

install from the path provided by `app_cache_path`.
When this is called, the source should try to fetch the specs and

Set internal representation to fetch the gems/specs from app cache.
def cached!
end

def can_lock?(spec)

NOTE: Do not override if you don't know what you are doing.
def can_lock?(spec)
  spec.source == self
end

def double_check_for(*); end

Other tags:
    Private: -
def double_check_for(*); end

def fetch_gemspec_files

Returns:
  • (Array) - paths of the gemspec files for gems that can
def fetch_gemspec_files
  []
end

def gem_install_dir

NOTE: Do not override if you don't know what you are doing.
def gem_install_dir
  Bundler.install_path
end

def hash

should have same hash.
docstring for `==` method, i.e. two methods equal by above comparison
When overriding `hash` please preserve the behaviour as mentioned in
def hash
  [self.class, uri].hash
end

def include?(other)

NOTE: Do not override if you don't know what you are doing.
def include?(other)
  other == self
end

def initialize(opts)

def initialize(opts)
  @options = opts
  @dependency_names = []
  @uri = opts["uri"]
  @type = opts["type"]
  @name = opts["name"] || "#{@type} at #{@uri}"
end

def install(spec, opts)

Returns:
  • (String) - post installation message (if any)
def install(spec, opts)
  raise MalformattedPlugin, "Source plugins need to override the install method."
end

def install_path

of its own.
servers multiple gems, it's not of much use and the source should one
A default installation path to install a single gem. If the source
def install_path
  @install_path ||=
    begin
      base_name = File.basename(Bundler::URI.parse(uri).normalize.path)
      gem_install_dir.join("#{base_name}-#{uri_hash[0..11]}")
    end
end

def installed?

A helper method, not necessary if not used internally.
def installed?
  File.directory?(install_path)
end

def local!

install from the local system.
When this is called, the source should try to fetch the specs and

Set internal representation to fetch the gems/specs locally.
def local!
end

def options_to_lock

Returns:
  • (Hash) -
def options_to_lock
  {}
end

def post_install(spec, disable_exts = false)

Note: Do not override if you don't know what you are doing.

It also runs Gem hooks `pre_install`, `post_build` and `post_install`

gem at correct install location.
It should be called in `install` after the plugin is done placing the

plugins should set that.
It depends on `spec.loaded_from` to get full_gem_path. The source

provided.
It builds extensions, generates bins and installs them for the spec
def post_install(spec, disable_exts = false)
  opts = { :env_shebang => false, :disable_extensions => disable_exts }
  installer = Bundler::Source::Path::Installer.new(spec, opts)
  installer.post_install
end

def remote!

install from remote path.
When this is called, the source should try to fetch the specs and

Set internal representation to fetch the gems/specs from remote.
def remote!
end

def root

Note: Do not override if you don't know what you are doing.

spec's loaded_from path is expanded against this to get full_gem_path

It is used to obtain the full_gem_path.
def root
  Bundler.root
end

def spec_names

Note: Do not override if you don't know what you are doing.

Used by definition.
def spec_names
  specs.spec_names
end

def specs

Returns:
  • (Bundler::Index) - index containing the specs
def specs
  files = fetch_gemspec_files
  Bundler::Index.build do |index|
    files.each do |file|
      next unless spec = Bundler.load_gemspec(file)
      Bundler.rubygems.set_installed_by_version(spec)
      spec.source = self
      Bundler.rubygems.validate(spec)
      index << spec
    end
  end
end

def to_lock

Note: Do not override if you don't know what you are doing.

and not override this.
Plugin should use `options_to_lock` to save information in lockfile

Saves type and remote and also calls to `options_to_lock`.
Generates the content to be entered into the lockfile.
def to_lock
  out = String.new("#{LockfileParser::PLUGIN}\n")
  out << "  remote: #{@uri}\n"
  out << "  type: #{@type}\n"
  options_to_lock.each do |opt, value|
    out << "  #{opt}: #{value}\n"
  end
  out << "  specs:\n"
end

def to_s

def to_s
  "plugin source for #{@type} with uri #{@uri}"
end

def unlock!

refresh the cache/specs (e.g. git sources can make a fresh clone).
If the source plugin is loaded from lockfile or otherwise, it shall

This is called to update the spec and installation.
def unlock!
end

def unmet_deps

Note: Do not override if you don't know what you are doing.

Used by definition.
def unmet_deps
  specs.unmet_dependency_names
end

def uri_hash

def uri_hash
  SharedHelpers.digest(:SHA1).hexdigest(uri)
end