20200305のRailsに関する記事は23件です。

もう悩まない!MacでRails環境構築する手順の全て

この記事は

初心者でも迷わず環境構築ができるよう、1つ1つの手順を丁寧に解説した記事です。
コマンドが羅列された記事はよく見かけますが、結局コマンドの意味がわからず理解が深まらないことが多々あったので、なるべくコマンドの解説もつけるようにしました。

「環境構築が苦手、よくわからない」という方は、ぜひ参考にしていただければと思います。
また「railsのバージョン指定がうまくいかない...」と悩んでいる方にも参考になると思います。

マシンスペック

参考までに、私のマシンスペックは以下の通りです。

  • macOS Catalina 10.15.3
  • MacBook Air (Retina, 13-inch, 2018)
  • Intel Core i5
  • メモリ 8GB

Ruby, Railsのバージョン

それぞれ下記バージョンで構築することを目指します。

  • Ruby 2.5.7
  • Rails 5.2.3

全体の流れ

この順番でインストールしていきます。

  1. rbenv(Rubyのバージョン管理ツール)
  2. Ruby
  3. PostgreSQL(データベース)
  4. Rails

構築手順

1. rbenvのインストール

rbenvとは、Rubyのバージョンを簡単に切り替えてくれるツールです。

1台のPCで複数のRailsアプリを作ろうとしたとき、「こっちのアプリではRuby2.5.0を使いたいけど、こっちはRuby2.6.0を使いたい...」という欲求が出てきたりします。
そんなときに活躍するのがrbenvです。
詳しい使い方は後で見ていくとして、まずはインストールしてしまいましょう。

rbenvをインストールするためにはHomebrewが必要で、HomebrewをインストールするためにはCommand Line Toolsが必要です。
順番に進めていきます。

1-1. Command Line Toolsのインストール

ターミナルを開き、次のコマンドを実行します。

# Command Line Toolsのインストール
$ xcode-select --install

Xcodeというツールをインストールしていない場合は「インストールしますか?」という画面が表示されるため、「インストール」を選択します。
Xcodeは重いので、少し時間がかかるかもしれません。

インストール完了後、次のコマンドでバージョンが表示されたら成功です。

# バージョン確認
$ xcodebuild -version

1-2. Homebrewのインストール

続いてHomebrewをインストールします。
Homebrewの公式サイトに飛ぶとインストール用のコマンドが用意されてるので、それをターミナルに貼り付けて実行するだけです。
コマンドは随時アップデートされたりするので、直接サイトから持ってくるのが賢明です。

ちなみにHomebrewとは、Macにデフォルトで入っていないソフトをインストールするためのツールです。
上の画像を見ると、「Appleが用意していないあなたの必要なものをインストールします」と書いてありますが、噛み砕くとそういうことです。
Homebrewを入れておくといろいろインストールできて便利なんだな、と理解しておけば大丈夫です。

1-3. rbenvのインストール

いよいよrbenvのインストールを行います。
ターミナルで次の3つのコマンドを順番に実行してください。

# (1) rbenvを初期化する処理を設定
$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile

# (2) 初期化設定の反映
$ source ~/.bash_profile

# (3) rbenvのインストール
$ brew install rbenv ruby-build

(3)では、rbenvと一緒に、rbenvのプラグインであるruby-buildもインストールしています。
rbenvについて詳しく知りたい方は、こちらのブログで詳しく紹介されていますので読んでみてください(ただ、初学者には難易度高めです)。

このrbenvを使って、Rubyをインストールしていきます!

2. Rubyのインストールとバージョン適用

2-1. Rubyのインストール

rbenvでインストールできるバージョンは下記コマンドで確認できます。

# rbenvでインストールできるバージョンを確認
$ rbenv install --list
#=> Available versions:
#   1.8.5-p52
#   1.8.5-p113
#   ...
#   truffleruby-19.2.0
#   truffleruby-19.2.0.1

今回は2.5.7をインストールします。

# Ruby2.5.7をインストール
$ rbenv install 2.5.7

これでRuby2.5.7がインストールされたはずです。
次のコマンドを打ってみましょう。

# インストール済みのRubyのバージョン一覧を表示する
$ rbenv versions
#=> * system (set by /Users/<ユーザ名>/.rbenv/version)
#     2.5.7

これは、system(Macのデフォルト)と2.5.7の2種類のRubyバージョンがPCにインストール済みであることを示しています。
2.5.7もインストールできたことがわかりますね。
ちなみに「*」がついている方が、現在の環境に適用されているバージョンです。

2-2. 作業ディレクトリの作成&移動

続けて作業ディレクトリ(フォルダ)を作成します。
作業ディレクトリの名前は「sample_app」や「workspace」などがよくありますが、作成するアプリ名にするとわかりやすいでしょう。
ここでは私が作成したアプリ名「cooklog」とします。

作業ディレクトを作成します。

# 作業ディレクトリを作成
$ mkdir ~/cooklog

ここまでは「ターミナルにおいてどのフォルダでコマンドを打つか」は気にしなくてよかったですが、ここからは作業フォルダに入ってコマンドを打っていくことになります。
そこで、ディレクトリへの移動を行います。

# 作業ディレクトリへ移動(適宜パスを指定)
$ cd cooklog

2-3. Rubyのバージョンを適用

それでは作業ディレクトリに対してRuby2.5.7を適用します。
先ほど作ったディレクトリにいることを確認して、次のコマンドを実行します。

# 現在のディレクトリにRuby2.5.7を適用
$ rbenv local 2.5.7

# 適用を反映
$ rbenv rehash

これで作業ディレクトリに適用するRubyのバージョンが変わったはずです。確認してみましょう。

$ ruby -v
#=> ruby 2.5.7p206 (2019-10-01 revision 67816) [x86_64-darwin19]

$ rbenv versions
#=>   system (set by /Users/<ユーザ名>/.rbenv/version)
#   * 2.5.7

Rubyのバージョンは、p206..以降が違っても問題ありません。
「*」2.5.7の方に移り、適用バージョンが変わったことを確認できました。

※ちなみにrbenv local x.x.xが現在のディレクトリのRubyバージョンを変更するのに対し、rbenv global x.x.xは使っているPCでデフォルトで使うRubyバージョンを指定します。
したがって、globalの方を使ってもOKです(今回は他のディレクトリへ影響することを避けるため、localにしています)。

2-4. bundlerのインストール

2章の最後に、gemのインストールには欠かせないbundlerをインストールしておきます。
次のコマンドを実行してください。

$ gem install bundler

3. PostgreSQLのインストール

続いてデータベースのインストールを行います。
今回はPostgreSQLを使うことにします。

次のコマンドを打ち込んで、インストールと起動を一気にやってしまいましょう。

# PostgreSQLのインストール
$ brew install postgresql

# PostgreSQLの起動
$ brew services start postgresql
==> Successfully started `postgresql` (label: homebrew.mxcl.postgresql)

4. Railsのインストール

ここからいよいよRailsをインストールしていきます。

まずはバージョンは気にせず、Railsをインストールします。

$ gem install rails

続けて、作成するアプリのバージョンは5.2.3とするので、次のコマンドを打ち込んでください。

# Railsアプリ作成(gemのインストールはスキップ)
$ rails _5.2.3_ new . --skip-bundle

途中、何か聞かれたら全てy(yesの意味)を打ってEnterを押せばOKです。
.はカレント(現在の)ディレクトリという意味です。

--skip-bundleの部分がポイントで、これをつけるとgemのインストールは後回しにしてRailsアプリに必要なファイルだけを先に作ってくれます。

次に作られたファイルの中のGemfileを開き、次のように書き換えます。

Gemfile
# 変更前
gem 'rails', '~> 5.2.3'
# 変更後
gem 'rails', '= 5.2.3'

'~> 5.2.3'は「バージョン5.2.3以上のものをインストールします」という意味なので、確実に5.2.3をインストールするためにこの変更を行います。

そして、次のコマンドを実行して各種gemをインストールします。

# 各種gemをインストール
$ bundle install

Railsのバージョンを確認し、きちんとインストールできていることを確認しましょう。

$ rails -v
#=> 5.2.3

最後に、Railsアプリを立ち上げるためにRailsサーバーを起動します。

$ rails s
=> Booting Puma
=> Rails 5.2.3 application starting in development 
...(省略)...
* Listening on tcp://localhost:3000
Use Ctrl-C to stop

上の感じになったらOKです。
http://localhost:3000/にアクセスしてみましょう。

スクリーンショット 2020-03-05 22.32.22.png

この画面が表示されれば、きちんとRailsアプリが立ち上がっています!
Rails 5.2.3Ruby 2.5.7が適用されていることがわかりますね。

これにて環境構築はおしまいです。

お疲れ様でした!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【完全版】MacでRails環境構築する手順の全て

この記事は

初心者でも迷わず環境構築ができるよう、1つ1つの手順を丁寧に解説した記事です。
なるべく「どうしてこのコマンドを実行するのか」も解説するようにしています。

「環境構築が苦手、よくわからない」という方は、ぜひ参考にしていただければと思います。
また「railsのバージョン指定がうまくいかない...」と悩んでいる方にも参考になると思います。

※もしこれを読んでもうまくいかなければ、ぜひ積極的にコメントいただけますと幸いです!

マシンスペック

参考までに、私のマシンスペックは以下の通りです。

  • macOS Catalina 10.15.3
  • MacBook Air (Retina, 13-inch, 2018)
  • Intel Core i5
  • メモリ 8GB

Ruby, Railsのバージョン

それぞれ下記バージョンで構築することを目指します。

  • Ruby 2.5.7
  • Rails 5.2.3

全体の流れ

この順番でインストールしていきます。

  1. rbenv(Rubyのバージョン管理ツール)
  2. Ruby
  3. PostgreSQL(データベース)
  4. Rails

構築手順

1. rbenvのインストール

rbenvとは、Rubyのバージョンを簡単に切り替えてくれるツールです。

1台のPCで複数のRailsアプリを作ろうとしたとき、「こっちのアプリではRuby2.5.0を使いたいけど、こっちはRuby2.6.0を使いたい...」という欲求が出てきたりします。
そんなときに活躍するのがrbenvです。
詳しい使い方は後で見ていくとして、まずはインストールしてしまいましょう。

rbenvをインストールするためにはHomebrewが必要で、HomebrewをインストールするためにはCommand Line Toolsが必要です。
順番に進めていきます。

1-1. Command Line Toolsのインストール

ターミナルを開き、次のコマンドを実行します。

# Command Line Toolsのインストール
$ xcode-select --install

Xcodeというツールをインストールしていない場合は「インストールしますか?」という画面が表示されるため、「インストール」を選択します。
Xcodeは重いので、少し時間がかかるかもしれません。

インストール完了後、次のコマンドでバージョンが表示されたら成功です。

# バージョン確認
$ xcodebuild -version

1-2. Homebrewのインストール

続いてHomebrewをインストールします。
Homebrewの公式サイトに飛ぶとインストール用のコマンドが用意されてるので、それをターミナルに貼り付けて実行するだけです。
コマンドは随時アップデートされたりするので、直接サイトから持ってくるのが賢明です。

ちなみにHomebrewとは、Macにデフォルトで入っていないソフトをインストールするためのツールです。
上の画像を見ると、「Appleが用意していないあなたの必要なものをインストールします」と書いてありますが、噛み砕くとそういうことです。
Homebrewを入れておくといろいろインストールできて便利なんだな、と理解しておけば大丈夫です。

1-3. rbenvのインストール

いよいよrbenvのインストールを行います。
ターミナルで次の4つのコマンドを順番に実行してください。

# (1) rbenvへのPATHを通す
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc

# (2) rbenvを使うために必要な「rbenv init -」コマンドを設定
$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile

# (3) 設定の反映
$ source ~/.bash_profile

# (4) rbenvのインストール
$ brew install rbenv ruby-build

(4)では、rbenvと一緒に、rbenvのプラグインであるruby-buildもインストールしています。
rbenvについて詳しく知りたい方は、こちらのブログで詳しく紹介されていますので読んでみてください(ただ、初学者には難易度高めです)。

このrbenvを使って、Rubyをインストールしていきます!

2. Rubyのインストールとバージョン適用

2-1. Rubyのインストール

rbenvでインストールできるバージョンは下記コマンドで確認できます。

