FrontPage  Index  Search  Changes  RSS  Login

[Ruby] Kernel.gem についてメモ

概要

Kernel.gem の流れについてメモ。

Kernal.gem

def gem(gem_name, *version_requirements)
  skip_list = (ENV['GEM_SKIP'] || "").split(/:/)
  raise Gem::LoadError, "skipping #{gem_name}" if skip_list.include? gem_name
  Gem.activate(gem_name, *version_requirements)
end
  1. 「gem の名前」と「要求バージョン」が与えられる
  2. GEM_SKIP という環境変数があるかもしれない
  3. GEM_SKIP は複数の gem の名前をコロン(:)で区切ったもの
  4. 引数で与えられた gem の名前が GEM_SKIP に含まれたら Gem::LoadError
  5. 以降、Gem.activate に委譲

Gem.activate

def self.activate(gem, *version_requirements)
  if version_requirements.empty? then
    version_requirements = Gem::Requirement.default
  end

  unless gem.respond_to?(:name) and
         gem.respond_to?(:version_requirements) then
    gem = Gem::Dependency.new(gem, version_requirements)
  end

  matches = Gem.source_index.find_name(gem.name, gem.version_requirements)
  report_activate_error(gem) if matches.empty?

  if @loaded_specs[gem.name] then
    # This gem is already loaded.  If the currently loaded gem is not in the
    # list of candidate gems, then we have a version conflict.
    existing_spec = @loaded_specs[gem.name]

    unless matches.any? { |spec| spec.version == existing_spec.version } then
      raise Gem::Exception,
            "can't activate #{gem}, already activated #{existing_spec.full_name}"
    end

    return false
  end

  # new load
  spec = matches.last
  return false if spec.loaded?

  spec.loaded = true
  @loaded_specs[spec.name] = spec

  # Load dependent gems first
  spec.runtime_dependencies.each do |dep_gem|
    activate dep_gem
  end

  # bin directory must come before library directories
  spec.require_paths.unshift spec.bindir if spec.bindir

  require_paths = spec.require_paths.map do |path|
    File.join spec.full_gem_path, path
  end

  sitelibdir = ConfigMap[:sitelibdir]

  # gem directories must come after -I and ENV['RUBYLIB']
  insert_index = load_path_insert_index

  if insert_index then
    # gem directories must come after -I and ENV['RUBYLIB']
    $LOAD_PATH.insert(insert_index, *require_paths)
  else
    # we are probably testing in core, -I and RUBYLIB don't apply
    $LOAD_PATH.unshift(*require_paths)
  end

  return true
end
  1. 要求バージョンが無ければ Gem::Requirement.default で補完
  2. 引数 gem が name と version_requirements に応えられなければ Gem::Dependency.new の結果を gem に代入
  3. gem の名前と要求バージョンに該当する gem パッケージを探索して読み込み候補を得る
  4. 見つからなければエラー報告
  5. gem が読み込み済みである場合
    1. 読み込み候補のすべてが読み込み済みのバージョンと一致しないなら
      1. Gem::Exception, "can't activate #{gem}, already activated #{existing_spec.full_name}"
    2. 一致するものがあれば
      1. 偽を返して終了
  6. 読み込み候補の末尾を読み込むことにして
  7. 既に読み込み済みなら false を返して終了
  8. 読み込んでなければ読み込み済みマークを付けておく
  9. 読み込み済み gem の一覧にも登録しておく
  10. 依存している gem を activate する
  11. bindir があるならロードパスの頭に追加
  12. などなどロードパスに gem のパスを追加しておく
  13. 新規読み込みとして最後に true を返す

version conflict

指定 gem が読み込み済みで、異なるバージョンのものを読もうとすると、バージョン衝突と判断され、例外が発生する。

Last modified:2008/11/14 22:38:50
Keyword(s):[ruby]
References: