Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Ruby: GemfileとGemfile.lock究極ガイド(翻訳)

概要

元サイトの許諾を得て翻訳・公開いたします。

Ruby: GemfileとGemfile.lock究極ガイド(翻訳)

Ruby on Railsの開発者なら、GemfileやGemfile.lockを知らない人はいないでしょう。この2つのファイルはRuby gemをインストールするのに欠かせませんが、仕組みを知らないままでは混乱する可能性もあります。本記事では、Gemfileとは何か、その中に何があるのか、および使い方について解説します。

最初に、デフォルトのRails 7アプリケーションを作成し、それからGemfileの各行を調べて意味を理解していきましょう。

新規作成したアプリのディレクトリには、GemfileとGemfile.lockがあるのがわかります。

# 新規作成したGemfile

source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby "3.1.0"

gem "rails", "~> 7.0.3", ">= 7.0.3.1"
gem "sprockets-rails"
gem "sqlite3", "~> 1.4"
gem "puma", "~> 5.0"
gem "importmap-rails"
gem "turbo-rails"
gem "stimulus-rails"
gem "jbuilder"
gem "redis", "~> 4.0"
gem "tzinfo-data", platforms: %i[ mingw mswin x64_mingw jruby ]
gem "bootsnap", require: false

group :development, :test do
  gem "debug", platforms: %i[ mri mingw x64_mingw ]
end

group :development do
  gem "web-console"
end

まずは最も基本的な疑問を解消しましょう。

🔗 Gemfileってそもそも何なの?

Gemfileを一言で言うと、アプリケーションで必要なすべてのgemのリストを含むファイルです。bundle installを実行すると、Bundlerがそれらのgemを探索してインストールします。

たとえば上のGemfileにgem "rails", "~> 7.0.3", ">= 7.0.3.1"とあるのは、このアプリケーションでRailsのgemを使うという意味です。

🔗 Gemfileのgemはどこから来るの?

gemがどこから来るかを理解するために、新たに生成したGemfileの最初の2行を詳しく調べてみましょう。

source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

sourceキーワードは「グローバルソース」と呼ばれています。1個のGemfileにつきグローバルソースは1つだけです。ほとんどの場合、sourceには "https://rubygems.org" が使われますが、たとえば私たちが独自のGemfuryアカウントを使っている場合は以下のような別のsourceにすることもあります。

source "https://user:password@gems.example.com"

2行目のgit_sourceメソッドは、Gitリポジトリにあるgemを使うよう指示します。組み込みの:githubというGitソースはひとつの例ですが、独自のGitソースを作って使うことも可能です。

認証gemとして有名なdevise gemがアプリケーションで必要になり、このgemをGitHubリポジトリからインストールすることに決めたとしましょう。この場合、以下のさまざまな指定方法が使えます。

  • その1: 最新バージョンのgemを探索してインストールする(GitHubからgemをインストールするときの最も一般的な方法)。
gem "devise", github: "plataformatec/devise"
  • その2: 特定のブランチを指定してgemをインストールする
gem "devise", github: "plataformatec/devise", branch: "4-stable"
  • その3: 特定のタグを指定してgemをインストールする
gem "devise", github: "plataformatec/devise", tag: "v4.1.0"
  • その4: 特定のコミットを指定してgemをインストールする
gem "devise", github: "plataformatec/devise", ref: "d4bf52bdfd652cc1d87fa5800a04b288a81fd787"

追伸: Bundlerでは、:github以外に:gist:bitbucketgit_sourcesの組み込みとして指定できます。ただし独自のgit_sourcesはいつでも作成できます。

🔗 Gemfile内でRubyのバージョンを指定する方法は?

ruby "3.1.0"

Gemfileに上の行があると、BundlerはRuby 3.1.0を使います。Rubyバージョンが指定されていない場合は、デフォルトのバージョンのRubyを使います。実行中のRubyバージョンがGemfile内で指定されているRubyバージョンと合っていない場合は、Bundlerで例外が発生します。