# rbenvでインストールできるバージョンを確認
$ rbenv install --list
#=> Available versions:
#   1.8.5-p52
#   1.8.5-p113
#   ...
#   truffleruby-19.2.0
#   truffleruby-19.2.0.1

今回は2.5.7をインストールします。

# Ruby2.5.7をインストール
$ rbenv install 2.5.7

これでRuby2.5.7がインストールされたはずです。
次のコマンドを打ってみましょう。

# インストール済みのRubyのバージョン一覧を表示する
$ rbenv versions
#=> * system (set by /Users/<ユーザ名>/.rbenv/version)
#     2.5.7

これは、system(Macのデフォルト)と2.5.7の2種類のRubyバージョンがPCにインストール済みであることを示しています。
2.5.7もインストールできたことがわかりますね。
ちなみに「*」がついている方が、現在の環境に適用されているバージョンです。

2-2. 作業ディレクトリの作成&移動

続けて作業ディレクトリ(フォルダ)を作成します。
作業ディレクトリの名前は「sample_app」や「workspace」などがよくありますが、作成するアプリ名にするとわかりやすいでしょう。
ここでは私が作成したアプリ名「cooklog」とします。

作業ディレクトを作成します。

# 作業ディレクトリを作成
$ mkdir ~/cooklog

ここまでは「ターミナルにおいてどのフォルダでコマンドを打つか」は気にしなくてよかったですが、ここからは作業フォルダに入ってコマンドを打っていくことになります。
そこで、ディレクトリへの移動を行います。

# 作業ディレクトリへ移動(適宜パスを指定)
$ cd cooklog

2-3. Rubyのバージョンを適用

それでは作業ディレクトリに対してRuby2.5.7を適用します。
先ほど作ったディレクトリにいることを確認して、次のコマンドを実行します。

# 現在のディレクトリにRuby2.5.7を適用
$ rbenv local 2.5.7

# 適用を反映
$ rbenv rehash

これで作業ディレクトリに適用するRubyのバージョンが変わったはずです。確認してみましょう。

$ ruby -v
#=> ruby 2.5.7p206 (2019-10-01 revision 67816) [x86_64-darwin19]

$ rbenv versions
#=>   system (set by /Users/<ユーザ名>/.rbenv/version)
#   * 2.5.7

Rubyのバージョンは、p206..以降が違っても問題ありません。
「*」2.5.7の方に移り、適用バージョンが変わったことを確認できました。

※ちなみにrbenv local x.x.xが現在のディレクトリのRubyバージョンを変更するのに対し、rbenv global x.x.xは使っているPCでデフォルトで使うRubyバージョンを指定します。
したがって、globalの方を使ってもOKです(今回は他のディレクトリへ影響することを避けるため、localにしています)。

2-4. bundlerのインストール

2章の最後に、gemのインストールには欠かせないbundlerをインストールしておきます。

まずはターミナルでbundler -vを実行して、bundlerがインストールされているか確認してみてください。

$ bundler -v
#=> Bundler version 1.17.2

上のようにbundlerのバージョンが表示されれば問題ありません。
「not found」と出るのであれば、次のコマンドでインストールしてください。

$ gem install bundler

3. PostgreSQLのインストール

続いてデータベースのインストールを行います。
今回はPostgreSQLを使うことにします。

次のコマンドを打ち込んで、インストールと起動を一気にやってしまいましょう。

# PostgreSQLのインストール
$ brew install postgresql

# PostgreSQLの起動
$ brew services start postgresql
==> Successfully started `postgresql` (label: homebrew.mxcl.postgresql)

4. Railsのインストール

ここからいよいよRailsをインストールしていきます。

まずはバージョンは気にせず、RailsをPC内にインストールします。

$ gem install rails

続けて、作成するアプリのバージョンは5.2.3とするので、次のコマンドを打ち込んでください。

# Railsアプリ作成(gemのインストールはスキップ)
$ rails _5.2.3_ new . --skip-bundle

途中、何か聞かれたら全てy(yesの意味)を打ってEnterを押せばOKです。
.はカレント(現在の)ディレクトリという意味です。

--skip-bundleの部分がポイントで、これをつけるとgemのインストールは後回しにしてRailsアプリに必要なファイルだけを先に作ってくれます。

次に作られたファイルの中のGemfileを開き、次のように書き換えます。

Gemfile
# 変更前
gem 'rails', '~> 5.2.3'
# 変更後
gem 'rails', '= 5.2.3'

'~> 5.2.3'は「バージョン5.2.3以上のものをインストールします」という意味なので、確実に5.2.3をインストールするためにこの変更を行います。

そして、次のコマンドを実行して各種gemをインストールします。

# 各種gemをインストール
$ bundle install

Railsのバージョンを確認し、きちんとインストールできていることを確認しましょう。

$ rails -v
#=> 5.2.3

最後に、Railsアプリを立ち上げるためにRailsサーバーを起動します。

$ rails s
=> Booting Puma
=> Rails 5.2.3 application starting in development 
...(省略)...
* Listening on tcp://localhost:3000
Use Ctrl-C to stop

上の感じになったらOKです。
http://localhost:3000/にアクセスしてみましょう。

スクリーンショット 2020-03-05 22.32.22.png

この画面が表示されれば、きちんとRailsアプリが立ち上がっています!
Rails 5.2.3Ruby 2.5.7が適用されていることがわかりますね。

これにて環境構築はおしまいです。

お疲れ様でした!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【これで完璧!】MacでRails環境構築する手順の全て

この記事は

初心者でも迷わず環境構築ができるよう、1つ1つの手順を丁寧に解説した記事です。
コマンドが羅列された記事はよく見かけますが、結局コマンドの意味がわからず理解が深まらないことが多々あったので、なるべくコマンドの解説もつけるようにしました。

「環境構築が苦手、よくわからない」という方は、ぜひ参考にしていただければと思います。

マシンスペック

参考までに、私のマシンスペックは以下の通りです。

  • macOS Catalina 10.15.3
  • MacBook Air (Retina, 13-inch, 2018)
  • Intel Core i5
  • メモリ 8GB

Ruby, Railsのバージョン

それぞれ下記バージョンで構築することを目指します。

  • Ruby 2.5.7
  • Rails 5.2.3

全体の流れ

この順番でインストールしていきます。

  1. rbenv(Rubyのバージョン管理ツール)
  2. Ruby
  3. PostgreSQL(データベース)
  4. Rails

構築手順

1. rbenvのインストール

rbenvとは、Rubyのバージョンを簡単に切り替えてくれるツールです。

1台のPCで複数のRailsアプリを作ろうとしたとき、「こっちのアプリではRuby2.5.0を使いたいけど、こっちはRuby2.6.0を使いたい...」という欲求が出てきたりします。
そんなときに活躍するのがrbenvです。
詳しい使い方は後で見ていくとして、まずはインストールしてしまいましょう。

rbenvをインストールするためにはHomebrewが必要で、HomebrewをインストールするためにはCommand Line Toolsが必要です。
順番に進めていきます。

1-1. Command Line Toolsのインストール

ターミナルを開き、次のコマンドを実行します。

# Command Line Toolsのインストール
$ xcode-select --install

Xcodeというツールをインストールしていない場合は「インストールしますか?」という画面が表示されるため、「インストール」を選択します。
Xcodeは重いので、少し時間がかかるかもしれません。

インストール完了後、次のコマンドでバージョンが表示されたら成功です。

# バージョン確認
$ xcodebuild -version

1-2. Homebrewインストール

続いてHomebrewをインストールします。
Homebrewの公式サイトに飛ぶとインストール用のコマンドが用意されてるので、それをターミナルに貼り付けて実行するだけです。
コマンドは随時アップデートされたりするので、直接サイトから持ってくるのが賢明です。

ちなみにHomebrewとは、Macにデフォルトで入っていないソフトをインストールするためのツールです。
上の画像を見ると、「Appleが用意していないあなたの必要なものをインストールします」と書いてありますが、噛み砕くとそういうことです。
Homebrewを入れておくといろいろインストールできて便利なんだな、と理解しておけば大丈夫です。

1-3. rbenvインストール

いよいよrbenvのインストールを行います。
ターミナルで次の3つのコマンドを順番に実行してください。

# (1) rbenvを初期化する処理を設定
$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile

# (2) 初期化設定の反映
$ source ~/.bash_profile

# (3) rbenvのインストール
$ brew install rbenv ruby-build

(3)では、rbenvと一緒に、rbenvのプラグインであるruby-buildもインストールしています。
rbenvについて詳しく知りたい方は、こちらのブログで詳しく紹介されていますので読んでみてください(ただ、初学者には難易度高めです)。

このrbenvを使って、Rubyをインストールしていきます!

2. Rubyのインストールとバージョン適用

2-1. Rubyのインストール

rbenvでインストールできるバージョンは下記コマンドで確認できます。

# rbenvでインストールできるバージョンを確認
$ rbenv install --list
#=> Available versions:
#   1.8.5-p52
#   1.8.5-p113
#   ...
#   truffleruby-19.2.0
#   truffleruby-19.2.0.1

今回は2.5.7をインストールします。

# Ruby2.5.7をインストール
$ rbenv install 2.5.7

これでRuby2.5.7がインストールされたはずです。
次のコマンドを打ってみましょう。

# インストール済みのRubyのバージョン一覧を表示する
$ rbenv versions
#=> * system (set by /Users/<ユーザ名>/.rbenv/version)
#     2.5.7

これは、system(Macのデフォルト)と2.5.7の2種類のRubyバージョンがPCにインストール済みであることを示しています。
2.5.7もインストールできたことがわかりますね。
ちなみに「*」がついている方が、現在の環境に適用されているバージョンです。

2-2. 作業ディレクトリの作成&移動

続けて作業ディレクトリ(フォルダ)を作成します。
作業ディレクトリの名前は「sample_app」や「workspace」などがよくありますが、作成するアプリ名にするとわかりやすいでしょう。
ここでは私が作成したアプリ名「cooklog」とします。

作業ディレクトを作成します。

# 作業ディレクトリを作成
$ mkdir ~/cooklog

ここまでは「ターミナルにおいてどのフォルダでコマンドを打つか」は気にしなくてよかったですが、ここからは作業フォルダに入ってコマンドを打っていくことになります。
そこで、ディレクトリへの移動を行います。

# 作業ディレクトリへ移動(適宜パスを指定)
$ cd cooklog

2-3. Rubyのバージョンを適用

それでは作業ディレクトリに対してRuby2.5.7を適用します。
先ほど作ったディレクトリにいることを確認して、次のコマンドを実行します。

# 現在のディレクトリにRuby2.5.7を適用
$ rbenv local 2.5.7

# 適用を反映
$ rbenv rehash

これで作業ディレクトリに適用するRubyのバージョンが変わったはずです。確認してみましょう。

$ ruby -v
#=> ruby 2.5.7p206 (2019-10-01 revision 67816) [x86_64-darwin19]

$ rbenv versions
#=>   system (set by /Users/<ユーザ名>/.rbenv/version)
#   * 2.5.7

Rubyのバージョンは、p206..以降が違っても問題ありません。
「*」2.5.7の方に移り、適用バージョンが変わったことを確認できました。

※ちなみにrbenv local x.x.xが現在のディレクトリのRubyバージョンを変更するのに対し、rbenv global x.x.xは使っているPCでデフォルトで使うRubyバージョンを指定します。
したがって、globalの方を使ってもOKです(今回は他のディレクトリへ影響することを避けるため、localにしています)。

2-4. bundlerのインストール

2章の最後に、gemのインストールには欠かせないbundlerをインストールしておきます。
次のコマンドを実行してください。

$ gem install bundler

3. PostgreSQLのインストール

続いてデータベースのインストールを行います。
今回はPostgreSQLを使うことにします。

次のコマンドを打ち込んで、インストールと起動を一気にやってしまいましょう。

# PostgreSQLのインストール
$ brew install postgresql

# PostgreSQLの起動
$ brew services start postgresql
==> Successfully started `postgresql` (label: homebrew.mxcl.postgresql)

4. Railsのインストール

ここからいよいよRailsをインストールしていきます。

バージョンは5.2.3とするので、次のコマンドを打ち込んでください。

# Railsアプリ作成(gemのインストールはスキップ)
$ rails _5.2.3_ new . --skip-bundle

