- 投稿日:2020-03-05T23:16:02+09:00
もう悩まない!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
全体の流れ
この順番でインストールしていきます。
- rbenv(Rubyのバージョン管理ツール)
- Ruby
- PostgreSQL(データベース)
- 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 --installXcodeというツールをインストールしていない場合は「インストールしますか?」という画面が表示されるため、「インストール」を選択します。
Xcodeは重いので、少し時間がかかるかもしれません。インストール完了後、次のコマンドでバージョンが表示されたら成功です。
# バージョン確認 $ xcodebuild -version1-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 cooklog2-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.7Rubyのバージョンは、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 bundler3. 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 installRailsのバージョンを確認し、きちんとインストールできていることを確認しましょう。
$ 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/にアクセスしてみましょう。この画面が表示されれば、きちんとRailsアプリが立ち上がっています!
Rails 5.2.3
、Ruby 2.5.7
が適用されていることがわかりますね。これにて環境構築はおしまいです。
お疲れ様でした!
- 投稿日:2020-03-05T23:16:02+09:00
【完全版】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
全体の流れ
この順番でインストールしていきます。
- rbenv(Rubyのバージョン管理ツール)
- Ruby
- PostgreSQL(データベース)
- 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 --installXcodeというツールをインストールしていない場合は「インストールしますか?」という画面が表示されるため、「インストール」を選択します。
Xcodeは重いので、少し時間がかかるかもしれません。インストール完了後、次のコマンドでバージョンが表示されたら成功です。
# バージョン確認 $ xcodebuild -version1-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 cooklog2-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.7Rubyのバージョンは、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 bundler3. 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 installRailsのバージョンを確認し、きちんとインストールできていることを確認しましょう。
$ 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/にアクセスしてみましょう。この画面が表示されれば、きちんとRailsアプリが立ち上がっています!
Rails 5.2.3
、Ruby 2.5.7
が適用されていることがわかりますね。これにて環境構築はおしまいです。
お疲れ様でした!
- 投稿日:2020-03-05T23:16:02+09:00
【これで完璧!】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
全体の流れ
この順番でインストールしていきます。
- rbenv(Rubyのバージョン管理ツール)
- Ruby
- PostgreSQL(データベース)
- 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 --installXcodeというツールをインストールしていない場合は「インストールしますか?」という画面が表示されるため、「インストール」を選択します。
Xcodeは重いので、少し時間がかかるかもしれません。インストール完了後、次のコマンドでバージョンが表示されたら成功です。
# バージョン確認 $ xcodebuild -version1-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 cooklog2-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.7Rubyのバージョンは、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 bundler3. 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 installRailsのバージョンを確認し、きちんとインストールできていることを確認しましょう。
$ 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/にアクセスしてみましょう。この画面が表示されれば、きちんとRailsアプリが立ち上がっています!
Rails 5.2.3
、Ruby 2.5.7
が適用されていることがわかりますね。これにて環境構築はおしまいです。
お疲れ様でした!
- 投稿日:2020-03-05T23:16:02+09:00
【これで完璧!】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
全体の流れ
この順番でインストールしていきます。
- rbenv(Rubyのバージョン管理ツール)
- Ruby
- PostgreSQL(データベース)
- 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 --installXcodeというツールをインストールしていない場合は「インストールしますか?」という画面が表示されるため、「インストール」を選択します。
Xcodeは重いので、少し時間がかかるかもしれません。インストール完了後、次のコマンドでバージョンが表示されたら成功です。
# バージョン確認 $ xcodebuild -version1-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 cooklog2-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.7Rubyのバージョンは、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 bundler3. 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 installRailsのバージョンを確認し、きちんとインストールできていることを確認しましょう。
$ 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/にアクセスしてみましょう。この画面が表示されれば、きちんとRailsアプリが立ち上がっています!
Rails 5.2.3
、Ruby 2.5.7
が適用されていることがわかりますね。これにて環境構築はおしまいです。
お疲れ様でした!
- 投稿日:2020-03-05T22:39:37+09:00
Railsのenumを読む
Railsのソースコードを読みます。
enumに関して
class User < ApplicationRecord binding.pry enum status: { active: 0, inactive: 1 } endbindingを使ってコードの中に入っていく。
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: endvaluesがハッシュか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: endvaluesがハッシュかつ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: enddetect_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) : namedefined_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: enddetect_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: endmatcherに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: endcacheから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_prefixenum_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: endnameや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: endscopeとして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.freezenot_#{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: endscopeとしてnot_#{value_method_name}メソッドを定義してwhere.not(attr => value)を行う。
これでenumのソースコードリーディング終了。
- 投稿日:2020-03-05T22:30:58+09:00
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
- 投稿日:2020-03-05T20:30:53+09:00
[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.lockGEM 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のバージョンに変更することができます
- 投稿日:2020-03-05T20:23:57+09:00
Rails on HerokuなアプリからFirestoreにアクセスしたい場合の実装例&ハマりどころ
背景
Firestoreを使用する場合、大抵はフロントエンドから直接クエリを発行してDB操作を行うのだろうが、実装者のスキルセットや要件によってはFirestoreの操作に関する処理をサーバーサイドに置きたい場合もある。例えば以下のような事例が見つかった。
- 既にREST API/RDSが存在しており、部分的にFirestoreを利用したハイブリッドな構成にしたい場合
- 小規模なアプリの開発時、ちょっとしたデータ置き場としてFirestoreを利用したい場合
- フロントエンドにDBへの書き込み処理を実装することによって生じる実装の煩雑さを回避したい場合
上記の事例の全てに該当する点として、サーバーサイドは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に定義する。
Procfileweb: bundle exec rails sProcfile.devweb: 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 endapp/views/pages/index.html.erb<div id="app"></div>config/routes.rb# frozen_string_literal: true Rails.application.routes.draw do root 'pages#index' endJSファイルを読み込ませるため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.tsximport React, { FC } from "react"; interface Props { name: string; } export const Hello: FC<Props> = ({ name }) => { return <div>Hello{name}!</div>; };app/javascript/index.tsximport 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。
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.devweb: 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をいじるだけです。
- 投稿日:2020-03-05T19:56:29+09:00
【Rails】Rakeタスクに:environmentがないとNameErrorが出る
エラー
Rakeタスクを実行したときに、
NameError: uninitialized constant ClassName
が出る。send_summary_mail.rakenamespace :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 c
でSummaryMailer.daily.deliver_now
を実行する。
→うまくいく。ということは、SummaryMailer
自体には問題はなさそう。send_summary_mail.rakerequire Rails.root.join("app/mailers/summary_mailer")を追加する。
→NameError: uninitialized constant ApplicationMailer
根本的にダメそう。send_summary_mail.rakenamespace :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 について
- 投稿日:2020-03-05T18:09:32+09:00
【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 '新規登録' endFailure/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これで解決。
- 投稿日:2020-03-05T18:01:28+09:00
devise のパスワードカラム名は「encrypted_password」だけど不可逆
「ハッシュ化」と「暗号化」
今まで
ハッシュ化 → 元に戻せないやつ。英語だと hash。
暗号化 → 元に戻せるやつ。英語だと encrypt。だと思って生きてきました。
今でもそう思っています。ところが、、、
devise のパスワードカラム名・・・encrypted_password!?
先日ふと気づいたのですが、gem devise のパスワードカラムの名前が「encrypted_password」になってます。
えっ、これってもしかして復号できちゃうの??
実装を見てみる
普通にハッシュ化してるっぽい。
/lib/devise/models/database_authenticatable.rbdef 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.rbrequire '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_password」だけどやってることはハッシュ化(不可逆)だから安心していい。
--
ちなみに、過去にカラム名をリネームする案も出たことがあるようですが、
「既存の利用者に与える影響でかすぎ。名前変更のリスクに見合うメリット無い(超意訳)」
との理由により close されていました。The word 'encrypted_password' raises red flags; time to rename?
https://github.com/heartcombo/devise/issues/5025
- 投稿日:2020-03-05T17:40:02+09:00
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>
- 投稿日:2020-03-05T17:07:15+09:00
【Rails】/users/:id ではなく/:username にする
理想
やり方
to_paramを使用する
user.rbclass User < ActiveRecord::Base validates_presence_of :username validates_uniqueness_of :username, case_sensitive: false def to_param username end
users_controller.rbclass UsersController < ApplicationController def show @user = User.find_by(username: params[:id]) end endroutes.rbresources: users, path: '/', only: [:show]
- 投稿日:2020-03-05T16:26:52+09:00
【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 => falsesaveのみだと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文字以上で入力してください, メールアドレスを入力してください, 名を入力してください, 姓を入力してください)
- 投稿日:2020-03-05T15:45:46+09:00
[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
- 投稿日:2020-03-05T15:07:52+09:00
RSpec&Rails少し複雑なテストのチートシート
RSpecでテストカバレッジを上げつつRailsのリファクタリングをしたい場合によく使います。
関数のパラメータをチェック
test.rbit '#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.rbit 'ログが出力される' 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参考
- 投稿日:2020-03-05T13:42:36+09:00
Rails + React + AjaxでCRUDのサンプルプロジェクト [Hello World]
React初心者が公式サイトで基礎を学んだ後に作るReact + AjaxによるCRUD(作成/読み込み/更新/削除)のサンプルプロジェクトです。
Twitterのように「つぶやき」を投稿可能で編集/削除もできます。動作確認はChrome、FireFox、Microsoft Edge、IE11です。恐らくマックさんのブラウザでも動作するはずです。
DEMO
https://www.petitmonte.com/rails-demo/react_crud
ソース一式
https://github.com/TakeshiOkamoto/mpp_react_crud
※学習用の為、ライセンスはパブリックドメイン
- 投稿日:2020-03-05T11:04:29+09:00
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
- 投稿日:2020-03-05T11:04:19+09:00
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
- 投稿日:2020-03-05T10:27:13+09:00
【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
Gemfilesource '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'DockerfileFROM 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.ymlversion: '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 GemfileGemfileにある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 で立ち上がるのを確認しましょう。
やったぜ。
終わりに
出来上がりのソースコードはこちらに置いてあります。(変更あるかもしれませんが)
RubyMineを最近購入したのでこれからウッキウキでコード書こうと思います。
Railsに関しての記事をこれから不定期ですが粛々とアップしていきますのでよければ!
- 投稿日:2020-03-05T09:25:18+09:00
1つの投稿で複数の画像をプレビュー表示させながら保存する
概要
Railsアプリケーションにて、投稿に紐付く画像を複数枚、プレビュー表示させながら投稿できる機能を実装したので備忘録としてまとめます。
ProductにImageが紐付きます。
ビューのところは特にもっといい方法があるんだろうなと思っておりますが、
改善点や間違いがあれば是非コメントいただけますと幸いです!完成イメージ
マイグレーション
マイグレーションは通常通り・・・
class CreateProducts < ActiveRecord::Migration[5.2] def change create_table :products do |t| t.string :name, null: false t.timestamps end end endclass 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 endinverse_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]); }以上となります!
なにぶん初心者のため、ここはこう書いたらリファクタリングできるよ!とかあればご教示いただけますと幸いです!
- 投稿日:2020-03-05T07:50:26+09:00
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をペーストこれで解決しました。
- 投稿日:2020-03-05T00:48:58+09:00
ログイン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
と打ち込みます。これで最後にもう一度最初のコマンドを
$ 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たくさんの記事に助けられました!
長々と読んでくださった方、ありがとうございました!