ライブラリ? gem? bundler? -- Rubyのgem管理に関するあれこれまとめ

#はじめに
Rubyの勉強をしているとgemの管理は大事だとか、```gem xxx```でgemをインストールしますだとか、```bundle xxx```でgemをインストールしますといった説明をよく目にします。
えっ、gemとbundleのコマンドって何が違うの、そもそもgemって何よ、bundleって何よと???な状態になるわけですね。
そんなわけで、Rubyのgem管理まわりの用語と一部コマンドの使い方も含めてまとめてみました。
私自身もRubyの勉強は始めたばかりで、記述内容に間違いがあるかもしれません。その時はご指摘をよろしくお願いします。

#用語について
まずは用語の意味について整理してゆきましょう。
全体像をつかむために図にしてみました。

![gem1](https://cl.ly/6f1dfb376e7e/gem1_correct1.png)

はい、絵心がない図のせいかサッパリですね。
一つずつ見てゆきましょう。

##ライブラリ
用語の意味は以下です。
>ある特定の機能を持ったコンピュータプログラムを他のプログラムから呼び出して利用できるように部品化し、そのようなプログラム部品を複数集めて一つのファイルに収納したものをライブラリという。

[IT用語辞典--ライブラリ 【 library 】 ライブラリー](http://e-words.jp/w/%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA.html)

最初の図で"nokogiri"や"active_support", "twitter"のことを指します。

例えば、日付の計算をしてくれるライブラリを挙げます。
昨日の日付を求めたい時に自分でコーディングすると、今日の日付を変数に格納して、そこから日付を-1して...と記述してゆかなければならないのをライブラリを使うことで、日付を求めるライブラリの使用を宣言して、昨日の日付を教えてと呼び出すことですみます。
(この例だと、同じ2ステップでかわらないじゃんとつっこまれそうですが...)
上の例だとあれですが、ライブラリを使うことでプログラミングで実現したいことを多機能に、そして簡単に実装できてしまうわけです。

また、これらライブラリの一つ一つをRubyではgemと呼んでいます。

ライブラリの説明については以下の投稿が具体例つきで丁寧に解説されているので、参考になるかと思います。

- [Rubygemsのススメ | Rubyに慣れていない初心者さんへ](https://qiita.com/sumyapp/items/5ec58bf3567e557c24d7)

##gem
上で記述したと通り、gemはライブラリのことです。
gemはRubyの環境にインストールしてこないと使えないです。(自作すればインストールする必要はないかな?)
なので、最初の図のgemはrepositoryからgem(ライブラリ)をインストールしたり、それだけでなくアップデートや削除といったgem(ライブラリ)を管理するgemコマンドを指しています。
ここで、RubyGemsについてですが用語の意味については以下です。

>Ruby言語用のパッケージ管理システムであり、Rubyのプログラムと（"gem" と呼ばれる）ライブラリの配布用標準フォーマットを提供している。gemを容易に管理でき、gemを配布するサーバの機能も持つ。Rubyバージョン1.9以降では標準ライブラリの一部となっている。

[RubyGems - Wikipedia](https://ja.wikipedia.org/wiki/RubyGems)

~~RubyGemsはgemを公開してくれている場所という解釈で良いと思います。~~
@topstone さんからご指摘を頂きました。
良くないですね、誤りです。
RubyGemsはgemを管理する仕組みそのもので、gemの標準フォーマット, 管理コマンド, repository等をまとめたものといったところでしょうか。(私自身の個人的な解釈を含むので、誤っている可能性があることをご容赦ください)
gemを公開してる場所はrepositoryで、オープンなものとして[https://rubygems.org](https://rubygems.org)があります。
gemを探したかったらここで検索すると見つかると思います。

##bundler
//////////////////////////////////////////////////
@topstone さんからのご指摘を受け、bundlerの説明を一新しました。
見やすさを考慮して、元の文を削除してしまうことをご容赦ください。
//////////////////////////////////////////////////
以下、解説開始
bundlerは"gemfile"と"gemfile.lock"に従って、gemを管理をするライブラリです。
bundlerもgemなんです。
最初の図のbundleはgemを管理する、bundleコマンドを指します。

bundlerの説明については以下の投稿やページが丁寧に解説されているので、参考になるかと思います。

- [Bundlerの使い方](https://qiita.com/oshou/items/6283c2315dc7dd244aef)
- [Bundler](https://bundler.io/)

##設定ファイル
gem関連の設定ファイルはいくつかありますが、今回は基本的で特に重要な"gemfile"と"gemfile.lock"の二つについてピックアップします。
この二つの設定ファイルは、先ほど解説したbundlerが利用します。
"gemfile"はbundlerがインストールするgemとそのバージョン情報を設定します。
"gemfile.lock"は"gemfile"で設定したgemと依存関係のあるgemの情報について設定します。

とはいっても開発者が設定するのは基本的に"gemfile"だけでよく、後ほど解説するbundleコマンドを実行すれば"gemfile"の設定に従って"gemfile.lock"は自動で設定されます。
"gemfile"の中身や設定方法については以下のページが参考になるかと思います。

- [xxxcaqui.log -- Gemfileについて調べてみた](http://xxxcaqui.hatenablog.com/entry/2013/02/11/013421)
- [Ruby STUDIO -- Gemfile ](http://ruby.studio-kingdom.com/bundler/gemfile/)

[参考]
```bundle init```で作られたばかりの"gemfile"の中身は以下のようになっています。

```:gemfile
# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

# gem "rails"
```


#実践
一通り用語の解説は完了しました。
せっかくgem管理まわりのことを理解して頂けた(?)ので、実践してみましょう。

実践の前提として、すでにRubyの実行環境が整っていることとします。
web上のIDEなり、ローカルの実行環境なりでRuby1.9.X以上が利用できればこの後の実践も行えます。
Windows限定になってしまいますが、ローカルでRubyの実行環境の構築に興味がある方は以下の記事を以前に投稿しているので、参考にしてみてください。

- [Rubyの実行環境構築 on Windows](https://qiita.com/3no3_tw/items/8c0bcf258370d91bf6e0)

gemやbundleコマンドはWindowsであればコマンドプロンプトやWindows PowerShell, MacOSであればターミナル等で実行できます。
この記事ではWindows PowerShellで実行してゆきたいと思います。

また、気をつけて欲しいのがgemをインストールする場所です。
gemのインストール場所については各々の環境によって違い、その場所を吟味しておかないとgemを運用してゆく上で問題が生じる可能性があります。
ただ単に、コマンドを実行してくださいと書いて、みなさんの環境を汚すようなことになっては不本意なので注意してください。
gemのインストール場所の設定や確認方法は以下のページ等を参考にしてみてください。

- [Ruby/rubygemsメモ("gem install"するその前にチェックしておきたい項目)](https://www.glamenv-septzen.net/view/1265)
- [bundlerでgemをプロジェクトごとに管理する](https://dev.classmethod.jp/server-side/language/bundler-gem-management/)
- [bundlerを使い始めた時に詰まったので調べた。](http://takuya-1st.hatenablog.jp/entry/2016/01/03/172013)

※この後の解説ではbundlerはシステムに、それ以外のgemは練習用のプロジェクトにインストールする流れで実行しています。

##bundlerのインストール
先ほど述べた通り、bundlerもgemの一つです。
~~また、gemの管理は基本的にgemコマンドよりもbundlerでとも述べましたが、~~ 導入しなければ使いようがないので、bundlerの導入まではgemコマンド、それ以降はbundleコマンドを使ってゆきます。

では、bundlerをgemコマンドでインストールしましょう。

```:PowerShell
PS> gem install bundler
```

これだけです。
本当にインストールされたのかよって感じだと思うので、以下のコマンドでインストールされたgemの一覧を確認できます。

```:PowerShell
PS> gem list

*** LOCAL GEMS ***

bigdecimal (default: 1.3.0)
bundler (1.16.1)
did_you_mean (1.1.0)
io-console (default: 0.4.6)
json (default: 2.0.4)
minitest (5.10.1)
net-telnet (0.1.1)
openssl (default: 2.0.5)
power_assert (0.4.1)
psych (default: 2.2.2)
rake (12.0.0)
rdoc (default: 5.0.0)
test-unit (3.2.3)
xmlrpc (0.2.1)
```

また、以下のコマンドでインストールしたbundlerのバージョンが確認できると思います。

```:PowerShell
PS> bundler -v

Bundler version 1.16.1
```

##gemのインストール
bundlerでgemをインストールしてみましょう。
先ほども述べましたように、システムの環境を汚さないように練習用のプロジェクトを作成して、そこで実行してゆきたいと思います。

まず適当な場所にディレクトリを作り、カレントディレクトリとして移動します。(作成するディレクトリ名は好きなように)

```:PowerShell
PS> mkdir practice
PS> cd practice
```

gemfileを作成します。

```:PowerShell
PS> bundle init
```

そうすると、カレントディレクトリにgemfileが作成できたことを確認できると思います。

```:PowerShell
PS> cat gemfile

# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

# gem "rails"
```

このgemfileをもとにgemをインストールするので、インストールしたいgemを設定しましょう。
エディタでgemfileを編集してください。

```:gemfile
# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

gem "rsense"
```

私は"rsense"というgemを設定していますが、何でも良いです。
たまたま環境構築で必要なgemだったため、選んだだけです。ちなみに、"rsense"については以下です。

- [rsense](https://rubygems.org/gems/rsense/versions/0.5.18)

gemをインストールしましょう。

```:PowerShell
PS> bundle install --path vendor/bundle

Fetching gem metadata from https://rubygems.org/.........
Using bundler 1.16.1
Using multipart-post 2.0.0
Using faraday 0.14.0
Using ffi 1.9.21 (x64-mingw32)
Using filetree 1.0.0
Using jruby-jars 1.7.27
Using jruby-parser 0.5.4
Using mustermann 1.0.1
Using rack 2.0.4
Using rack-protection 2.0.0
Using spoon 0.0.6
Using thor 0.19.4
Using rsense-core 0.6.6
Using tilt 2.0.8
Using sinatra 2.0.0
Using rsense-server 0.5.18
Fetching rsense 0.5.18
Installing rsense 0.5.18
Bundle complete! 1 Gemfile dependency, 17 gems now installed.
Bundled gems are installed into `./vendor/bundle`
```

```bundle install```でgemfileの設定をもとにgemをインストールします。
```--path vendor/bundle```のオプションは```--path```以下のディレクトリ```vendor/bundle```にgemをインストールすることを指します。
このオプションを指定することで、gemのインストール先をシステムの環境と分けることができるわけです。
```--path```は一度指定すれば"config"の設定ファイルに設定されて、それ以降は```bundle install```のコマンドだけで、```--path```で指定した先にgemがインストールされるようになります。

```:PowerShell
PS> cat xxx\practice\.bundle\config  /*xxxはプロジェクトのディレクトリを作成したパス
---
BUNDLE_PATH: "vendor/bundle"
```

システムの方にインストール先を戻したければ```bundle install --system```で設定できます。
ひとつ、参考程度に記述しておきますが、システムにインストールされているgemはプロジェクトごとのgemfileに設定してもインストールされずにスルーされて、実際にプログラムでgemを使用する時はシステムのgemが使用されるみたいです。その回避方法もあるようで、以下のページで解説されています。

- [昼メシ物語 -- gem管理の新標準ツール"Bundler"のTips](http://blog.mirakui.com/entry/20100703/1278165723)

それと今回、gemのインストール先として指定したパス"vendor/bundle"は色々なページでこの名前を使うように解説されていますが、bundlerの制約で決まっているものではないようです。
なので、任意のディレクトリ名でも大丈夫かと思います。
そのことは以下のページで解説されています。

- [teratail -- bundle install するときになぜ vendor/bundle に入れるのか](https://teratail.com/questions/94285)

色々と話が逸れましたが、最後にgemfile.lockファイルとgemがインストールされたか確認してみましょう。

gemfile.lockは```bundle install```を実行した時点で自動作成されています。

```:PowerShell
PS> cat xxx\practice\gemfile.lock  /*xxxはプロジェクトのディレクトリを作成したパス

GEM
  remote: https://rubygems.org/
  specs:
    faraday (0.14.0)
      multipart-post (>= 1.2, < 3)
    ffi (1.9.21-x64-mingw32)
    filetree (1.0.0)
    jruby-jars (1.7.27)
    jruby-parser (0.5.4)
    multipart-post (2.0.0)
    mustermann (1.0.1)
    rack (2.0.4)
    rack-protection (2.0.0)
      rack
    rsense (0.5.18)
      bundler (~> 1.6)
      filetree (~> 1.0.0)
      jruby-jars (~> 1.7.4)
      jruby-parser (~> 0.5.4)
      rsense-core (~> 0.6.6)
      rsense-server (~> 0.5.18)
      spoon (~> 0.0.4)
      thor (>= 0.18.1, < 0.20)
    rsense-core (0.6.6)
      filetree (~> 1.0.0)
      jruby-jars (~> 1.7.4)
      jruby-parser (~> 0.5.4)
      spoon (~> 0.0.4)
      thor (>= 0.18.1, < 0.20)
    rsense-server (0.5.18)
      bundler (~> 1.6)
      faraday
      filetree (~> 1.0.0)
      jruby-jars (~> 1.7.4)
      jruby-parser (~> 0.5.4)
      rsense-core (~> 0.6.6)
      sinatra
      spoon (~> 0.0.4)
    sinatra (2.0.0)
      mustermann (~> 1.0)
      rack (~> 2.0)
      rack-protection (= 2.0.0)
      tilt (~> 2.0)
    spoon (0.0.6)
      ffi
    thor (0.19.4)
    tilt (2.0.8)

PLATFORMS
  x64-mingw32

DEPENDENCIES
  rsense

BUNDLED WITH
   1.16.1
```

gemがインストールされたかの確認。

```:PowerShell
PS> bundle exec gem list

*** LOCAL GEMS ***

bundler (1.16.1)
faraday (0.14.0)
ffi (1.9.21 x64-mingw32)
filetree (1.0.0)
jruby-jars (1.7.27)
jruby-parser (0.5.4)
multipart-post (2.0.0)
mustermann (1.0.1)
rack (2.0.4)
rack-protection (2.0.0)
rsense (0.5.18)
rsense-core (0.6.6)
rsense-server (0.5.18)
sinatra (2.0.0)
spoon (0.0.6)
thor (0.19.4)
tilt (2.0.8)
```

"rsense"をインストールした私の環境では上記のようになりました。
```gem list```はインストールしたgemの一覧を表示するコマンドでしたが、そのままだとシステムの方の一覧を表示してしまうので、```bundle exec```を前につけることで現在のプロジェクトのgemの一覧を表示してくれます。

あれ?、gemの一覧はconfigファイルで記述された場所を見ているとして、じゃあ、プロジェクトがたくさんあってconfigファイルがたくさんある状態だったらどのconfigファイルを見ているんだよって感じですよね。
「現在のプロジェクト」だなんて曖昧な表現をしやがってと、つっこみの嵐だと思います。
以下のページの解説でconfigファイルを参照する順番があるようです。

- [bundle config](http://ruby.studio-kingdom.com/bundler/bundle_config/)

順番は以下。
//////////////////////////////////////////////////
1. ローカルのアプリケーション(app/.bundle/config)
2. 環境変数
3. ユーザーのホームディレクトリ(~/.bundle/config)
//////////////////////////////////////////////////

ですが、これはあくまで```bundle config```コマンドの話で、```bundle exec```系のコマンドにも当てはまるのかとは思います。
自分の環境で試してみた限りでは、```bundle exec```系のコマンドでもおそらく上記の順番でconfigファイルを参照しているだろうなーという程度の認識です。
間違っていたらごめんなさい。

以上で実践も終了です。

##+α
+αとしてgemfileとgemfile.lockの使われ方、それに関連して```bundle install```と```bundle update```コマンドの違いについて記述しておきます。
記述すると言っておきながら、解説は他の方の投稿やページを参照してくださいで終わってしまいますが…

gemfileとgemfile.lockの使われ方、それに関連して```bundle install```と```bundle update```コマンドの違いについては以下の方々の素晴らしい解説を参照してみてください。

- [bundle install と bundle updateの違いについて](https://qiita.com/lasershow/items/1a048d03ddaaba98171e)
- [ちょっと待った! Railsでgitリポジトリから除外すべきでないファイル:Gemfile.lockとdb/schema.rb](https://techracho.bpsinc.jp/hachi8833/2014_02_07/15390)
- [Ruby STUDIO -- bundle update](http://ruby.studio-kingdom.com/bundler/bundle_update/)
- [Ruby STUDIO -- Gemの更新](http://ruby.studio-kingdom.com/bundler/guides/updating_gems)

本当はこの素晴らしい解説内容を解釈してまとめようと思っていたのですが、頭の固い私にはgemの依存関係の解説がわかったようなわからないような感じで、まとめる前に力尽きました…
ここら辺のことをすっきり理解できた方がいたら、ご教授ください。

こんな終わり方でごめんなさい。

#修正履歴
##2019/01/10
**修正点**

- [用語について](#用語について)の図を修正
- RubyGemsの解説を修正
- bundlerの解説を修正

##2019/11/05
**修正点**

- [コマンドのミスを修正](https://qiita.com/3no3_tw/items/8c1e3e95c75edae1036d/patches/79591)

@itaya さん、ご指摘をありがとうございました。