Gemfileに書いてあるgemのコードはどこから取得しているのか
Bundlerの動作を少し詳しく調べてみようと思ったのですが、その前にGemfileの内容について 少し調べました。
内容
- Gemfileとは
- Gemfileに書いてあるgemのコードはどこから取得しているのか
- global source lines
- sourceオプション
- pathオプション
- gitオプション
- ローカルリポジトリを取得先に指定した場合のファイルの更新内容の反映について
- gem探索の際の各種オプションの優先順位
参考
Gemfile(5) - A format for describing gem dependencies for Ruby programs
http://memo.yomukaku.net/entries/IpCSQmo
Bundler: The best way to manage a Ruby application's gems
Gemfileとは
GemfileはRubyのコードを動かすために必要なgemの依存性を書いておくためのファイルです。 gemの依存性を管理したいプロジェクトのルートディレクトリに置いておきます。
たとえば、 RailsではRakefile(Railsの起動コマンドを叩くファイル)と Gemfile(railsやrubyのバージョンをgemの形で管理している)を同じトップディレクトリに置いています。
Gemfile
# # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' gem 'rails', '4.1.0'
# Add your own tasks in files placed in lib/tasks ending in .rake, # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. require File.expand_path('../config/application', __FILE__) Rails.application.load_tasks
Gemfileに書いてあるgemのコードはどこから取得しているのか
global source lines
gemに対して、特に:source
や:git
, :path
オプションの指定をしなければGemfileの先頭行に書いてあるsourceメソッドの引数のサイトから取得されます。
source "https://rubygems.org"
ほぼ、https://rubygems.orgのはずです。
作成したgemを外部から利用されたい場合は、このサイトに作成したgemのgitリポジトリ等を登録すればよさそうです。
実は、sourceメソッドは複数行記述することが可能ですが、Bundlerのバージョンが1.7から非推奨になっております。
(また、このsourceメソッドの行はマニュアル中でglobal source lines
と呼ばれることがあります)
いろんなサイトから複数のバージョンが登録されていたりすることがあるため、 意図せぬライブラリを読み込まないためにするためだとおもいますが、 たとえば、社内ライブラリなので社内サーバから取得したいんだけど...という場合もありますよね。
そう言う場合は下記の各種オプションから適切なものを使えばよさそうです。
個人的に良く見かけているのはgit
です。
sourceオプション
下記のように、:source
オプションを利用してgemを検索してくるサイトを指定することが出来ます。
gem "some_internal_gem", :source => "https://gems.example.com"
ただし、指定したサイトにgemがなければbundlerはsourceオプションで指定したサイトからgemを探すので注意が必要です(後述)。
pathオプション
ファイルシステム内の特定のパスを指定して、gemの取得先とする事が出来ます。
指定するディレクトリとしては取得したいgemに対応する.gemspec
ファイルが
そのディレクトリのルートに含まれているか、
bundlerが使うgemのバージョンを指定する必要があります。
また、いくつかの同じディレクトリ下に入っているgemのパスを指定したい場合は、
path 'components' do gem 'admin_ui' gem 'public_ui' end
のようにブロックでそのディレクトリを与えると、gemの名前に対応するサブディレクトリの
.gemspec
を指定します。
gitオプション
また、これらのオプションはgroup
のブロックを使って一括して書く事が出来ます。
ブロックに対して与えたオプション(ref
, tag
, branch
など)は、ブロック内の各行に対して継承されます。
source "https://gems.example.com" do gem "some_internal_gem" gem "another_internal_gem" end git "https://github.com/rails/rails.git" do gem "activesupport" gem "actionpack" end platforms :ruby do gem "ruby-debug" gem "sqlite3" end group :development do gem "wirble" gem "faker" end
ローカルリポジトリを取得先に指定した場合のファイルの更新内容の反映について
リモートのサイトからgemを取得する場合、 リモートのサイトのgitに登録されているgemのバージョンで指定されているコミットの内容を取得します。
しかし、gemの指定先にローカルのブランチを指定した場合、
デフォルトでは、.gemspec
においてs.files = [ ... ]
に含まれているファイルの内容が更新されているかを調べ、
ローカルのブランチにコミットしなくてもファイルに変更を保存するたびに、
その更新内容を反映させます。
たとえばRails::Engineの内容を元のRailsプロジェクトからローカルから取得するようにしたとき、
gem 'some_engine', :path => "~/some_engine"
some_engine内のコードの加えられたコードの変更を反映させたい時は、 元のプロジェクト内のModelのコードの変更を反映させる時と同じように、railsコンソール上で、
reload!
コマンドを実行するかコンソールを再起動すれば、リモートへプッシュしたりローカルのgitにコミットされていなくても 変更を反映させる事が出来ます。
このローカルのgemの更新の検査、更新内容の反映を止めさせたい場合は、
bundle config disable_local_branch_check false
とbundlerに設定を追加します。
gem探索の際の各種オプションの優先順位
- そのgemに個別にオプションが設定されていれば、そのオプション(
:source
や:git
,:path
)の設定を利用します - Gemfileに書いたgemが依存している等、暗黙的に指定されているgemを、そのgemを利用するGemfileに書いたgemの設定に従って、取り込みます。有名な例としては、Railsが利用しているActiveSupportのgemがあります。ActiveSupportはrubygems.orgに別のバージョンが公開されていますが、Railsをインストールしたときにはrailsのgemspecで指定されているrailsリポジトリ内のActiveSupportのコードが利用されます。
- 最後に1行目に
global source lines
として指定したデフォルトのsourceのURLからgemを探します。
実は、これ以外にbundler自身のオプションも読み込まれるみたいで、 bundler自体のconfigに特定のgemのconfigを下記のように設定しておくと
bundle config local.GEM_NAME /path/to/local/git/repository
と打ち込んだ上で、Gemfileを
gem 'rack', :github => 'rack/rack', :branch => 'master' # branchの部分を追加!
上記の通りにbundler自身に設定してしまうと、その設定はパソコン内のbundlerでglobalな設定なのでやや注意が必要です。 また、
bundle config --delete local.GEM_NAME
も打ち込まないといけず、gem自身を置き換えたいならGemfileだけで十分かも、という気がしています。