railsの学習を始めて数ヶ月経ちましたが、環境構築について、写経のように何も理解できないまま行ってしまっていたので、各ツール、プログラムの全体での立ち位置を意識しながら、それぞれがどの立場で何をやっているのか、またその中で感じた疑問をまとめてみました。
初学者の方が環境構築するにあたって、今何をしているのかわかる地図のようになればいいなぁと思っております。
誤り等にお気づきの方はご連絡をお願いいたします。
■想定読者
①:複数回環境構築を行い、やり方はわかったが、何をしているのか分からない初学者の方々
②:まさに①である筆者の拙い記事を、温かい目で校正していただける先輩エンジニアの方々
全体イメージ
※あくまで環境構築の一例。
(例えば、rbenvがなくてもrubyのインストールはできるし、bundlerがなくてもgemのインストールは可能。)
イメージ図
概要
①Command Line Toolsによって、homebrewインストール、brewコマンドの使用ができるようにして、
②homebrewによって、MAC OSにインストールするソフトウェアのパッケージ管理をして、
③rbenvによって、rubyのインストールや、アプリごと・システム全体のrubyのバージョンについて、管理して、
④bundlerによって、各アプリで使用する、rubyの外部ライブラリ(をまとめたgemと呼ばれるパッケージ)をパッケージ管理する!
※パッケージ管理 :ソフトウェアの依存関係、互換性を管理(後述)
感想:大企業みたい。
①Command Line Tools for Xcodeとは
Xcodeとは
Apple社が開発・リリースしているソフトウェアです。MacやiPhone、iPad向けのアプリケーション開発に特化していて、デスクトップアプリやスマホアプリを制作する際には必須とも言えるほどメジャーな開発ツール。らしい。
Command Line Tools(CLT) for Xcodeとは?
Xcodeに付属している開発キットで、単体でインストールすることが可能。
コマンドラインとは、ターミナルのようなキーボードだけで操作する画面上の入力行のことことで、
CLTを入れることで、homebrewのインストールと、brewコマンドでの操作が可能になる。homebrewだけでなく、その他のソフトウェアにおいても使用されている。
# CommandLineToolsの格納場所
% xcode-select -p
/Library/Developer/CommandLineTool
【参考】
brew upgrade でのエラー対処からCommand Line Toolsについてまとめてみる|TechRacho by BPS株式会社
Git のインストール 〜Git をMacにインストールしよう〜 | バージョン管理システム入門(初心者向け)
XcodeとHomebrewについて - しゃちの備忘録
https://blog.codecamp.jp/what_is_xcode
CommandLineToolsとXcode - Qiita
コマンドラインとは|「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典
②homebrewとは
homebrewは、macOS上でソフトウェアの導入を単純化するパッケージ管理システムのひとつ。homebrewの他に、MacPorts、Finkというものがあるらしいが、主流はhomebrew。ちなみに、プログラミング言語はrubyで作成されている。
他のOSでhomebrewと同じ役割を果たしているものでは、Red Hat系LinuxだとRpm、Yum、Debian系Linuxだとdpkg、APTとなっている。
# homebrewでインストールしたソフトウェアの一覧
% brew list
==> Formulae
autoconf libnghttp2 rbenv
brotli libunistring readline
c-ares libuv redis
ca-certificates libyaml ruby
docbook m4 ruby-build
docbook-xsl mpdecimal shared-mime-info
gdbm node-build sqlite
gettext nodenv wget
glib openssl@1.1 xmlto
gnu-getopt pcre xz
icu4c pkg-config yarn
libffi pyenv
libidn2 python@3.9
==> Casks
chromedriver db-browser-for-sqlite
【参考】
Homebrew (パッケージ管理システム) - Wikipedia
パッケージ管理システム - Wikipedia
Homebrew使い方まとめ - Qiita
RubyをmacOSのマシンにインストールするための手順 | ポテパンスタイル
Homebrew
③Ruby・rbenvとは
前提
rubyとは
・プログラミング言語
rbenvとは
・バージョンの異なるrubyをまとめて管理するツール
・アプリケーションごとに適切なバージョンにいつでも切り替えられる
rubyはどこにあるの?(MACに標準でインストールされているRubyとrbenvで管理しているRubyってなんなの?)
→環境構築の際に、rbenvでバージョンを変えたのに、「ruby -v」でバージョンが変わらないということがあった。原因としては、以下の通り、MACにはデフォルトでrubyが入っており、そちらが優先して使用されるように設定されていたためであった(理由については後述)。
じゃあ標準装備のrubyを使えばいいじゃないかと思うが、アプリケーションごとにrubyのバージョンを切り替えるには、rbenvで管理する必要があるとのこと。
# whereは入っているコマンドを全て探す
% where ruby
/Users/sy.yun/.rbenv/shims/ruby # rbenvでインストールしたruby
/usr/bin/ruby # デフォルトでMACに入っているruby
# whichは今使用されているコマンドを探す
# 設定を変えたため、rbenvでインストールしたるrubyがアクティブになっている
% which ruby
/Users/sy.yun/.rbenv/shims/ruby # rbenvでインストールしたruby
【参考】
Rubyのバージョンが切り替わらない時の対処法! - Qiita
どっちのrubyを使うかはどう決めているの?
→PATH変数で上からrubyを探していき、先に見つかったコマンドを使っている!
「.zshrc」に、「PATH変数にrbenvのpathを追加する処理」を記載することで、MACの標準装備rubyの前にrbenvのrubyを読み込ませるようにしている。
# PATH変数を表示
% echo $PATH | sed -e 's/:/\n/g'
/Users/sy.yun/.nodenv/shims
/Users/sy.yun/.pyenv/shims
/Users/sy.yun/.rbenv/shims
~/.rbenv/shims
/usr/local/bin
/opt/homebrew/bin
/opt/homebrew/sbin
/usr/local/bin
/usr/bin
/bin
/usr/sbin
/sbin
# .zshrcの中身を表示
% cat .zshrc
export PATH="~/.rbenv/shims:/usr/local/bin:$PATH"
eval "$(rbenv init -)"
PATH変数って何?
絶対パス名でコマンドを入力すると、シェルはそのパス名を使ってコマンドを探す。コマンド名しか指定しないと、シェルは PATH変数で指定されているディレクトリの順でコマンドを探す。コンピュータ上のすべてのファイルからコマンドを探すのは時間がかかるため、このようにPATH変数を利用している。
.zshrcに書いてあるのは何?
export PATH="~/.rbenv/shims:/usr/local/bin:$PATH"
→PATH変数にパス設定をしている。
この書き方だと、もともとデフォルトで設定されていた$PATH変数の前に
「~/.rbenv/shims」「/usr/local/bin」がきて、その後にデフォルトのPATH変数という順になる。
eval "$(rbenv init -)"
# rbenv initを打つとこのように出力され、その中に「.zhrc」に上記を記載しろと指示が書いてある
% rbenv init
# Load rbenv automatically by appending
# the following to ~/.zshrc:
eval "$(rbenv init - zsh)"
# rbenv-initのコードに以下の記述があった
echo 'export PATH="'${RBENV_ROOT}'/shims:${PATH}"'
→.zshrcに「eval "$(rbenv init - zsh)"」を記載することによって、自動的にrbenvがshellに(?)読み込まれる。
また、PATH変数に記載されている「/Users/sy.yun/.rbenv/shims」は、このコマンドの中で、パスに追加されている模様。
なぜ直接PATH変数に追加せずに.zshrcに書くの?
環境変数はshellからexitすると消えるので、環境変数を永続化するためにはshellファイルを利用する。Bashというshellなら~/.bash_profileや~/.bashrc、zshなら~/.zshrcがshellファイルに当たる。このファイルの中にexportで環境変数を追加する処理を加えれば、環境変数を永続化できる。すなわち、Terminalを再び立ち上げた後もその変数を利用できる。
なぜ.zshrcで設定していないものもPATH変数に入っているの?
/etcファイル配下のシステム全体用の設定ファイルの中で、初期読み込みファイルがあり、shellを立ち上げた際に、デフォルトの内容がPATH変数に書き込まれている。(らしい)
【参考】
.zshenvと.zshrcの場所・違い・設定方法を解説します!
【PATHを通す】を理解する
環境変数PATHを消すだけなら復活するから大丈夫
XXXenv の初期化時のeval "$(XXXenv init-)"の意味
rbenv/rbenv-init at master · rbenv/rbenv
zshrcとzprofileについて - gallardo diary
反映されているバージョンはどうやって見るの?
→rbenv versionとruby -vで見れる。
# 「rbenv version」でどのように設定されたかの情報と共に、
# 現在アクティブになっているRubyのバージョンを表示する。
% rbenv version
2.5.1 (set by /Users/sy.yun/.rbenv/version)
# 「rbenv versions」でrbenvが把握している全てのRubyのバージョンの一覧を出力し、
# 現在アクティブな状態にあるバージョンの隣にはアスタリスク(*)を表示する。
% rbenv versions
system
* 2.5.1 (set by /Users/sy.yun/.rbenv/version)
2.6.3
2.6.4
2.6.5
2.6.6
2.7.4
3.0.2
3.0.4
# 「ruby -v」と「rbenv -version」が一致しない場合は、PATH変数がおかしい。
% ruby -v
ruby 2.5.1p57 (2018-03-29 revision 63029) [-darwin21]
【参考】
rbenv version | rbenv日本語リファレンス | Ruby STUDIO
rbenv versions | rbenv日本語リファレンス | Ruby STUDIO
まずはruby -versionで!Rubyのバージョン管理方法とは | ポテパンスタイル
バージョンはどうやって設定されているの?
こちらをほぼコピペさせていただきました。
rbenv | global と local と .ruby-version の微妙な関係 - Qiita
システム全体で設定されているglobalのバージョンと、プロジェクトごとに設定されるlocalのバージョンがあり、それぞれ以下の通り設定されている。
rvenv global x.x.x コマンド
全体で使用するrubyのバージョンを設定しており、「~/.rbenv/version」ファイル(このフォルダはユーザごとに1ファイルのみ)の内容を書き出している。
rvenv local x.x.x コマンド
プロジェクトごとに使用するrubyのバージョンを設定理ており、カレントディレクトリに 「.ruby-version」 ファイルを書き出している。
仕組みとしては、以下のようになっている。
rbenv は、まずカレントディレクトリの .ruby-version を見る。あればそれを使う。
もし無ければ、ひとつ上のディレクトリの .ruby-version を見る。あればそれを使う。
これをホームディレクトリ ~/ にたどりつくまで繰り返す。
ホームディレクトリまで辿っても .ruby-version がなければ、 ~/rbenv/version を見る。あればそれを使う。
つまり、ホームディレクトリに .ruby-version を作ってしまうと、global はどこからも参照されなくなる。
Railsアプリではいつrubyのバージョンを設定しているの?
→rails newをした際にactiveになっているバージョン(「rbenv version」で出てくるバージョン)で、rails newによって「 .ruby-version 」が一緒に作成される。
# ホームディレクトリ
% rbenv versions
system
* 2.5.1 (set by /Users/sy.yun/.rbenv/version)
2.6.3
2.6.4
2.6.5
2.6.6
2.7.4
3.0.2
3.0.4
# 別のプロジェクト配下にある、ローカルなバージョンを設定している「.ruby-version」ファイルの中身を表示
% cat /プロジェクト/.ruby-version
2.6.6%
# プロジェクト配下にcdで移動してから「rbenv versions」コマンドを使用
# 結果が変わる!
(プロジェクト配下)% rbenv versions
system
2.5.1
2.6.3
2.6.4
2.6.5
* 2.6.6 (set by /プロジェクト名/.ruby-version)
2.7.4
3.0.2
3.0.4
(M1mac向け)RUBY_CFLAGS=-DUSE_FFI_CLOSURE_ALLOCとは
M1macでインストールする際に、rbenvでインストール使用するとエラーが出て、調べると表題のコマンドを打つようにという記事がよくあるが、このオプションはなんなのか調べてみた。
以下を参考にしたが、正直、詳しいところまでは分からなかった(難しい。。)ので、あくまで予想の範囲。
https://github.com/rbenv/ruby-build/issues/1699
Building Ruby on arm64 macOS
まず、M1マックでオプションなしでrbenvインストールすると、以下のようなエラーが出力する。
closure.c:264:14: error: implicit declaration of function 'ffi_prep_closure' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
result = ffi_prep_closure(pcl, cif, callback, (void *)self);
上記は、コンパイルの中で、ffi_prep_closurという関数が無効であることを示している。
その関数は以下に記載がある。
fiddle/closure.c at 9be9eddf7a821e7ae45b8fd8ee1ecba78bacd4fe · ruby/fiddle
#if USE_FFI_CLOSURE_ALLOC
result = ffi_prep_closure_loc(pcl, cif, callback,
(void *)self, cl->code);
#else
result = ffi_prep_closure(pcl, cif, callback, (void *)self);
上記を前提に、以下のコマンドを見ていく。
RUBY_CFLAGS=-DUSE_FFI_CLOSURE_ALLOC
・CFLAGSとは??
「拡張ライブラリをコンパイルするときの C コンパイラのオプションや、 ヘッダファイルのディレクトリを指定する文字列」らしい。
バージョン:2.1.0 > クエリ:$CFLAGS | るりまサーチ (Ruby 2.1.0)
DUSE_FFI_CLOSURE_ALLOCは、おそらく、Don’t USE_FFI_CLOSURE_ALLOCではないかなと。。
で、USE_FFI_CLOSURE_ALLOCは、エラーの原因のffi_prep_closureが使われれる構文の中に使われている。。
じゃあ、FFI_CLOSURE_ALLOCとは何なのだ。
・FFIとは??
Rubyのgemではないかと予想。
ffi:「Ruby-FFI は、動的にリンクされたネイティブライブラリをプログラムで読み込み、その中で関数をバインドし、Ruby のコードからその関数を呼び出すための gem です。」らしい。
https://github.com/ffi/ffi
・CLOUSUREとは??
「関数閉包はプログラミング言語における関数オブジェクトの一種。」らしい。
なぜクロージャ(Closure)と言うのか? - Qiita
・ALLOCとは??
「記憶域の割り振り」らしい。
ALLOC (記憶域の割り振り)
・結論
→なんかよく分からんけど、rubyのバージョンによってはM1チップではFFI_CLOSURE_ALLOCを使えないため、そのままrbenvでインストールできないらしく、CFLAGSを書き換え、FFI_CLOSURE_ALLOCを使わないようにコンパイル時の情報を変えることで、インストールしている模様…!
なので、オプションなしでだめなら、おとなしく以下を試してみようと思います。
RUBY_CFLAGS=-DUSE_FFI_CLOSURE_ALLOC rbenv install ○.○.◯
【参考】
【Rails】M1チップ搭載MacでRuby on Railsの開発環境構築
GCCの最適化
M1 macでpod installができない。sudo gem install ffiをしても治らない(rbenvが原因) - Qiita
会社の支給PCがMacBook Pro M1なので、新しく開発環境を構築した話 - 食べチョク開発者ブログ
Building Ruby on arm64 macOS
clang で 64bit 整数から 32bit 整数への暗黙の型変換を警告する (-Wshorten-64-to-32) - yokaze.github.io
Makefile
④bundlerとは(gemとrailsとは)
前提
ライブラリとは
「汎用性の高い機能を他のプログラムで呼び出して使えるように部品化して集めたファイル」のこと。
以下の3つに分けられる。
種類 説明 インストール 読み込み
→requireすること標準ライブラリの組み込みライブラリ
例. Integer,String,ArrayなどRuby本体に組み込まれるライブラリ(標準ライブラリに属する) 不要 不要 標準ライブラリの組み込みではないライブラリ
例. DATE,JSONなどRubyインストール時にRuby本体としてインストールされるライブラリ 不要 必要 外部ライブラリ
例. sorcery,bootstrap,rails,bundlerなど有志の開発者が他の開発者も使えるように開発したライブラリ(※Rubyインストール時に一緒にインストールされるものもある) 必要 必要
(以下から引用して追記)
gemの仕組みを図解形式で学ぼう
gemとは(gemパッケージ/パッケージとは)
ライブラリはコマンドや自作関数、設定ファイル等のファイルでできており、
それを再配布しやすいように1つのgem形式のファイルにパッケージングしたRuby言語用の外部ライブラリのことを、gem、gemパッケージという。
ライブラリのイメージ
びっくりポイント→railsもbundlerもactiverecordもgem!
bundlerは何をしてくれるの?
こちらがめちゃくちゃ詳しく記載されています。そこから、全体感を知るために必要な部分のみ抜粋して、追記しています。
結局bundlerって何?bundlerの仕組みを図解形式で徹底解説
bundlerとは、gemをどのバージョンで使うか一元管理できるgemです。bundlerを使うとこんなことができます。
- bundlerを使ってgemをインストールするとgem同士の互換性を保ってくれるので、gemを追加したり時間が経ってもエラーを起こさず開発できる
- Gmfileという1つのファイルにgemを書くのでgemの管理がしやすい
- Gemfileを使ってアプリごとにgemを管理できる
- 環境ごとにインストールするgemを管理できる。
- gemの互換性を保ってくれるので、Gemfileさえ共有すれば複数人でアプリを開発してもエラーを起こさない
依存性、互換性とは?(①の補足)
gemfileでi18nのバージョンを指定すると、以下のエラーが出力
# gemfile
gem 'rails-i18n', '~> 7.0.0'
# bundle install
(プロジェクト配下)% bundle
Fetching gem metadata from https://rubygems.org/...........
Resolving dependencies...........................................................................................................................................................................................................................
(省略)
Bundler could not find compatible versions for gem "railties":
In Gemfile:
coffee-rails (~> 4.2) was resolved to 4.2.2, which depends on
railties (>= 4.0.0)
jquery-rails was resolved to 4.5.0, which depends on
railties (>= 4.2)
rails (~> 5.2.3) was resolved to 5.2.4.rc1, which depends on
railties (= 5.2.4.rc1)
rails-i18n (~> 7.0.0) was resolved to 7.0.5, which depends on
railties (< 8, >= 6.0.0)
sass-rails (~> 5.0) was resolved to 5.1.0, which depends on
railties (>= 5.2.0)
web-console (>= 3.3.0) was resolved to 3.7.0, which depends on
railties (>= 5.0)
(省略)
# 和訳
Bundler は gem "railties" の互換性のあるバージョンを見つけられませんでした。
Gemfile で
coffee-rails (~> 4.2) は 4.2.2 に解決され、これは以下のものに依存しています。
railties (>= 4.0.0) に依存(depends on)しています。
jquery-rails は 4.5.0 に解決され、railties (>= 4.0.0) に依存するようになりました。
railties (>= 4.2) に依存しています。
rails (~> 5.2.3) は 5.2.4.rc1 に解決され、以下のものに依存します。
railties (= 5.2.4.rc1) に依存します。
rails-i18n (~> 7.0.0) は 7.0.5 に解決され、以下のものに依存するようになりました。
railties (< 8, >= 6.0.0) に依存しています。
sass-rails (~> 5.0) は 5.1.0 に解決され、以下のものに依存します。
railties (>= 5.2.0) に解決されました。
web-console (>= 3.3.0) は 3.7.0 に解決され、以下のものに依存しています。
railties (>= 5.0) に解決されました。
bundle installでi18nのバージョンを指定しないとうまくいった!
gem 'rails-i18n'
# コマンドの結果
Bundle complete! 25 Gemfile dependencies, 109 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
⇨つまり、「rails-i18n」のバージョンを7.0.0以上を指定した際に、railtiesのバージョン6.0.0以上8未満 (< 8, >= 6.0.0) を必要するため、Gemfileには記載していない、rails-i18nを動かすために必要な(依存(compatible)している)ralitiesGemをインストールしようとしてくれる。
また、その他のGemでもralitiesを必要としているGemがあるため、その中で、すべてのGemの要望(依存)に応えるgem(互換性のあるgem)をbundlerが探してくれる!
ただし、今回の「rails-i18n」のバージョンを7.0.0以上を指定した場合は、rails-i18nが依存している条件が、他のGemfileに記載しているgemが依存している条件と合わなかったため、エラーとなった。
そのため、i18nのバージョン指定を消したところ、i18n以外が希望する条件でも使用できる(互換性のある)rails-i18nのバージョン(Gemfile.lockを確認するとrails-i18n (5.1.3)となっていた)を取り込んでくれた!
バージョン指定(①の補足)
以下のようにバージョンを指定すると、依存性、互換性を判断して、指定した範囲でよしなに設定してくれる。
gem 'Gem名', 'バージョン', 'オプション'
指定方法 | 説明 |
---|---|
'1.0.0' | バージョンを固定 |
'>= 1.0.0' | 指定した以降のバージョン |
'>= 1.0.0' , '< 2.0.0' | 指定した範囲のバージョン |
'~> 1.0.0' | 指定したバージョン以降で、マイナーバージョンが変わらない範囲 |
'~> 1.0' | 指定したバージョン以降で、メジャーバージョンが変わらない範囲 |
指定なし | 依存性を解決した上で、互換性のある最新のバージョン |
環境ごととは(④の補足)
以下のように環境ごと(開発環境、試験環境、商用環境)に管理している。
# Gemfile
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
ruby '2.5.1'
# 特段groupで囲われていない上部は、開発環境、試験環境、商用環境で使用
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 5.2.3'
# 途中省略
group :development, :test do # 開発環境と試験環境で使用
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
end
group :development do # 開発環境で使用
# Access an interactive console on exception pages or by calling 'console' anywhere in the code.
gem 'web-console', '>= 3.3.0'
end
group :test do # 試験環境で使用
# Adds support for Capybara system testing and selenium driver
gem 'capybara', '>= 2.15'
end
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
bundlerもgemならそのbundlerはどうやってインストールするの?
→標準&組み込みライブラリのRubyGemsでインストールする。「gem install gem名」はRubyGemsのメソッド!
たまに「bundle install --path vendor/bundle」としているのはなんで?
指定しないと、以下のようにrbenvのバージョンのA.B.CのA.B(マイナーバージョン)にgemはまとめて格納される。この時に、一つのアプリだけでなく、rubyのマイナーバージョンが同じアプリのgemが全て格納される。
指定すると、railsアプリの直下のvendor/bundleディレクトリにインストールされ、railsアプリケーションの中だけにインストールされるようになる。
以下の記事にオプションをつけるメリット、デメリットの記載があったが、個人的にはすごく「オプション」なものだと感じた。どっちでも良さげ。
bundle install時に--path vendor/bundleを付ける必要性は本当にあるのか、もう一度よく考えてみよう - Qiita
指定しない場合の格納先例: .rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems)
% pwd
/Users/sy.yun/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems
% ls
actioncable-5.2.6 msgpack-1.5.2
actioncable-5.2.7.1 msgpack-1.5.3
actioncable-5.2.8 multi_json-1.15.0
actionmailer-5.2.6 multi_xml-0.6.0
actionmailer-5.2.7.1 multipart-post-2.2.3
actionmailer-5.2.8 net-telnet-0.1.1
actionpack-5.2.6 nio4r-2.5.8
actionpack-5.2.7.1 nokogiri-1.12.5
actionpack-5.2.8 oauth-0.5.10
actionview-5.2.6 oauth2-1.4.10
(以下省略)
railsってなんなの?
Rubyを使用したフレームワークであり、rubyのgem!gemであるrailsが、gemであるactiverecord等、複数のgemを使用している。
(フレームワークとしての詳細は割愛。あくまでも環境構築という中でのrailsの立場の話。)
フレームワークとライブラリの違いは以下の記事が分かりやすかった。(特に図1)
Part1 ソフトウエアのフレームワークとはなにか
→railsはrubyのライブラリであり、railsアプリの設計レベルの骨組みであるを提供してくれるフレームワークでもあると勝手に理解しました。
railsのコマンドを打つ時に、「rails」とか「bundle exec rails」とか「bin/rails」など、使い分けられているのはなんで?
ちょっと複雑でしたが、以下を参考にまとめてみました。(難しかったので、信頼度は低です。。)
Rails 4.1以降のコンソールコマンドは必ず bin/ を付けなきゃいけないの? - Qiita
いまさらながら bundle exec ってなんだっけ?
rubygems/bundle-exec.1.ronn at 96e5cff3df491c4d943186804c6d03b57fcb459b · rubygems/rubygems
コマンドの投入方法として3つあり、かつ、railsコマンドとその他(rake、rspec)でも挙動が異なる模様(後述)。
・bundle exec →bundlerのコマンドでGemfileを使って、記載のバージョンの実行ファイルを呼び出す。
・bin/ →rails newで作成されるフォルダで、フォルダ配下の実行ファイルを呼び出す
・無印 →PTAH変数をもとに実行ファイルを呼び出す
挙動を考える際にポイントは以下の2つ。
・実行されるバージョン
→Gemfileで指定したバージョンが実行されるか、もしくはシステム共通の最新のバージョンのものが実行されるか。かつ、実行されるコマンドの実体が異なる(※)。
※上述の「bundle install --path vendor/bundle」でオプションをつけてインストールしたもの(アプリ直下と記載)か、オプションなしでインストールしたもの(システム共通と記載)か。
・springの起動有無
→バックグラウンドでRailsサーバーが動いている状態になるかを制御して、コマンドが素早く起動するかが変わる
上記をもとに簡単に表にまとめてみた。
bundle exec | bin/ | 無印 | |
---|---|---|---|
rails | バージョン指定:○ →アプリ直下orシステム共通のGemfileで指定したrailsからbin/railsが呼ばれて実行。 spring→○ |
バージョン指定:○ →bin/railsが呼ばれて実行。 spring→○ |
バージョン指定:△ (アプリ直下にしかrailsがない場合はエラーになる。) →システム共通のrailsからbin/railsが呼ばれて実行。 spring→○ |
rails以外(rake,rspec等) | バージョン指定:○ →アプリ直下orシステム共通のGemfileで指定したバージョンを実行。 spring→× |
バージョン指定:○ →binディレクトリのコマンドを実行。 spring→○ |
バージョン指定:× →システム共通の最新のバージョンを実行。 spring→× |
railsコマンドとその他(rake、rspec)でも挙動が異なる模様。
railsをロードする際に、bin/railsを優先的に使用するように設定されている模様。(ちゃんとコードの流れが読めていないので、理解できたらより詳しく加筆します。。)
そして、bin/rails の中で、springを使うようになっている。
だから、railsでrbenv管理のrailsを呼んでも、bundle execでbundler管理のrailsから読み込んでも、bin配下のrailsの実行可能ファイルが起動されるようになっている。
https://github.com/rails/rails/blob/4-2-stable/railties/lib/rails/app_rails_loader.rb
# rails/railties/lib/rails/app_rails_loader.rb
# bin/railsを優先的に使用するように設定されているっぽい
def exec_app_rails
original_cwd = Dir.pwd
loop do
if exe = find_executable
contents = File.read(exe)
if contents =~ /(APP|ENGINE)_PATH/
exec RUBY, exe, *ARGV
break # non reachable, hack to be able to stub exec in the test suite
elsif exe.end_with?('bin/rails') && contents.include?('This file was generated by Bundler')
$stderr.puts(BUNDLER_WARNING)
Object.const_set(:APP_PATH, File.expand_path('config/application', Dir.pwd))
require File.expand_path('../boot', APP_PATH)
require 'rails/commands'
break
end
end
# If we exhaust the search there is no executable, this could be a
# call to generate a new application, so restore the original cwd.
Dir.chdir(original_cwd) and return if Pathname.new(Dir.pwd).root?
# Otherwise keep moving upwards in search of an executable.
Dir.chdir('..')
end
end
# rails newするとできる、bin/railsのファイルの中身
#!/usr/bin/env ruby
begin
load File.expand_path('../spring', __FILE__) # springを読むようになっている。
rescue LoadError => e
raise unless e.message.include?('spring')
end
APP_PATH = File.expand_path('../config/application', __dir__)
require_relative '../config/boot'
require 'rails/commands'
⇨結論!railsは無印 or bin/。 他はbin/つけておけば良さそう。
ちなみにrailsコマンドの場所を検索してみた。
無印だとこのシステム共通のrailsが呼ばれる。
(プロジェクト配下) % where rails
/Users/sy.yun/.rbenv/shims/rails # rbenvでrubyにインストールしたシステム共通のrails
/usr/bin/rails # Mac本体のrails
【参考】
結局bundlerって何?bundlerの仕組みを図解形式で徹底解説
Gemfileにバージョンを指定したり、しなかったり。Gemfileについて - Qiita
Ruby on Railsとは?基礎知識と特徴をわかりやすく解説|発注成功のための知識が身に付く【発注ラウンジ】
https://qiita.com/jnchito/items/c5a0848144203dce6e26
railsコマンドを実行するときにbundle execをつけた場合とつけない場合の挙動について
gemでインストールしたrailsコマンドがなぜ使えるのか?? - Qiita
いまさらながら bundle exec ってなんだっけ?
https://railsguides.jp/initialization.html
⑤全て踏まえてどういった順番でrailsの環境構築をすれば良いか
- (初回のみ)command line toolのインストール
- (初回のみ)homebrewのインストール
- (初回のみ)rbenvのインストール
- (必要な時のみ)rbenvで使用するrubyのインストール
- ディレクトリを移動して「rbenv local」or「rbenv global」コマンドでアプリで使用するrubyのバージョンを指定
- bundler initをしてGemfileを作成
- Gemfileにrailsを記載(必要に応じてバージョン指定)
- bundle install --path vendor/bundle
- rails new(必要に応じてバージョン指定)
- 作成したrailsプロジェクト配下に新しくGemfileができるため、以降はこちらでgemを管理する。(railsを記載したgemファイルはもう編集しない)
【参考】
新規Railsプロジェクトの作成手順まとめ - Qiita
長文、お付き合いいただきありがとうございました…!