途中、何か聞かれたら全てy(yesの意味)を打ってEnterを押せばOKです。
.はカレント(現在の)ディレクトリという意味です。

--skip-bundleの部分がポイントで、これをつけるとgemのインストールは後回しにしてRailsアプリに必要なファイルだけを先に作ってくれます。

次に作られたファイルの中のGemfileを開き、次のように書き換えます。

Gemfile
# 変更前
gem 'rails', '~> 5.2.3'
# 変更後
gem 'rails', '= 5.2.3'

'~> 5.2.3'は「バージョン5.2.3以上のものをインストールします」という意味なので、確実に5.2.3をインストールするためにこの変更を行います。

そして、次のコマンドを実行して各種gemをインストールします。

# 各種gemをインストール
$ bundle install

Railsのバージョンを確認し、きちんとインストールできていることを確認しましょう。

$ rails -v
#=> 5.2.3

最後に、Railsアプリを立ち上げるためにRailsサーバーを起動します。

$ rails s
=> Booting Puma
=> Rails 5.2.3 application starting in development 
...(省略)...
* Listening on tcp://localhost:3000
Use Ctrl-C to stop

上の感じになったらOKです。
http://localhost:3000/にアクセスしてみましょう。

スクリーンショット 2020-03-05 22.32.22.png

この画面が表示されれば、きちんとRailsアプリが立ち上がっています!
Rails 5.2.3Ruby 2.5.7が適用されていることがわかりますね。

これにて環境構築はおしまいです。

お疲れ様でした!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【これで完璧!】MacでRails環境構築する手順を超丁寧に解説する

この記事は

初心者でも迷わず環境構築ができるよう、1つ1つの手順を丁寧に解説した記事です。
コマンドが羅列された記事はよく見かけますが、結局コマンドの意味がわからず理解が深まらないことが多々あったので、なるべくコマンドの解説もつけるようにしました。

「環境構築が苦手、よくわからない」という方は、ぜひ参考にしていただければと思います。
また「railsのバージョン指定がうまくいかない...」と悩んでいる方にも参考になると思います。

マシンスペック

参考までに、私のマシンスペックは以下の通りです。

  • macOS Catalina 10.15.3
  • MacBook Air (Retina, 13-inch, 2018)
  • Intel Core i5
  • メモリ 8GB

Ruby, Railsのバージョン

それぞれ下記バージョンで構築することを目指します。

  • Ruby 2.5.7
  • Rails 5.2.3

全体の流れ

この順番でインストールしていきます。

  1. rbenv(Rubyのバージョン管理ツール)
  2. Ruby
  3. PostgreSQL(データベース)
  4. Rails

構築手順

1. rbenvのインストール

rbenvとは、Rubyのバージョンを簡単に切り替えてくれるツールです。

1台のPCで複数のRailsアプリを作ろうとしたとき、「こっちのアプリではRuby2.5.0を使いたいけど、こっちはRuby2.6.0を使いたい...」という欲求が出てきたりします。
そんなときに活躍するのがrbenvです。
詳しい使い方は後で見ていくとして、まずはインストールしてしまいましょう。

rbenvをインストールするためにはHomebrewが必要で、HomebrewをインストールするためにはCommand Line Toolsが必要です。
順番に進めていきます。

1-1. Command Line Toolsのインストール

ターミナルを開き、次のコマンドを実行します。

# Command Line Toolsのインストール
$ xcode-select --install

Xcodeというツールをインストールしていない場合は「インストールしますか?」という画面が表示されるため、「インストール」を選択します。
Xcodeは重いので、少し時間がかかるかもしれません。

インストール完了後、次のコマンドでバージョンが表示されたら成功です。

# バージョン確認
$ xcodebuild -version

1-2. Homebrewのインストール

続いてHomebrewをインストールします。
Homebrewの公式サイトに飛ぶとインストール用のコマンドが用意されてるので、それをターミナルに貼り付けて実行するだけです。
コマンドは随時アップデートされたりするので、直接サイトから持ってくるのが賢明です。

ちなみにHomebrewとは、Macにデフォルトで入っていないソフトをインストールするためのツールです。
上の画像を見ると、「Appleが用意していないあなたの必要なものをインストールします」と書いてありますが、噛み砕くとそういうことです。
Homebrewを入れておくといろいろインストールできて便利なんだな、と理解しておけば大丈夫です。

1-3. rbenvのインストール

いよいよrbenvのインストールを行います。
ターミナルで次の3つのコマンドを順番に実行してください。

# (1) rbenvを初期化する処理を設定
$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile

# (2) 初期化設定の反映
$ source ~/.bash_profile

# (3) rbenvのインストール
$ brew install rbenv ruby-build

(3)では、rbenvと一緒に、rbenvのプラグインであるruby-buildもインストールしています。
rbenvについて詳しく知りたい方は、こちらのブログで詳しく紹介されていますので読んでみてください(ただ、初学者には難易度高めです)。

このrbenvを使って、Rubyをインストールしていきます!

2. Rubyのインストールとバージョン適用

2-1. Rubyのインストール

rbenvでインストールできるバージョンは下記コマンドで確認できます。

# rbenvでインストールできるバージョンを確認
$ rbenv install --list
#=> Available versions:
#   1.8.5-p52
#   1.8.5-p113
#   ...
#   truffleruby-19.2.0
#   truffleruby-19.2.0.1

今回は2.5.7をインストールします。

# Ruby2.5.7をインストール
$ rbenv install 2.5.7

これでRuby2.5.7がインストールされたはずです。
次のコマンドを打ってみましょう。

# インストール済みのRubyのバージョン一覧を表示する
$ rbenv versions
#=> * system (set by /Users/<ユーザ名>/.rbenv/version)
#     2.5.7

これは、system(Macのデフォルト)と2.5.7の2種類のRubyバージョンがPCにインストール済みであることを示しています。
2.5.7もインストールできたことがわかりますね。
ちなみに「*」がついている方が、現在の環境に適用されているバージョンです。

2-2. 作業ディレクトリの作成&移動

続けて作業ディレクトリ(フォルダ)を作成します。
作業ディレクトリの名前は「sample_app」や「workspace」などがよくありますが、作成するアプリ名にするとわかりやすいでしょう。
ここでは私が作成したアプリ名「cooklog」とします。

作業ディレクトを作成します。

# 作業ディレクトリを作成
$ mkdir ~/cooklog

ここまでは「ターミナルにおいてどのフォルダでコマンドを打つか」は気にしなくてよかったですが、ここからは作業フォルダに入ってコマンドを打っていくことになります。
そこで、ディレクトリへの移動を行います。

# 作業ディレクトリへ移動(適宜パスを指定)
$ cd cooklog

2-3. Rubyのバージョンを適用

それでは作業ディレクトリに対してRuby2.5.7を適用します。
先ほど作ったディレクトリにいることを確認して、次のコマンドを実行します。

# 現在のディレクトリにRuby2.5.7を適用
$ rbenv local 2.5.7

# 適用を反映
$ rbenv rehash

これで作業ディレクトリに適用するRubyのバージョンが変わったはずです。確認してみましょう。

$ ruby -v
#=> ruby 2.5.7p206 (2019-10-01 revision 67816) [x86_64-darwin19]

$ rbenv versions
#=>   system (set by /Users/<ユーザ名>/.rbenv/version)
#   * 2.5.7

Rubyのバージョンは、p206..以降が違っても問題ありません。
「*」2.5.7の方に移り、適用バージョンが変わったことを確認できました。

※ちなみにrbenv local x.x.xが現在のディレクトリのRubyバージョンを変更するのに対し、rbenv global x.x.xは使っているPCでデフォルトで使うRubyバージョンを指定します。
したがって、globalの方を使ってもOKです(今回は他のディレクトリへ影響することを避けるため、localにしています)。

2-4. bundlerのインストール

2章の最後に、gemのインストールには欠かせないbundlerをインストールしておきます。
次のコマンドを実行してください。

$ gem install bundler

3. PostgreSQLのインストール

続いてデータベースのインストールを行います。
今回はPostgreSQLを使うことにします。

次のコマンドを打ち込んで、インストールと起動を一気にやってしまいましょう。

# PostgreSQLのインストール
$ brew install postgresql

# PostgreSQLの起動
$ brew services start postgresql
==> Successfully started `postgresql` (label: homebrew.mxcl.postgresql)

4. Railsのインストール

ここからいよいよRailsをインストールしていきます。

まずはバージョンは気にせず、Railsをインストールします。

$ gem install rails

続けて、作成するアプリのバージョンは5.2.3とするので、次のコマンドを打ち込んでください。

# Railsアプリ作成(gemのインストールはスキップ)
$ rails _5.2.3_ new . --skip-bundle

途中、何か聞かれたら全てy(yesの意味)を打ってEnterを押せばOKです。
.はカレント(現在の)ディレクトリという意味です。

--skip-bundleの部分がポイントで、これをつけるとgemのインストールは後回しにしてRailsアプリに必要なファイルだけを先に作ってくれます。

次に作られたファイルの中のGemfileを開き、次のように書き換えます。

Gemfile
# 変更前
gem 'rails', '~> 5.2.3'
# 変更後
gem 'rails', '= 5.2.3'

'~> 5.2.3'は「バージョン5.2.3以上のものをインストールします」という意味なので、確実に5.2.3をインストールするためにこの変更を行います。

そして、次のコマンドを実行して各種gemをインストールします。

# 各種gemをインストール
$ bundle install

Railsのバージョンを確認し、きちんとインストールできていることを確認しましょう。

$ rails -v
#=> 5.2.3

最後に、Railsアプリを立ち上げるためにRailsサーバーを起動します。

$ rails s
=> Booting Puma
=> Rails 5.2.3 application starting in development 
...(省略)...
* Listening on tcp://localhost:3000
Use Ctrl-C to stop

上の感じになったらOKです。
http://localhost:3000/にアクセスしてみましょう。

スクリーンショット 2020-03-05 22.32.22.png

この画面が表示されれば、きちんとRailsアプリが立ち上がっています!
Rails 5.2.3Ruby 2.5.7が適用されていることがわかりますね。

これにて環境構築はおしまいです。

お疲れ様でした!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsのenumを読む

Railsのソースコードを読みます。

enumに関して

class User < ApplicationRecord

binding.pry
  enum status: { active: 0, inactive: 1 }
end

bindingを使ってコードの中に入っていく。

    153:         attr_reader :name, :mapping, :subtype
    154:     end
    155: 
    156:     def enum(definitions)
    157:       klass = self
 => 158:       enum_prefix = definitions.delete(:_prefix)
    159:       enum_suffix = definitions.delete(:_suffix)
    160:       enum_scopes = definitions.delete(:_scopes)
    161:       definitions.each do |name, values|
    162:         assert_valid_enum_definition_values(values)
    163:         # statuses = { }

prefix・suffix・_scopesが存在すれば、削除して代入。ちなみに、prefixは接頭辞、suffixは接尾辞、_scopesはスコープの設定である。

[3] pry(User)> klass
=> User (call 'User.connection' to establish a connection)
[4] pry(User)> definitions
=> {:status=>{:active=>0, :inactive=>1}}
    156:     def enum(definitions)
    157:       klass = self
    158:       enum_prefix = definitions.delete(:_prefix)
    159:       enum_suffix = definitions.delete(:_suffix)
    160:       enum_scopes = definitions.delete(:_scopes)
 => 161:       definitions.each do |name, values|
    162:         assert_valid_enum_definition_values(values)
    163:         # statuses = { }
    164:         enum_values = ActiveSupport::HashWithIndifferentAccess.new
    165:         name = name.to_s
    166: 
[5] pry(User)> name
=> :status
[6] pry(User)> values
=> {:active=>0, :inactive=>1}

assert_valid_enum_definition_valuesメソッドの中に入っていく。

    232: def assert_valid_enum_definition_values(values)
 => 233:   unless values.is_a?(Hash) || values.all? { |v| v.is_a?(Symbol) } || values.all? { |v| v.is_a?(String) }
    234:     error_message = <<~MSG
    235:       Enum values #{values} must be either a hash, an array of symbols, or an array of strings.
    236:     MSG
    237:     raise ArgumentError, error_message
    238:   end
    239: 
    240:   if values.is_a?(Hash) && values.keys.any?(&:blank?) || values.is_a?(Array) && values.any?(&:blank?)
    241:     raise ArgumentError, "Enum label name must not be blank."
    242:   end
    243: end