Your Ruby version is 2.7.6, but your Gemfile specified 3.1.0

ローカルのRubyバージョンが2.7.6で、Gemfileで指定されているRubyバージョンが3.1.0の場合は例外が発生します。さらに:engineオプションや:engine_versionsオプションを使うと、使いたいRubyエンジンやバージョンを指定できます。

ruby "3.1.0", engine: "jruby", engine_version: '9.1.1.0'

🔗 gemの設定オプションにはどんなものがあるの?

大小さまざまな設定オプションを使ってgemの属性を細かく指定できます。

もう一度Gemfileを見てみましょう。

gem "rails", "~> 7.0.3", ">= 7.0.3.1"
gem "sprockets-rails"
gem "sqlite3", "~> 1.4"
gem "puma", "~> 5.0"
gem "importmap-rails"
gem "turbo-rails"
gem "stimulus-rails"
gem "jbuilder"
gem "redis", "~> 4.0"
gem "tzinfo-data", platforms: %i[ mingw mswin x64_mingw jruby ]
gem "bootsnap", require: false

ここではdevise gemをインストールする必要があるとします。ここで誰しもすぐに気がつくのは、Gemfileにgemを追加する方法が1種類ではなく、たくさんあることです。たいていのgemにはバージョンが複数あり、動作するプラットフォームもさまざまです。また、gemのバージョン指定方法もいろいろあることに気づきます。

=
指定のバージョンに等しい(デフォルト)
!=
指定のバージョンに等しくない
>
指定のバージョンより大きい
<
指定のバージョンより小さい
>=
指定のバージョンと等しいか大きい
<=
指定のバージョンと等しいか小さい
~>
指定のバージョンと等しいか大きい(悲観的な比較)

🔗 =演算子

最もシンプルなgemバージョン指定方法は、=演算子を使うことです。

gem "devise", "= 4.0.0"

上の場合は、devise gem 4.0.0だけをインストールします。バージョンを何も指定しない場合は、利用可能なgemバージョンのうち最新のものをインストールします。

🔗 !=演算子

バージョン3.1.1以外のdevise gemをインストールするとしましょう(このバージョンがアプリでサポートされていないなどの理由で)。そういうときは以下のようにも書けます。

gem "devise", "!= 3.1.1"

上の場合は、3.1.1でない最新のdevise gemをインストールします。

🔗 ><演算子

devise gemのバージョンのうち、3.1.0より大きく4.1.0より小さいバージョンだけをインストールしたい場合は、以下のように書きます。

gem "devise", "> 3.1.0", "< 4.1.0"

🔗 >=<=演算子

devise gemのバージョンのうち、3.1.0以上かつ4.1.0以下のバージョンだけをインストールしたい場合は、以下のように書きます。

gem "devise", ">= 3.1.0", "<= 4.1.0"

🔗 ~>演算子

たとえばアプリケーションのコードベースを壊さずにdevise gemの今後のバージョンでも動くようにしたいとしましょう。これを確実に行う唯一の方法は、後方互換性のあるgemバージョンだけをインストールすることです。つまり、システムにdevise 3.1.0がインストールされているのであれば、最新のdevise 4.1.0をインストールするよりも、devise 3.1.1をインストールする方がアプリケーションが壊れにくくなります。

インストールできるgemバージョンを特定の範囲のみに制限したい場合は、悲観的バージョン演算子~>を利用できます。

gem "devise", "~> 3.1" # これは">= 3.1.0", "< 4.0.0"と同じ

~>演算子で指定するバージョンの桁数を3.1から以下のように3.1.0に増やすと、バージョンの範囲を狭められます。

gem "devise", "~> 3.1.0" # これは">= 3.1.0", "< 3.2.0"と同じ

ところで、これらのバージョン指定子について、誰もが一度はこんな重大な疑問を抱いたことがあるはずです。