valuesがハッシュかvaluesの要素全てがシンボルかvaluesの要素全てが文字列かを判定して、それがfalseだったらエラーを返す。今回はtrueなので実行されない。

    232: def assert_valid_enum_definition_values(values)
    233:   unless values.is_a?(Hash) || values.all? { |v| v.is_a?(Symbol) } || values.all? { |v| v.is_a?(String) }
    234:     error_message = <<~MSG
    235:       Enum values #{values} must be either a hash, an array of symbols, or an array of strings.
    236:     MSG
    237:     raise ArgumentError, error_message
    238:   end
    239: 
 => 240:   if values.is_a?(Hash) && values.keys.any?(&:blank?) || values.is_a?(Array) && values.any?(&:blank?)
    241:     raise ArgumentError, "Enum label name must not be blank."
    242:   end
    243: end

valuesがハッシュかつvaluesのキーがblankかvaluesが配列かつvaluesの要素がblankかを判定して、trueならraiseする。今回はfalseなので次へ。

    159:       enum_suffix = definitions.delete(:_suffix)
    160:       enum_scopes = definitions.delete(:_scopes)
    161:       definitions.each do |name, values|
    162:         assert_valid_enum_definition_values(values)
    163:         # statuses = { }
 => 164:         enum_values = ActiveSupport::HashWithIndifferentAccess.new
    165:         name = name.to_s
    166: 
    167:         # def self.statuses() statuses end
    168:         detect_enum_conflict!(name, name.pluralize, true)
    169:         singleton_class.define_method(name.pluralize) { enum_values }

ActiveSupport::HashWithIndifferentAccess.newで空のインスタンスをenum_valuesに代入。またnameを文字列に変換。次。

    163:         # statuses = { }
    164:         enum_values = ActiveSupport::HashWithIndifferentAccess.new
    165:         name = name.to_s
    166: 
    167:         # def self.statuses() statuses end
 => 168:         detect_enum_conflict!(name, name.pluralize, true)
    169:         singleton_class.define_method(name.pluralize) { enum_values }
    170:         defined_enums[name] = enum_values
    171: 
    172:         detect_enum_conflict!(name, name)
    173:         detect_enum_conflict!(name, "#{name}=")

detect_enum_conflict!の中を見ていく。

    251: def detect_enum_conflict!(enum_name, method_name, klass_method = false)
 => 252:   if klass_method && dangerous_class_method?(method_name)
    253:     raise_conflict_error(enum_name, method_name, type: "class")
    254:   elsif klass_method && method_defined_within?(method_name, Relation)
    255:     raise_conflict_error(enum_name, method_name, type: "class", source: Relation.name)
    256:   elsif !klass_method && dangerous_attribute_method?(method_name)
    257:     raise_conflict_error(enum_name, method_name)
    258:   elsif !klass_method && method_defined_within?(method_name, _enum_methods_module, Module)
    259:     raise_conflict_error(enum_name, method_name, source: "another enum")
    260:   end
    261: end

detect_enum_conflist!は第2引数で与えられたものがどこかに定義されているかどうかを判断して例外を発生させるメソッド。今回は例外は発生しない。

    164:         enum_values = ActiveSupport::HashWithIndifferentAccess.new
    165:         name = name.to_s
    166: 
    167:         # def self.statuses() statuses end
    168:         detect_enum_conflict!(name, name.pluralize, true)
 => 169:         singleton_class.define_method(name.pluralize) { enum_values }
    170:         defined_enums[name] = enum_values
    171: 
    172:         detect_enum_conflict!(name, name)
    173:         detect_enum_conflict!(name, "#{name}=")

singleton_class.define_method(name.pluralize) { enum_values }でname.pluralizeという特異メソッドを定義する。

    165:         name = name.to_s
    166: 
    167:         # def self.statuses() statuses end
    168:         detect_enum_conflict!(name, name.pluralize, true)
    169:         singleton_class.define_method(name.pluralize) { enum_values }
 => 170:         defined_enums[name] = enum_values
    171: 
    172:         detect_enum_conflict!(name, name)
    173:         detect_enum_conflict!(name, "#{name}=")
    174: 
    175:         attr = attribute_alias?(name) ? attribute_alias(name) : name

defined_enums[name]に空のハッシュを代入

    168:         detect_enum_conflict!(name, name.pluralize, true)
    169:         singleton_class.define_method(name.pluralize) { enum_values }
    170:         defined_enums[name] = enum_values
    171: 
    172:         detect_enum_conflict!(name, name)
 => 173:         detect_enum_conflict!(name, "#{name}=")
    174: 
    175:         attr = attribute_alias?(name) ? attribute_alias(name) : name
    176:         decorate_attribute_type(attr, :enum) do |subtype|
    177:           EnumType.new(attr, enum_values, subtype)
    178:         end

detect_enum_conflict!でnameがどこかで定義されていないか判定。

    170:         defined_enums[name] = enum_values
    171: 
    172:         detect_enum_conflict!(name, name)
    173:         detect_enum_conflict!(name, "#{name}=")
    174: 
 => 175:         attr = attribute_alias?(name) ? attribute_alias(name) : name
    176:         decorate_attribute_type(attr, :enum) do |subtype|
    177:           EnumType.new(attr, enum_values, subtype)
    178:         end
    179: 
    180:         _enum_methods_module.module_eval do

エイリアスが存在したら、それを返す。

    171: 
    172:         detect_enum_conflict!(name, name)
    173:         detect_enum_conflict!(name, "#{name}=")
    174: 
    175:         attr = attribute_alias?(name) ? attribute_alias(name) : name
 => 176:         decorate_attribute_type(attr, :enum) do |subtype|
    177:           EnumType.new(attr, enum_values, subtype)
    178:         end
    179: 
    180:         _enum_methods_module.module_eval do
    181:           pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index

ブロックを引数としてdecorate_attribute_typeを実行。decorate_attribute_typeの中身を見ていく。

    23: def decorate_attribute_type(column_name, decorator_name, &block)
    24:   matcher = ->(name, _) { name == column_name.to_s }
 => 25:   key = "_#{column_name}_#{decorator_name}"
    26:   decorate_matching_attribute_types(matcher, key, &block)
    27: end

matcherにname==column_name.to_sを判定するようなブロックを代入。keyに文字列を代入。それぞれの値とブロックを引数としてdecorate_matching_attribute_typesを呼び出し。

    40: def decorate_matching_attribute_types(matcher, decorator_name, &block)
 => 41:   reload_schema_from_cache
    42:   decorator_name = decorator_name.to_s
    43: 
    44:   # Create new hashes so we don't modify parent classes
    45:   self.attribute_type_decorations = attribute_type_decorations.merge(decorator_name => [matcher, block])
    46: end

cacheからschemaをリロード。selfのattribute_type_decorationsにdecorator_nameがキーでmatcherとblockが値となるようなハッシュをmerge。ここで与えられているblockがEnumType.new(attr, enum_values, subtype)なので、ここでRailsのenumとしての機能が使えるような処理をしている。

    175:         attr = attribute_alias?(name) ? attribute_alias(name) : name
    176:         decorate_attribute_type(attr, :enum) do |subtype|
    177:           EnumType.new(attr, enum_values, subtype)
    178:         end
    179: 
 => 180:         _enum_methods_module.module_eval do
    181:           pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
    182:           pairs.each do |label, value|
    183:             if enum_prefix == true
    184:               prefix = "#{name}_"
    185:             elsif enum_prefix

enum_methods_moduleに対してmoduleevalを呼ぶ。

    176:         decorate_attribute_type(attr, :enum) do |subtype|
    177:           EnumType.new(attr, enum_values, subtype)
    178:         end
    179: 
    180:         _enum_methods_module.module_eval do
 => 181:           pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
    182:           pairs.each do |label, value|
    183:             if enum_prefix == true
    184:               prefix = "#{name}_"
    185:             elsif enum_prefix
    186:               prefix = "#{enum_prefix}_"

valuesがeachに応答できるかどうかを判定して、trueならvalues.each_pair、falseならvalues.each_with_indexを返す。

    177:           EnumType.new(attr, enum_values, subtype)
    178:         end
    179: 
    180:         _enum_methods_module.module_eval do
    181:           pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
 => 182:           pairs.each do |label, value|
    183:             if enum_prefix == true
    184:               prefix = "#{name}_"
    185:             elsif enum_prefix
    186:               prefix = "#{enum_prefix}_"
    187:             end

ここから複数行は、prefixとsuffixに関しての内容。enum_prefixとenum_suffixを判定して、その判定によって、prefixとsuffixの内容を変更する。

    189:               suffix = "_#{name}"
    190:             elsif enum_suffix
    191:               suffix = "_#{enum_suffix}"
    192:             end
    193: 
 => 194:             value_method_name = "#{prefix}#{label}#{suffix}"
    195:             enum_values[label] = value
    196:             label = label.to_s
    197: 
    198:             # def active?() status == "active" end
    199:             klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")

先程判定したprefixとsuffixを使ってlabelに接頭辞と接尾辞をつけたものをvalue_method_nameに代入。

    190:             elsif enum_suffix
    191:               suffix = "_#{enum_suffix}"
    192:             end
    193: 
    194:             value_method_name = "#{prefix}#{label}#{suffix}"
 => 195:             enum_values[label] = value
    196:             label = label.to_s
    197: 
    198:             # def active?() status == "active" end
    199:             klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
    200:             define_method("#{value_method_name}?") { self[attr] == label }

enum_valuesハッシュにキーであるlabelと値であるvalueを追加。

    191:               suffix = "_#{enum_suffix}"
    192:             end
    193: 
    194:             value_method_name = "#{prefix}#{label}#{suffix}"
    195:             enum_values[label] = value
 => 196:             label = label.to_s
    197: 
    198:             # def active?() status == "active" end
    199:             klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
    200:             define_method("#{value_method_name}?") { self[attr] == label }
    201: 

labelを文字列に変更。

    194:             value_method_name = "#{prefix}#{label}#{suffix}"
    195:             enum_values[label] = value
    196:             label = label.to_s
    197: 
    198:             # def active?() status == "active" end
 => 199:             klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
    200:             define_method("#{value_method_name}?") { self[attr] == label }
    201: 
    202:             # def active!() update!(status: 0) end
    203:             klass.send(:detect_enum_conflict!, name, "#{value_method_name}!")
    204:             define_method("#{value_method_name}!") { update!(attr => value) }

すでに#{value_method_name}?がメソッドとして定義されていないか判定。

    195:             enum_values[label] = value
    196:             label = label.to_s
    197: 
    198:             # def active?() status == "active" end
    199:             klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
 => 200:             define_method("#{value_method_name}?") { self[attr] == label }
    201: 
    202:             # def active!() update!(status: 0) end
    203:             klass.send(:detect_enum_conflict!, name, "#{value_method_name}!")
    204:             define_method("#{value_method_name}!") { update!(attr => value) }
    205: 

{value_method_name}?というメソッドを定義して、self[attr] == labelという処理を行う。

    198:             # def active?() status == "active" end
    199:             klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
    200:             define_method("#{value_method_name}?") { self[attr] == label }
    201: 
    202:             # def active!() update!(status: 0) end
 => 203:             klass.send(:detect_enum_conflict!, name, "#{value_method_name}!")
    204:             define_method("#{value_method_name}!") { update!(attr => value) }
    205: 
    206:             # scope :active, -> { where(status: 0) }
    207:             # scope :not_active, -> { where.not(status: 0) }
    208:             if enum_scopes != false

すでに#{value_method_name}!がメソッドとして定義されていないか判定。

    199:             klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
    200:             define_method("#{value_method_name}?") { self[attr] == label }
    201: 
    202:             # def active!() update!(status: 0) end
    203:             klass.send(:detect_enum_conflict!, name, "#{value_method_name}!")
 => 204:             define_method("#{value_method_name}!") { update!(attr => value) }
    205: 
    206:             # scope :active, -> { where(status: 0) }
    207:             # scope :not_active, -> { where.not(status: 0) }
    208:             if enum_scopes != false
    209:               klass.send(:detect_negative_condition!, value_method_name)

{value_method_name}!というメソッドを定義して、update!(attr => value)という処理を行う。

    203:             klass.send(:detect_enum_conflict!, name, "#{value_method_name}!")
    204:             define_method("#{value_method_name}!") { update!(attr => value) }
    205: 
    206:             # scope :active, -> { where(status: 0) }
    207:             # scope :not_active, -> { where.not(status: 0) }
 => 208:             if enum_scopes != false
    209:               klass.send(:detect_negative_condition!, value_method_name)
    210: 
    211:               klass.send(:detect_enum_conflict!, name, value_method_name, true)
    212:               klass.scope value_method_name, -> { where(attr => value) }
    213: 

ここで、enumで設定した値によって、scopeできるようにしている。enum_scopesがfalseかどうかを判定。

    204:             define_method("#{value_method_name}!") { update!(attr => value) }
    205: 
    206:             # scope :active, -> { where(status: 0) }
    207:             # scope :not_active, -> { where.not(status: 0) }
    208:             if enum_scopes != false
 => 209:               klass.send(:detect_negative_condition!, value_method_name)
    210: 
    211:               klass.send(:detect_enum_conflict!, name, value_method_name, true)
    212:               klass.scope value_method_name, -> { where(attr => value) }
    213: 
    214:               klass.send(:detect_enum_conflict!, name, "not_#{value_method_name}", true)

notがついたvalue_method_nameメソッドが定義されていないか判定。

    206:             # scope :active, -> { where(status: 0) }
    207:             # scope :not_active, -> { where.not(status: 0) }
    208:             if enum_scopes != false
    209:               klass.send(:detect_negative_condition!, value_method_name)
    210: 
 => 211:               klass.send(:detect_enum_conflict!, name, value_method_name, true)
    212:               klass.scope value_method_name, -> { where(attr => value) }
    213: 
    214:               klass.send(:detect_enum_conflict!, name, "not_#{value_method_name}", true)
    215:               klass.scope "not_#{value_method_name}", -> { where.not(attr => value) }
    216:             end

nameやvalue_method_nameがメソッドとして定義されていないか判定。

    207:             # scope :not_active, -> { where.not(status: 0) }
    208:             if enum_scopes != false
    209:               klass.send(:detect_negative_condition!, value_method_name)
    210: 
    211:               klass.send(:detect_enum_conflict!, name, value_method_name, true)
 => 212:               klass.scope value_method_name, -> { where(attr => value) }
    213: 
    214:               klass.send(:detect_enum_conflict!, name, "not_#{value_method_name}", true)
    215:               klass.scope "not_#{value_method_name}", -> { where.not(attr => value) }
    216:             end
    217:           end

scopeとしてvalue_method_nameメソッドでwhere(attr => value)を定義する。

    209:               klass.send(:detect_negative_condition!, value_method_name)
    210: 
    211:               klass.send(:detect_enum_conflict!, name, value_method_name, true)
    212:               klass.scope value_method_name, -> { where(attr => value) }
    213: 
 => 214:               klass.send(:detect_enum_conflict!, name, "not_#{value_method_name}", true)
    215:               klass.scope "not_#{value_method_name}", -> { where.not(attr => value) }
    216:             end
    217:           end
    218:         end
    219:         enum_values.freeze

not_#{value_method_name}が定義されているか判定

    210: 
    211:               klass.send(:detect_enum_conflict!, name, value_method_name, true)
    212:               klass.scope value_method_name, -> { where(attr => value) }
    213: 
    214:               klass.send(:detect_enum_conflict!, name, "not_#{value_method_name}", true)
 => 215:               klass.scope "not_#{value_method_name}", -> { where.not(attr => value) }
    216:             end
    217:           end
    218:         end
    219:         enum_values.freeze
    220:       end

scopeとしてnot_#{value_method_name}メソッドを定義してwhere.not(attr => value)を行う。

これでenumのソースコードリーディング終了。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

deviseのcontrollerをインストールしていない場合は、ログイン後のユーザー作成は出来ないよって話。

自分用メモ
macOS Catalina 10.15.3
ruby 2.6.3
rails 5.2.4

ログイン機能なしの作業報告用アプリを作成。

ユーザーを作成して、devise後付けでだが、deviseのcontrollerは無し。(インストールしなくても裏で作動する)
deviseのmodelとviewはインストール。

deviseのcontrollerをインストールしていない場合は、ログイン後のユーザー作成は出来ない。

と言うか、社内用を想定してのアプリだから、

ユーザー登録=ユーザー作成

よって新規ユーザー作成のリンクは削除。

どうしても作成したい場合は、ログインする前のログイン画面にリンクを持ってくる。

している場合は、こちらを参照(管理者権限でユーザー作成)

https://qiita.com/MasatoYoshioka@github/items/7de96ac05bc0c17b7535

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[rails,aws]budlerのバージョンが変更できないエラーの解決方法

1.エラーが出るまでの経緯

筆者はチーム開発のローカル開発でbundler-2.0.1を使ってきたのですが、githubからのセキュリティのためのバージョンアップを行なった際に、誤って本番環境のbundlerのバージョンを2.1.4にあげてしまいました。しかし不要なバージョンアップはバグの原因になるため極力避けたいので、ダウングレード(最新バージョンを削除し使いたいバージョンbundlerをインストール)しようとしました。その結果下記のような結果となりました。

#使いたいbundlerのバージョンを指定(インストール)
[ec2-user@ip-111-11-11-11 freemarket_sample_62d]$ gem install bundler -v 2.0.1
Successfully installed bundler-2.0.1
Parsing documentation for bundler-2.0.1
Done installing documentation for bundler after 3 seconds
1 gem installed


#現在の高すぎるバージョンを削除(アンインストール)
[ec2-user@ip-111-11-11-11 freemarket_sample_62d]$ gem uninstall -v 2.1.4
ERROR:  While executing gem ... (Gem::CommandLineError)
    Please specify at least one gem name (e.g. gem build GEMNAME)


#bundlerのバージョンを確認
[ec2-user@ip-111-11-11-11 freemarket_sample_62d]$ bundler -v
Traceback (most recent call last):
    2: from /home/ec2-user/.rbenv/versions/2.5.1/bin/bundler:23:in `<main>'
    1: from /home/ec2-user/.rbenv/versions/2.5.1/lib/ruby/2.5.0/rubygems.rb:308:in `activate_bin_path'