🔗 gemはバージョン指定なしで追加すべきなの?

バージョン指定子なしでgemを追加するのはバッドプラクティスとみなされています。バージョン指定子なしでgemを追加すると、実は暗黙で= 0.0.0というバージョン指定子をつけるのと同じになります。

gem "devise" # => gem "devise", "= 0.0.0"

これは現在のアプリケーションでは問題なく動くかもしれませんが、今後アプリケーションをアップグレードするとgemの互換性が失われてアプリケーションが壊れる可能性があるかもしれません。つまり、安全のためにはバージョン指定子を常に指定しておくべきということになります。

🔗 require: falseって何?

Gemfileを開くと、たとえばbootsnap gemに以下のようなrequire: falseというオプションが追加されていたりします。

gem "bootsnap", require: false

これは、bundle installを実行した時点ではbootsnap gemをインストールするだけにとどめ、requireはしないでおくよう指示しています。requireを実行すると、アプリケーションが起動するときにすべての関連gemファイルが読み込まれます。

require: falseオプションは、アプリケーションと直接関係ないgemをインストールする場合に便利です。コード品質のチェックやメトリクス計測に使うgemをたくさんインストールすることがありますが、アプリケーションで直接使わずに開発の利便性を高めるだけのgemにrequire: falseを指定しておけば、必要が生じたときにのみ遅延読み込みされるようになります。

たとえば、bootsnapはconfig/boot.rbファイル内で使われています。つまり、config/boot.rbファイル内では必要に応じてbootsnapを以下のようにrequireできます。

require "bootsnap/setup"

上のコードはconfig/boot.rbファイル内にあり、ここでbootsnapは遅延読み込みする形でrequireされています。

🔗 環境を指定してgemをインストールするには?

私たちのGemfileには以下の記述があります。

group :development, :test do
  gem "debug", platforms: %i[ mri mingw x64_mingw ]
end

group :development do
  gem "web-console"
end

私たちのGemfileには:development:testという2つのグループが追加されていることがわかります。これは、特定の環境でのみ使うgemをインストールするのに役立ちます。

:developmentグループ内でインストールしたgemはdevelopment環境でのみ利用できます。:test:productionなどについても同じ要領で設定できます。

debug gemが必要なのは:developmentグループと:testグループのみで、:productionグループでは必須ではありません。web-console gemが必要なのは:developmentグループのみで、:test:productionでは必須ではありません。

たとえば、development環境とtest環境ではsqlite3 gemを使い、production環境ではPostgreSQLを使いたいとしましょう。これを行うには、以下のようにsqlite3 gemを:development, :testグループに追加し、PostgreSQLは:productionグループに追加します。

group :development, :test do
  gem "sqlite3", "~> 1.4"
end

group :production do
  gem "pg", "~> 0.18"
end

ただし、アプリケーションをどの環境で実行しても、システムにはすべてのgemがダウンロード1されることをお忘れなく。つまり、production環境のサーバーでもtest環境用のgemやdevelopment環境用のgemがダウンロードされます。

🔗 プラットフォーム

platforms:オプションも、gemのグループ化と似たような感じでgemをグループ化しますが、そのgemが利用可能なプラットフォームを指定する点が異なります。

gem "debug", platforms: %i[ mri mingw x64_mingw ]

上のgemは、mrimingwx64_mingwプラットフォームで利用できるようになります。

指定できるプラットフォームは以下のとおりです。

ruby
CRuby(MRI)、Rubinius、TruffleRuby
ただしWindowsは含まない
mri
CRuby(MRI)のみ、
ただしWindowsは含まない
mingw
Windows 32ビット'mingw32'プラットフォーム
(RubyInstallerとも呼ばれます)
x64_mingw
Windows 64ビット'mingw32'プラットフォーム
(RubyInstaller x64とも呼ばれます)
rbx
Rubynius
jruby
JRuby
truffleruby
TruffleRuby
mswin
Windows

訳注