/home/ec2-user/.rbenv/versions/2.5.1/lib/ruby/2.5.0/rubygems.rb:289:in `find_spec_for_exe': can't find gem bundler (>= 0.a) with executable bundler (Gem::GemNotFoundExcep)

2.原因

Gemfile.lockの記述にあるバージョン以外は適用されない(bundlerと認識されない)というルールがあるようです。そのため、このGemfile.lockを残したままbundlerのバージョンを変えようとしても変更することができませんでした。

Gemfile.lock
GEM
  remote: https://rubygems.org/
  specs:
    actioncable (5.2.4.1)

〜中略〜

RUBY VERSION
   ruby 2.5.1p57

BUNDLED WITH
   2.0.1     #この行に記載のあるバージョン以外はエラーを出すようになっています

3.解決方法

Gemfile.lockを削除→bundlerをダウングレード→bundle installの手順で実行していただければお望みのbundlerのバージョンに変更することができます

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails on HerokuなアプリからFirestoreにアクセスしたい場合の実装例&ハマりどころ

背景

Firestoreを使用する場合、大抵はフロントエンドから直接クエリを発行してDB操作を行うのだろうが、実装者のスキルセットや要件によってはFirestoreの操作に関する処理をサーバーサイドに置きたい場合もある。例えば以下のような事例が見つかった。

上記の事例の全てに該当する点として、サーバーサイドはRailsで構成されることが前提となっている。これはFirebaseとRailsが共にスタートアップのスピーディーなプロダクト開発と相性が良いため必然的にそうなっているのだろう。そしてRailsサーバーのデプロイ先と言えばやはりHerokuが真っ先に頭に浮かぶ(Firebaseとの連携を考えるならGCPの方が本当は色々やりやすいのだろうけど)。

自分も例に漏れず、Rails on HerokuなアプリからFirestoreにアクセスしたいという場面に出くわした。
その際特にFirestoreの認証情報をHerokuに読み込ませる辺りでハマったので、備忘録も兼ねてプロジェクトの土台作りの手順をまとめておく。

環境

  • Rails 6.0.2.1
  • Ruby 2.6.5 (なぜ2.7系ではないかというとgoogle-cloud-firestore gemが2020/3/1時点で2.7系に未対応だから...) (RailsサーバーはFirestoreに接続するだけでなく自分のとこのMySQLも使うという場合を想定)
  • MySQL 5.7
  • フロントの設定はRails 6にデフォで組み込まれているWebpackerではなくSimpackerで生成(自分は宗教上の理由でWebpackerを使えません)。JSフレームワークは何でも良いが例としてReact×Typescriptな構成を考える。
  • 開発環境ではforemanを使ってRailsサーバーとwebpack-dev-serverを同時に立ち上げる
  • Rails側からFirestoreに接続する部分にはgoogle-cloud-firestore gemを利用
  • 本番環境はHeroku + Clear DB
  • Firestoreは本番用と開発時などのデバッグ用の2つが用意されているものとする

プロジェクトの土台作り

$ bundle exec rails new rails_firestore_sample -B -C -M -T -J -S -d mysql --skip-turbolinks
$ cd rails_firestore_sample
$ bundle add simpacker
$ bundle exec rails simpacker:install
$ npm i -D webpack-dev-server typescript ts-loader @types/react @types/react-dom
$ npm i -S react react-dom
$ vi webpack.config.js

以下のように修正する。

webpack.config.js
//...(中略)
    entry: {
      application: path.resolve(__dirname, "app/javascript/index.tsx") // < ココ変更
    },
    output: {
      path: path.resolve(__dirname, "public/packs"),
      publicPath: isProd ? "/packs/" : "//localhost:8081/packs/", // < ココ変更
      filename: isProd ? "[name]-[hash].js" : "[name].js"
    },
    resolve: {
      extensions: [".js", ".jsx", ".ts", ".tsx"]  // < ココ変更
    },
    module: {  // < ココ追加
      rules: [
        {
          test: /\.tsx?$/,
          loader: "ts-loader",
          options: {
            transpileOnly: true
          }
        }
      ]
    },
    devServer: {  // < ココ追加
      contentBase: path.resolve(__dirname, "public"),
      publicPath: "/packs/",
      host: "localhost",
      port: 8081,
      headers: {
        "Access-Control-Allow-Origin": "*"
      }
    },
    plugins: [new WebpackAssetsManifest({ publicPath: true, writeToDisk: true })] // < ココ変更

$ vi tsconfig.json

以下の内容を貼る。./node_modules/.bin/tsc --init でtsconfigを生成しても良いが、必ずapp/javascriptをincludeすること。

tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "lib": ["es2019", "dom", "dom.iterable"],
    "module": "es2015",
    "jsx": "react",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "downlevelIteration": true,
    "sourceMap": true,
    "removeComments": false,
    "noImplicitAny": false,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictBindCallApply": true,
    "strictPropertyInitialization": true,
    "noImplicitThis": true
  },
  "include": ["app/javascript/**/*"]
}

開発環境でRailsサーバーとwebpack-dev-serverを同時に立ち上げるためforeman gemを追加する。

$ bundle add foreman

foremanが管理するプロセスの起動方法をProcfileに定義する。

Procfile
web: bundle exec rails s
Procfile.dev
web: bundle exec rails s
client: ./node_modules/.bin/webpack-dev-server

これで開発時にはbundle exec foreman start -f Procfile.dev とするだけでRailsとwebpack-dev-serverが起動し、JS/TSファイルのホットリロードが効くようになる。Herokuへのデプロイ時にはProcfileの方が参照されてRailsサーバーのみが立ち上がる。

動作確認用に簡単なviewを用意していく。まずルートページを表示するためにpages controllerを用意し、indexアクションを追加する。

app/controllers/pages_controller.rb
# frozen_string_literal: true

class PagesController < ApplicationController
  def index; end
end
app/views/pages/index.html.erb
<div id="app"></div>
config/routes.rb
# frozen_string_literal: true

Rails.application.routes.draw do
  root 'pages#index'
end

JSファイルを読み込ませるためlayoutファイルにjavascript_pack_tagを追加。

app/views/layouts/application.html.erb
...(中略)
<%= csp_meta_tag %>

- <%= stylesheet_link_tag 'application', media: 'all' %>  <!-- 不要なので削除 -->
+ <%= javascript_pack_tag 'application' %>  <!-- 追加 -->
</head>

動作確認用に以下のようなコンポーネントを用意。

app/javascript/components/App.tsx
import React, { FC } from "react";

interface Props {
  name: string;
}

export const Hello: FC<Props> = ({ name }) => {
  return <div>Hello{name}!</div>;
};
app/javascript/index.tsx
import React from "react";
import ReactDOM from "react-dom";
import { Hello } from "./components/App";

document.addEventListener("DOMContentLoaded", () => {
  ReactDOM.render(<Hello name="Rails" />, document.getElementById("app"));
});

bundle exec foreman start -f Procfile.devでサーバーを起動し、localhost:5000にアクセスすると以下のようなページが表示される。App.tsxの中身を編集して保存すると自動でビルドが走ってページが更新されるのが確認できればOK。
rails_root.png

Firestore上のデータをRails側から参照する

まずはgoogle-cloud-firestoreをインストールする。

$ bundle add google-cloud-firestore

次にFirestoreへの接続用の基底クラスを用意する。

app/models/firestore/base.rb
# frozen_string_literal: true

require 'google/cloud/firestore'

class Firestore::Base
  class_attribute :client
  self.client = Google::Cloud::Firestore.new(project_id: ENV['FIREBASE_PROJECT_ID'])
end

環境変数 FIREBASE_PROJECT_ID には作成しておいた開発時用のFirebaseプロジェクトのProject IDを入れておく。

続いてFirestore上の何らかのコレクションを参照するAPIを生やしていく。
まずFirestoreのコンソール等で何らかのコレクションを作成し、Seedデータを投入しておく(ここは説明省略)。

ここでは例としてFirestore上にusersコレクション(name fieldを持っているものとする)が用意されているものとし、usersの一覧表示と詳細表示までできるようにする。

app/models/firestore/user.rb
# frozen_string_literal: true

class Firestore::User < Firestore::Base
  COLLECTION_NAME = 'users'

  class_attribute :repo
  self.repo = client.col COLLECTION_NAME

  def self.all
    repo.order('name').get.map do |user|  # nameの値でソートしてuserの一覧を返す
      user.data.merge({ documentId: user.document_id })  # この辺はお好みでどうぞ
    end
  end

  def self.find(document_id)
    snapshot = repo.doc(document_id).get

    snapshot.data.merge({ documentId: snapshot.document_id }) if snapshot.exists?
  end
end

コントローラーを用意する。

app/controllers/api/firestore/users_controller.rb
# frozen_string_literal: true

class Api::Firestore::UsersController < ApplicationController
  before_action :set_user, only: :show

  def index
    @users = Firestore::User.all
    render json: @users
  end

  def show
    render json: @user
  end

  private

  def set_user
    @user = Firestore::User.find(params[:id])
  end
end

ルートを追加。

config/routes.rb
# frozen_string_literal: true

Rails.application.routes.draw do
  root 'pages#index'

  namespace :api, format: :json do
    namespace :firestore do
      resources :users, only: %i[index show]
    end
  end
end

これで localhost:5000/api/users, localhost:5000/users/:idにアクセスするとuserの一覧、詳細が返却され...
ない!! その前にもう一つやることがある。

GCPの認証情報をRailsに読み込ませるための設定を追加

Firebase公式 > Cloud Firestoreを使ってみる > 開発環境を設定するに書かれているように、

Cloud Firestore サーバーのクライアント ライブラリ(Java、Node.js、Python、Go、PHP、C#、Ruby)は、認証に Google アプリケーションのデフォルト認証情報を使用します。
開発環境から認証するには、JSON サービス アカウント キーファイルを指すように GOOGLE_APPLICATION_CREDENTIALS 環境変数を設定します。キーファイルは、API Console の認証情報ページで作成できます。

なのでAPI consoleからプロジェクトを選択 > サービスアカウントを選択 > キーを作成 > キーのタイプはJSONを選択
し、本番用と開発用のFirebaseプロジェクトそれぞれについて認証情報が書かれたJSONファイルを落としてきてRailsプロジェクトのルートに配置してください。これらのファイルは間違ってもgitにコミットしないように.gitignoreに追加しておきます。

そして最初の方で作成しておいたforemanの設定ファイルを修正します。

Procfile.dev
web: GOOGLE_APPLICATION_CREDENTIALS=[開発用のFirebaseプロジェクトの認証情報が書かれたjsonファイル名] bundle exec rails s
client: ./node_modules/.bin/webpack-dev-server

これで開発環境でforemanを使って立ち上げたRailsサーバーについては開発用のFirestoreにアクセスできるようになります。先程生やしたAPIにアクセスすると投入しておいたSeedデータがちゃんと返ってくるはずです。

問題はこのアプリをHerokuにデプロイした際にはGOOGLE_APPLICATION_CREDENTIALSで在り処を指定しているjsonファイルはgitの管理外なので、Procfile.devと同じようにProcfileを記述しても、このjsonファイルを読むことはできず、Firestoreに接続できないという点です。この点についてはFirebaseのドキュメントには一切記述がないのですが、実はgoogle-cloud-firestore gemのドキュメントをよーく読むとコソッと書いてありまして、

Project and Credential Lookup

(中略)

Environment Variables

The Project ID and Credentials JSON can be placed in environment variables instead of declaring them directly in code. Each service has its own environment variable, allowing for different service accounts to be used for different services. (See the READMEs for the individual service gems for details.) The path to the Credentials JSON file can be stored in the environment variable, or the Credentials JSON itself can be stored for environments such as Docker containers where writing files is difficult or not encouraged.

(中略)
The environment variables that google-cloud-firestore checks for credentials are configured on Google::Cloud::Firestore::V1::Credentials:
1. FIRESTORE_CREDENTIALS - Path to JSON file, or JSON contents ⇐ コレが使える!!!
2. FIRESTORE_KEYFILE - Path to JSON file, or JSON contents
3. GOOGLE_CLOUD_CREDENTIALS - Path to JSON file, or JSON contents
4. GOOGLE_CLOUD_KEYFILE - Path to JSON file, or JSON contents
5. GOOGLE_APPLICATION_CREDENTIALS - Path to JSON file ⇐ これは開発環境のようにjsonファイルが実際に配置可能な時なら使えるので, Procfile.devの方はこちらを使っても良い

ということなので、本番環境のProcfileの方はそのままいじらずに、HerokuのダッシュボードやCLIから環境変数FIRESTORE_CREDENTIALSに本番用のFirebaseプロジェクトの認証情報が書かれたjsonファイルの中身を直接セットするようにすれば動きます。Heroku CLIから環境変数をセットする場合には

$ heroku config:set FIRESTORE_CREDENTIALS="$(< ./[認証情報が書かれたjsonファイル名])"

のようにすればOKです。

【最後に】Simpacker使用時のHerokuへのデプロイにおける注意点

Herokuへのデプロイ手順については本題と外れるのでここまで触れてきませんでしたが、上で述べたようにFirestoreの認証情報を環境変数にセットすればFirestoreに接続するAPIの部分は動いても、実はフロントエンドのコードが動作しません。

というのもHerokuへのRailsアプリのデプロイ時には自動的にassets:precompileタスクが実行されるようになっているのですが、今回rails newしたときにはオプションでsprocketsを入れないようにしており、このままではassets:precompileタスクが定義されていないとHerokuに怒られます。おとなしくWebpackerを使っていれば脳死でとりあえずgit push heroku masterするだけで良いのですが、前述したように自分は宗教上の理由でWebpackerが使えまs...

そこで対策として自前でassets:precompileタスクを追加し、このタイミングでnode_modulesのインストール及びwebpackによるProductionビルドが実行されるようにします。この辺りはSimpacker heroku example(GitHub)の方で説明されています。

lib/tasks/simpacker.rake
# frozen_string_literal: true

namespace :assets do
  task :precompile do
    sh 'npm install'
    sh 'NODE_ENV=production ./node_modules/.bin/webpack --mode production'
  end
end

こいつを用意した状態で

$ heroku buildpacks:add --index 1 heroku/nodejs

してNodeのbuildpackをRubyより手前に入れてから git push heroku master すれば(本当はまだClear DBのセットアップも残っていますが)フロントエンドも動作するようになります。

あとはゴリゴリReactでフロント作ってRailsのAPI経由でFirestoreをいじるだけです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】Rakeタスクに:environmentがないとNameErrorが出る

エラー

Rakeタスクを実行したときに、NameError: uninitialized constant ClassNameが出る。

send_summary_mail.rake
namespace :send_summary_mail do
  desc '管理者に記事数一覧をメール送信する'
  task :send_mail do
    SummaryMailer.daily.deliver_now
    puts 'メールを送信しました'
  end
end
$ rake send_summary_mail:send_mail
rake aborted!
NameError: uninitialized constant SummaryMailer
/Users/k_end/workspace/application/lib/tasks/send_mail_summary.rake:4:in `block (2 levels) in <top (required)>'
Tasks: TOP => send_summary_mail:send_mail
(See full trace by running task with --trace)

どうやらSummaryMailerが読み込みできていないようです。
しかし、パスは正しいしタイポもしていない……。

試行錯誤

rails cSummaryMailer.daily.deliver_nowを実行する。
→うまくいく。ということは、SummaryMailer自体には問題はなさそう。

send_summary_mail.rake
require Rails.root.join("app/mailers/summary_mailer")

を追加する。
NameError: uninitialized constant ApplicationMailer
根本的にダメそう。

send_summary_mail.rake
namespace :send_summary_mail do
  desc '管理者に記事数一覧をメール送信する'
  task :send_mail do
    # SummaryMailer.daily.deliver_now
    puts 'メールを送信しました'
  end
end

原因であろう部分をコメントアウトしてみる。
→うまくいく。

この辺から、「多分RakeタスクがRails環境で実行されていないんだろうなー」と予測。

原因

taskに:environmentを付けていませんでした。generateした時点で自動的に付いているはずなのですが、どこかで消えていた模様。

namespace :send_summary_mail do
  desc '管理者に記事数一覧をメール送信する'
  task send_mail: :environment do
    SummaryMailer.daily.deliver_now
    puts 'メールを送信しました'
  end
end
$ rake send_summary_mail:send_mail
メールを送信しました

無事、SummatyMailerを呼び出してタスクを実行することができました。
気が付いてみれば大したことがないのに、ハマるときは無駄にハマる。

リンク

:environmentで何やってるの?という点についてまとめている先人がいました。
Rails における rake タスクの :environment について

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【RSpec】toggleされた要素へのクリック対応

はじめに

RSpecでユーザー登録の統合テストをかいているときに、
toggleされた要素に対するクリックのやり方でつまづきました。
僕と同じ初学者の方の参考になりましたら幸いです。

RSpecでのテスト

RSpecでの簡単なテストを一部抜粋。
ルートページで「新規登録」ボタンをおすと、
「ユーザーの新規登録」と記載されているページに遷移することを確認するテスト。

it 'ユーザー登録ページが表示される' do
  visit root_path
  click_on '新規登録'
  expect(page).to have_content 'ユーザーの新規登録'
end

下記エラーが発生。

Failure/Error: click_on '新規登録'

     Capybara::ElementNotFound:
       Unable to find visible link or button "新規登録"

slimには=link_to signup_path, class: 'nav-link' do | 新規登録のコードがあり、
ブラウザでページを表示させても「新規登録」ボタンは確かにあるのに、上記エラーが発生。

Bootstrapのtoggleが原因

テスト用のブラウザでは「新規登録」ボタンがtoggle化されていたため、
エラーが発生していた。

試しに、下記のように内容を変更してみる。

it '新規登録ボタンの確認' do
  visit root_path
  expect(page).to have_content '新規登録'
end
Failure/Error: expect(page).to have_content '新規登録'
       expected to find text "新規登録" in (略).
 (However, it was found 1 time including non-visible text.)

「non-visible text」としては「新規登録」があるとの指摘。

対応

toggleのアイコンをクリックするステップを入れる。

it 'ユーザー登録ページが表示される' do
  visit root_path
 find(".navbar-toggler-icon").click
  click_on '新規登録'
  expect(page).to have_content 'ユーザーの新規登録'
end

これで解決。

 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

devise のパスワードカラム名は「encrypted_pa​​ssword」だけど不可逆

「ハッシュ化」と「暗号化」

今まで

ハッシュ化 → 元に戻せないやつ。英語だと hash。
暗号化 → 元に戻せるやつ。英語だと encrypt。

だと思って生きてきました。
今でもそう思っています。

ところが、、、

devise のパスワードカラム名・・・encrypted_pa​​ssword!?

先日ふと気づいたのですが、gem devise のパスワードカラムの名前が「encrypted_pa​​ssword」になってます。

えっ、これってもしかして復号できちゃうの??

実装を見てみる

普通にハッシュ化してるっぽい。

/lib/devise/models/database_authenticatable.rb
      def password=(new_password)
        @password = new_password
        self.encrypted_password = password_digest(@password) if @password.present?
      end

      ...

      def password_digest(password)
        Devise::Encryptor.digest(self.class, password)
      end
/lib/devise/encryptor.rb
require 'bcrypt'

module Devise
  module Encryptor
    def self.digest(klass, password)
      if klass.pepper.present?
        password = "#{password}#{klass.pepper}"
      end
      ::BCrypt::Password.create(password, cost: klass.stretches).to_s
    end

    ...

まとめ

カラム名は「encrypted_pa​​ssword」だけどやってることはハッシュ化(不可逆)だから安心していい。

--

ちなみに、過去にカラム名をリネームする案も出たことがあるようですが、
「既存の利用者に与える影響でかすぎ。名前変更のリスクに見合うメリット無い(超意訳)」
との理由により close されていました。

The word 'encrypted_password' raises red flags; time to rename?
https://github.com/heartcombo/devise/issues/5025

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

bootstrapでdropdownが正常に動作しない場合

デベロッパーツールで見ると、cosoleのところに下記のようなエラーが出ていた。

Uncaught Error: Bootstrap's JavaScript requires jQuery at

要は先にjQueryを読み込めということらしいです。

<header>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</header>

上記の場所で下記を挿入してjqueryを読み込めば正常に動作するようになりました。

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】/users/:id ではなく/:username にする

理想

スクリーンショット 2020-03-05 16.54.21.png


やり方

to_paramを使用する

user.rb
class User < ActiveRecord::Base
  validates_presence_of :username
  validates_uniqueness_of :username, case_sensitive: false

  def to_param
    username
  end

users_controller.rb
class UsersController < ApplicationController
  def show
    @user = User.find_by(username: params[:id])
  end
end
routes.rb
  resources: users, path: '/', only: [:show]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】saveやcreateメソッドでのエラーが発生した時のエラーメッセージの出し方

saveメソッド

irb(main):005:0> user.save
   (0.3ms)  BEGIN
  User Exists (7.7ms)  SELECT  1 AS one FROM `users` WHERE `users`.`email` IS NULL LIMIT 1
   (0.2ms)  ROLLBACK
=> false

saveのみだとfalseが返されるのみ。エラーメッセージが出てこないので、エラー原因が特定できない。そういった場合saveの後に「!」をつけるとエラーメッセージが表示される。

irb(main):006:0> user.save!
   (0.3ms)  BEGIN
  User Exists (0.4ms)  SELECT  1 AS one FROM `users` WHERE `users`.`email` IS NULL LIMIT 1
   (0.1ms)  ROLLBACK
Traceback (most recent call last):
        1: from (irb):6
ActiveRecord::RecordInvalid (バリデーションに失敗しました: パスワードは3文字以上で入力してください, メールアドレスを入力してください, 名を入力してください, 姓を入力してください)

createメソッド

saveメソッドに!マークをつけるとエラーメッセージが出てくることを書いている記事は多いが、createメソッドについても同様に!マークをつけると、エラーメッセージが出る。

irb(main):008:0> User.create
   (0.1ms)  BEGIN
  User Exists (0.3ms)  SELECT  1 AS one FROM `users` WHERE `users`.`email` IS NULL LIMIT 1
   (0.1ms)  ROLLBACK
=> #<User id: nil, email: nil, crypted_password: nil, salt: nil, created_at: nil, updated_at: nil, first_name: nil, last_name: nil>

!マークを付けた場合エラーメッセージが出てくる。

irb(main):009:0> User.create!
   (0.2ms)  BEGIN
  User Exists (0.4ms)  SELECT  1 AS one FROM `users` WHERE `users`.`email` IS NULL LIMIT 1
   (0.1ms)  ROLLBACK
Traceback (most recent call last):
        1: from (irb):9
ActiveRecord::RecordInvalid (バリデーションに失敗しました: パスワードは3文字以上で入力してください, メールアドレスを入力してください, 名を入力してください, 姓を入力してください)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[rails] http ステータスコードのsymbol一覧確認方法メモ

rspecでレスポンス結果200のhttp statusである、という場合は下記のように書ける

expect(response).to have_http_status(200)
expect(response).to have_http_status(:ok)

下の:okといったsymbolのステータスコードを指定する場合、どんな値が定義されているか分からず都度調べたりしていたが、下記の定数に定義されているらしい