rubymrimswinmswin64windowsではバージョン指定も可能です。たとえばRuby 2.3を指定する場合は以下のように書きます。

ruby_23

参考: Bundler: gemfile

🔗 Gemfile.lockって何?

最後はGemfile.lockファイルです。このファイルには、現在インストールされているgemに関するすべての情報が含まれています。

Gemfile.lockは、bundle installコマンドを実行すると作成されます。Gemfile.lockには、そのアプリケーションで必要なgemの正確なバージョン付きリストが含まれています。

私たちが生成したGemfile.lockファイルの中身を見てみましょう。

GEM
  remote: https://rubygems.org/
  specs:
    rails (7.0.3.1)
      actioncable (= 7.0.3.1)
      actionmailbox (= 7.0.3.1)
      actionmailer (= 7.0.3.1)
      actionpack (= 7.0.3.1)
      actiontext (= 7.0.3.1)
      actionview (= 7.0.3.1)
      activejob (= 7.0.3.1)
      activemodel (= 7.0.3.1)
      activerecord (= 7.0.3.1)
      activestorage (= 7.0.3.1)
      activesupport (= 7.0.3.1)
      bundler (>= 1.15.0)
      railties (= 7.0.3.1)
      ...

PLATFORMS
  x86_64-darwin-20

DEPENDENCIES
  bootsnap
  debug
  importmap-rails
  jbuilder
  puma (~> 5.0)
  rails (~> 7.0.3, >= 7.0.3.1)
  redis (~> 4.0)
  sprockets-rails
  sqlite3 (~> 1.4)
  stimulus-rails
  turbo-rails
  tzinfo-data
  web-console

RUBY VERSION
   ruby 3.1.0p0

BUNDLED WITH
   2.3.14

上のGemfile.lockファイルは、以下の4つのセクションに分かれています。

🔗 GEM

GEMセクションには実際にインストールされているgemがあり、以下の2つのパートに分かれています。

remote:
インストールしたgemの取得元が記述されています。ここでは"https://rubygems.org/"です。
specs:
インストールされているgemのリストです。Gemfileに記述されているgemよりも数が多いことがわかります。これは、Gemfileに記述されているgemで必要な依存gemもインストールする必要があるためです。
たとえば、rails gemが多くのgemに依存していることがこのセクションを見るとわかります。つまり、これらのgemもインストールする必要があります。

🔗 PLATFORMS

PLATFORMSセクションには、gemを利用可能なプラットフォームのリストがあります。

アプリケーションディレクトリでbundle platformを実行すると、以下のような出力が得られます。

Your platform is: x86_64-darwin-20

Your app has gems that work on these platforms:
* x86_64-darwin-20

Your Gemfile specifies a Ruby version requirement:
* ruby 3.1.0p0

🔗 DEPENDENCIES

DEPENDENCIESセクションは、そのアプリケーションで現在インストールされている依存gemのリストです。これらのgemは、Gemfile内にあるgemのリストです。

🔗 RUBY VERSION

RUBY VERSIONセクションは、Gemfileで指定されたRubyバージョンです。

RUBY VERSION
   ruby 3.1.0p0 # GemfileがRuby 3.1.0p0用であることを示す

🔗 BUNDLED WITH

BUNDLED WITHセクションは、必要なgemを最後にインストールするのに使われたBundlerのバージョンです。

BUNDLED WITH
   2.3.14 # Bundler 2.3.14が使われたことを示す

GemfileやBundlerについて詳しくは、以下の公式ドキュメントをどうぞ。

参考: Bundler: gemfile

本記事を読んだ皆さんのGemfileやGemfile.lockの理解が進むことを願っています。

関連記事

Ruby 3.1: error_highlight gemが追加された(翻訳)


  1. 原文では「install」でしたが、Bundlerのドキュメントに合わせて「ダウンロード」としました。その環境で使わないgemはダウンロードされるだけで、インストールはされません。 

CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。