[5] pry(main)> Rack::Utils::SYMBOL_TO_STATUS_CODE
=> {:continue=>100,
 :switching_protocols=>101,
 :processing=>102,
 :ok=>200,
 :created=>201,

そもそもドキュメントの頭に書いてあったし、Railsでも使っているよね。。。

参考
https://relishapp.com/rspec/rspec-rails/docs/matchers/have-http-status-matcher

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RSpec&Rails少し複雑なテストのチートシート

RSpecでテストカバレッジを上げつつRailsのリファクタリングをしたい場合によく使います。

関数のパラメータをチェック

test.rb
it '#functionにパラメータparamを渡すこと' do
  allow(TargetClass).to receive(:function) {
    |object, param1|

    expect(param1).to eq 'this is parameter'
  }

  TargetClass.function('this is parameter')
end

パラメータが複数ある場合は |object, param1, param2, paramn| のように追加します。
関数の引数の数と一致しなくてもエラーは出ません。

ログのチェック

スタブを使うことで実現できます。
ログレベルはテストしたいログに合わせて設定します。

test.rb
it 'ログが出力される' do
  allow(logger).to receive(:info)

  TargetClass.output_log
  expect(logger).to have_received(:info).with('[INFO] 処理を開始しました')
  expect(logger).to have_received(:info).with('[INFO] 処理を終了しました')
end

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails + React + AjaxでCRUDのサンプルプロジェクト [Hello World]

React初心者が公式サイトで基礎を学んだ後に作るReact + AjaxによるCRUD(作成/読み込み/更新/削除)のサンプルプロジェクトです。
react_crud_1.png
Twitterのように「つぶやき」を投稿可能で編集/削除もできます。

動作確認はChrome、FireFox、Microsoft Edge、IE11です。恐らくマックさんのブラウザでも動作するはずです。

DEMO

https://www.petitmonte.com/rails-demo/react_crud

ソース一式

https://github.com/TakeshiOkamoto/mpp_react_crud

※学習用の為、ライセンスはパブリックドメイン

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Model#xxx_before_type_cast / Get instance / Not type casted inputs ( attributes ) / e.g invalid Date to be nil / #Rails

# == Schema Information
#
# Table name: users
#
#  id                       :bigint           not null, primary key
#  birth_date               :date             not null

class User < ApplicationRecord
end

# Valid Date
# type casted 
# be Date
User.new(birth_date: "19800229").birth_date
# => Fri, 29 Feb 1980

# valid Date
# before Type Casted
# be String
User.new(birth_date: "19800229").birth_date_before_type_cast
# => "19800229"

# Invalid Date
# type casted 
# be nil
User.new(birth_date: "19800230").birth_date

# Invalid Date
# Get not type casted input 
User.new(birth_date: "19800230").birth_date_before_type_cast
# => "19800230"

Other methods

User.new.methods.grep /birth_date/
=> [:birth_date=,
 :birth_date,
 :birth_date?,
 :birth_date_changed?,
 :birth_date_change,
 :birth_date_will_change!,
 :birth_date_was,
 :birth_date_previously_changed?,
 :birth_date_previous_change,
 :restore_birth_date!,
 :saved_change_to_birth_date?,
 :birth_date_before_last_save,
 :saved_change_to_birth_date,
 :birth_date_change_to_be_saved,
 :will_save_change_to_birth_date?,
 :birth_date_in_database,
 :birth_date_before_type_cast]

Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/3009

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

xxx_before_type_cast / #Rails で 不正な日付を ActiveRecord に渡すと nil に変換されてしまうため 、タイプキャストする前の入力値を得る

# == Schema Information
#
# Table name: users
#
#  id                       :bigint           not null, primary key
#  birth_date               :date             not null

class User < ApplicationRecord
end

# Valid Date
# type casted 
# be Date
User.new(birth_date: "19800229").birth_date
# => Fri, 29 Feb 1980

# valid Date
# before Type Casted
# be String
User.new(birth_date: "19800229").birth_date_before_type_cast
# => "19800229"

# Invalid Date
# type casted 
# be nil
User.new(birth_date: "19800230").birth_date

# Invalid Date
# Get not type casted input 
User.new(birth_date: "19800230").birth_date_before_type_cast
# => "19800230"

Other methods

User.new.methods.grep /birth_date/
=> [:birth_date=,
 :birth_date,
 :birth_date?,
 :birth_date_changed?,
 :birth_date_change,
 :birth_date_will_change!,
 :birth_date_was,
 :birth_date_previously_changed?,
 :birth_date_previous_change,
 :restore_birth_date!,
 :saved_change_to_birth_date?,
 :birth_date_before_last_save,
 :saved_change_to_birth_date,
 :birth_date_change_to_be_saved,
 :will_save_change_to_birth_date?,
 :birth_date_in_database,
 :birth_date_before_type_cast]

Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/3010

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails不定期連載】#1 DockerでRailsの環境構築

アウトプットの練習 & 初心に帰って基礎からRailsを見直してこうと思います。
不定期に更新します。

今回はRuby on Railsの環境構築。

今回はDockerでRailsチュートリアルを進めていくためのRailsプロジェクトを作ります。
rbenvなどを用いたローカルでの環境構築ではないです。

前置き

前提として...

  • 以下の場合はローカルでの環境構築を勧めます
    • railsの環境構築1度もしたことない
    • dockerを使った開発をしたことがない(Dockerfileの記述がわからない) ### アプリケーション要件
    • ruby 2.6.5
    • rails 6.2.0
    • sqlite3
    • webpacker、springはとりあえず不要なので除く

すごいざっくりですが、こんなイメージ、コンテナを作るのがゴールです。

レッツ環境構築

STEP1 rails newコマンドまでの準備

PJ用のディレクトリを作成してその中で作業していきます。

まずは必要になるファイルを用意しましょう。

touch Dockerfile docker-compose.yml Gemfile Gemfile.lock .gitignore

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

ruby '2.6.5'

gem 'rails', '~> 6.0.2', '>= 6.0.2.1'

Dockerfile
FROM ruby:2.6.5-alpine

ENV LANG="C.UTF-8" \
    PACKAGES="curl-dev build-base alpine-sdk tzdata sqlite-dev less ruby-dev nodejs"

RUN apk update && \
    apk add --no-cache --update $PACKAGES

WORKDIR /var/www

COPY ./ ./

RUN gem install bundler && \
    bundle install -j4

EXPOSE 3000

CMD ["rails", "server", "-b", "0.0.0.0", "-p", "3000"]

docker-compose.yml
version: '3'
services:
  app:
    build:
      context: ./
      dockerfile: Dockerfile
    volumes:
      - ./:/var/www
    ports:
      - "3000:3000"
    tty: true
    stdin_open: true
    restart: always

.gitignore
# Ignore bundler config.
/.bundle

# Ignore the default SQLite database.
/db/*.sqlite3
/db/*.sqlite3-journal
/db/*.sqlite3-*

# Ignore all logfiles and tempfiles.
/log/*
/tmp/*
!/log/.keep
!/tmp/.keep

# Ignore uploaded files in development.
/storage/*
!/storage/.keep

/public/assets
.byebug_history

# Ignore master key for decrypting credentials and more.
/config/master.key

.idea
.vscode
.DS_store

一旦ここまで作ってしまいます。
(.gitignoreはお好みあれば)

ここまで作ったらイメージを作成します。
docker-compose build

STEP2 Railsプロジェクトの作成、微修正

buildが完了したら、いよいよRailsプロジェクトを作成します。

-d sqlite -> 使用DBをsqlite3を選択
-B -> プロジェクト作成後のbundle installをしない(修正するからスキップで)
--skip-webpack-install -> プロジェクト作成後のwebpacker:installをしない
--skip-spring -> プロジェクト作成後のspringのinstallをしない

docker-compose run --rm app rails new -d sqlite3 -B --skip-webpack-install --skip-spring .

コマンド実行すると、いくつかのファイルがもう存在するけどどうする〜?みたいに聞かれます。
.gitignoreファイルは上書きなし、Gemfileは上書きしてしまいましょう。

    conflict  .gitignore
Overwrite /var/www/.gitignore? (enter "h" for help) [Ynaqdhm] n
        skip  .gitignore
    conflict  Gemfile
Overwrite /var/www/Gemfile? (enter "h" for help) [Ynaqdhm] Y
       force  Gemfile

Gemfileにあるwebpackerの記述を消しましょう

Gemfile
# 下記二行を削除
# Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
gem 'webpacker', '~> 4.0'

サーバー立ち上げた後にsw.jsが無いとエラーを吐きまくるので仮で作っておきましょう。
touch public/sw.js

ここまでくればもう少し、bundle installとdbの作成、マイグレーションを実行します。

その前にgemを更新するため、再度ビルドしてあげましょう。

docker-compose build

docker-compose run --rm app rails db:create

docker-compose run --rm app rails db:migrate

STEP3 Railsプロジェクトの起動

あとはコマンド一つでRailsサーバーが立ち上がります。
docker-compose up -d

http://localhost:3000 で立ち上がるのを確認しましょう。

hello Rails

やったぜ。

終わりに

出来上がりのソースコードはこちらに置いてあります。(変更あるかもしれませんが)

RubyMineを最近購入したのでこれからウッキウキでコード書こうと思います。

Railsに関しての記事をこれから不定期ですが粛々とアップしていきますのでよければ!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

1つの投稿で複数の画像をプレビュー表示させながら保存する

概要

Railsアプリケーションにて、投稿に紐付く画像を複数枚、プレビュー表示させながら投稿できる機能を実装したので備忘録としてまとめます。

ProductにImageが紐付きます。

ビューのところは特にもっといい方法があるんだろうなと思っておりますが、
改善点や間違いがあれば是非コメントいただけますと幸いです!

完成イメージ

スクリーンショット 2020-03-05 9.21.22.png

MySQL内
スクリーンショット 2020-03-04 21.14.25.png

マイグレーション

マイグレーションは通常通り・・・

class CreateProducts < ActiveRecord::Migration[5.2]
  def change
    create_table :products do |t|
      t.string :name, null: false
      t.timestamps
    end
  end
end
class CreateImages < ActiveRecord::Migration[5.2]
  def change
    create_table :images do |t|
      t.string :image
      t.references :product, null: false, foreign_key: true
      t.timestamps
    end
  end
end

モデル

accepts_nested_attributes_forにて「Productに紐付くImage」というネストの関係を作ることができます。

class Product < ApplicationRecord
  has_many :images, inverse_of: :product
  accepts_nested_attributes_for :images
end

inverse_ofはRails4.1以上の場合はデフォルト化させているとのことです。

class Image < ApplicationRecord
  belongs_to :product, inverse_of: :images
  mount_uploaders :image, ImageUploader
end

コントローラー

buildはnewと同じ役割です

_attibutesを用いてProductのparamsの中で一括で受け取るように記述します。

class ProductsController < ApplicationController

  def new
    @product = Product.new
    @product.images.build
  end

  def create
    @product = Product.new(product_params)
    if @product.save
      redirect_to root_path
    else
      render 'new'
    end    
  end

  private

  def product_params
    params.require(:product).permit(:name,images_attributes: {image: []}).merge(user_id: current_user.id)
  end

end

ビュー

今回は最大3枚までとしました。
画像数が多くなれば書き方はもっと工夫した方がいいのかなと思っております・・・

fields_forをつかうことでform_for内で異なるモデルを編集できるようになります。

  = form_for @product do |f|
    %p.upload__box-head 最大3枚までアップロードできます
      .upload__box-images
        = f.fields_for :images do |c|
          .upload__box-image
            %label{for: "image1"}
              = image_tag "pict/item_upload_dummy.png", id: "preview1", class: "preview-image"
            = c.file_field :image, multiple: true, id:"image1", type: "file", accept: "image/*", onchange: "previewImage1(this);"
          .upload__box-image
            %label{for: "image2"}
              = image_tag "pict/item_upload_dummy.png", id: "preview2", class: "preview-image"
            = c.file_field :image, multiple: true, id:"image2", type: "file", accept: "image/*", onchange: "previewImage2(this);"
          .upload__box-image
            %label{for: "image3"}
              = image_tag "pict/item_upload_dummy.png", id: "preview3", class: "preview-image"
            = c.file_field :image, multiple: true, id:"image3", type: "file", accept: "image/*", onchange: "previewImage3(this);"

JS

ここも1つにまとめられそうではありますね・・・

function previewImage1(obj){
  var fileReader = new FileReader();
  fileReader.onload = (function() {
    document.getElementById('preview1').src = fileReader.result;
  });
  fileReader.readAsDataURL(obj.files[0]);
}

function previewImage2(obj){
  var fileReader = new FileReader();
  fileReader.onload = (function() {
    document.getElementById('preview2').src = fileReader.result;
  });
  fileReader.readAsDataURL(obj.files[0]);
}

function previewImage3(obj){
  var fileReader = new FileReader();
  fileReader.onload = (function() {
    document.getElementById('preview3').src = fileReader.result;
  });
  fileReader.readAsDataURL(obj.files[0]);
}

以上となります!
なにぶん初心者のため、ここはこう書いたらリファクタリングできるよ!とかあればご教示いただけますと幸いです!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

rails sができない時の対処方

rails sを叩くとエラー出力

spring stopを試して解決できず少し詰まったので備忘録として残します。

/Users/arakitakuro/.rbenv/versions/2.5.1/lib/ruby/2.5.0/fileutils.rb:90: warning: already initialized constant FileUtils::VERSION
/Users/arakitakuro/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/fileutils-1.4.1/lib/fileutils.rb:105: warning: previous definition of VERSION was here
/Users/arakitakuro/.rbenv/versions/2.5.1/lib/ruby/2.5.0/fileutils.rb:1188: warning: already initialized constant FileUtils::Entry_::S_IF_DOOR
/Users/arakitakuro/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/fileutils-1.4.1/lib/fileutils.rb:1284: warning: previous definition of S_IF_DOOR was here
/Users/arakitakuro/.rbenv/versions/2.5.1/lib/ruby/2.5.0/fileutils.rb:1446: warning: already initialized constant FileUtils::Entry_::DIRECTORY_TERM
/Users/arakitakuro/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/fileutils-1.4.1/lib/fileutils.rb:1568: warning: previous definition of DIRECTORY_TERM was here
/Users/arakitakuro/.rbenv/versions/2.5.1/lib/ruby/2.5.0/fileutils.rb:1501: warning: already initialized constant FileUtils::OPT_TABLE
/Users/arakitakuro/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/fileutils-1.4.1/lib/fileutils.rb:1626: warning: previous definition of OPT_TABLE was here
/Users/arakitakuro/.rbenv/versions/2.5.1/lib/ruby/2.5.0/fileutils.rb:1555: warning: already initialized constant FileUtils::LOW_METHODS
/Users/arakitakuro/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/fileutils-1.4.1/lib/fileutils.rb:1685: warning: previous definition of LOW_METHODS was here
/Users/arakitakuro/.rbenv/versions/2.5.1/lib/ruby/2.5.0/fileutils.rb:1562: warning: already initialized constant FileUtils::METHODS
/Users/arakitakuro/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/fileutils-1.4.1/lib/fileutils.rb:1692: warning: previous definition of METHODS was here
=> Booting Puma
=> Rails 5.2.4.1 application starting in development 
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.12.4 (ruby 2.5.1-p57), codename: Llamas in Pajamas
* Min threads: 5, max threads: 5
* Environment: development
Exiting
Traceback (most recent call last):
    44: from bin/rails:3:in `<main>'
    43: from bin/rails:3:in `load'
以下略

原因

rails sしたままターミナルを閉じたのが原因でした。

解決方法

下記コマンドを入力し、出力されるPIDをコピー

lsof -i :3000 #PIDを確認

kill -QUIT PIDをペースト

これで解決しました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ログインshellの変更、そしてrbenvでインストールしたRubyをデフォルトにする方法

ゴールはrbenvでインストールしたRubyをデフォルトで開けること。
→その設定が書かれた.bash_profileというファイルを
 デフォルトで読み込んでもらえないというのが問題でした。

前回記事
https://qiita.com/hacchi_mom/items/2095e6ac7aecd03f131a

.bash_profileについて

まず.bash_profileってなんなんだというところから調べると
【ログインの際に読み込まれる設定ファイル】とのこと。

そして重要なのは、その設定ファイルは
【ログインshellがbashのときに読み込まれるもの】
ということ!!!!

ログインshell問題

ログインshellとはログイン時に起動するshellのことですが
そもそも私の起動時のログインshellはbashではなくzshだったのです。

Terminal起動時に毎回まずbashと一度打ってから
全ての作業を始めておりました…そういうもんだと思っていました。笑

初歩すぎること書いててすいません。。。
だから.bash_profileを読み込んでもらえなかったんですね。

ログインshellの変更への道

まずは以下のコマンドでbashを使う指示をしてみる。

$ chsh -s /usr/local/bin/bash
chsh: /usr/local/bin/bash: non-standard shell

早速拒否されたので笑
まず変更できるshellの一覧を出してみる。

$ cat /etc/shells
# List of acceptable shells for chpass(1).
# Ftpd will not allow users to connect who are not using
# one of these shells.

/bin/bash
/bin/csh
/bin/dash
/bin/ksh
/bin/sh
/bin/tcsh
/bin/zsh

先ほどのコマンドを有効にするためには、
この/etc/shellsというファイルに
/usr/local/bin/bashを追記しなければならないそうです。

そしてそのファイルに書く手段として検索して出てきたのが【vim】

正直初心者には操作性が難しすぎた…

私にはvim以外の書き方を検索する力がなく、
以下のリンクを参考にして奇跡的に書けました。
https://original-game.com/vim-mac2/#m_heading-1

※vimについてはまだ説明できる力がないので割愛します。。
 Finderでファイル名検索とかしても非表示ファイルだから出てこないですが、
 エディタで編集する方法も調べればありそうです!

そしてもう一度、変更できるshellの一覧

$ cat /etc/shells
# List of acceptable shells for chpass(1).
# Ftpd will not allow users to connect who are not using
# one of these shells.

/usr/local/bin/bash  ← これが無事追加されました!
/bin/bash
/bin/csh
/bin/dash
/bin/ksh
/bin/sh

そしてもう一度、最初に打ったbashを使う指示のコマンド

$ chsh -s /usr/local/bin/bash
chsh: WARNING: shell '/usr/local/bin/bash' does not exist
chsh: no changes made

さっきとエラー文が変わった!

これを調べるとコマンドではなく
ターミナル >環境設定 >一般 のなかに
【開くシェル】という設定がありました!

これをデフォルト → コマンドに設定を変更し、
以下の画像のように/bin/bashと打ち込みます。

スクリーンショット 2020-03-04 23.56.13.png

これで最後にもう一度最初のコマンドを

$ chsh -s /usr/local/bin/bash
Changing shell for local.
Password for local:

(↑ちなみに最初の2回は割愛しましたが
このコマンド打つと毎回パスワードを求められます。)

そしてターミナルを再起動すると
ログインshellはbashになっていました!

そして私のゴールだったRubyは…

$ which ruby
/Users/local/.rbenv/shims/ruby

できました!
これでrbenvでインストールしたRubyがデフォルトになりました。

参考記事

https://wa3.i-3-i.info/word13650.html
https://teratail.com/questions/59821
https://www.task-notes.com/entry/20150117/1421482066
https://qiita.com/n_oshiumi/items/5ea418bed44fb81dd653
https://creepfablic.site/2019/10/13/bash-zsh/#index_id4

たくさんの記事に助けられました!
長々と読んでくださった方、ありがとうございました!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む