- 投稿日:2020-02-08T22:37:21+09:00
READMEとは説明書。まず最初に読んでほしい
リードミー(Readme)とは、ソフトウェアを配布する際の添付文書のひとつ。配布物の一般的な情報を記載したファイルである。多くの場合、そのソフトウェアをインストールし使用する前に読むべきものとされている。(引用元:Wikipedia)
自分でテンプレートを作る、他人のテンプレートを使うことで作業効率アップ!
そのソフトウェアでなにができるのか、端的にシンプルな記述。
参考
https://cpp-learning.com/readme/
////rails修行中の大きなぼやき////
- 投稿日:2020-02-08T22:03:17+09:00
MacでRuby on Railsの環境構築チートシート(http://localhost:3000/を立ち上げるまで)
MacでRuby on Railsの環境構築するときの簡単な手順です!とりあえず、何もないところからrails sでサーバーを立ち上げるまでです!
homebrewをインストール
homebrewでMySQLをインストール
$ brew install mysql
MySQL起動
$ mysql.server start
Starting MySQL
SUCCESS!homebrewを使用して、rbenvおよびruby-build(プラグイン)をインストール
rbenvインストール
$ brew install rbenv
$ brew install ruby-build
・ruby-buildは、rbenvのプラグイン。(
$rbenv install
コマンドを使うためのプラグイン)
・rbenv installコマンドで、rubyのver2.6.4をインストール※注意:
○Homebrewのパスを通す必要あり
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
$ echo 'if which rbenv > /dev/null; then eval "$(rbenv init -)"; fi' >> ~/.bash_profile
$ source ~/.bash_profile
→このコマンドで設定を保存Rubyをインストール
rbenvでrubyのバージョン設定
$ rbenv version
を実行し、rbenvで管理されているrubyのバージョンを確認→開発で使用したい該当のバージョンがなければインストールする必要あり。
$ rbenv install 2.6.5
(バージョンは一例)※rubyのインストール時にエラーが出た場合
If you don't have the version you need, try upgrading ruby-build: brew update && brew upgrade ruby-buildこのエラーが出れば、指摘通り以下のコマンド実行
$brew update
$brew upgrade ruby-build
その後、もう一度トライすると上手くいく
$rbenv install 2.6.5
$ rbenv install 2.6.1
→rbenvでrubyをインストールする
$ rbenv global 2.6.1
→rbenvのバージョンを切り替える
$ rbenv rehash
→反映
$ ruby -v
→rubyバージョンを確認gemのインストール
$ gem install bundler
→bundlerインストール$ bundle -v
→bundlerのバージョンを確認補足
・gemを管理するシステムがRubyGems
・RubyGemsはRubyに標準添付されているシステムなのでRubyが使える状態ならすぐに使うことができる。→rubyに同梱(セット)
→同梱のバージョン確認には
$ gem update --system
・bundlerはgemの一種で、gemをデフォルトのRubyGemsより効率よく管理できるツール。まず最初に、RubyGemsを使ってbundlerをインストール
・bundlerを使ってgemをインストール
・BundlerはGemfileをもとにgemをインストールする。
・gemをシステムにインストールするのではなく、ディレクトリ内で管理する。
・そのためにpathオプションをつける。
$ bundle install --path vendor/bundle
でpathオプションをつける。このことにより、コマンドの頭にbundle execコマンドをつけて、実行する。
・railsもgemの一種
$ bundle install --path vendor/bundle
※以下のようなbundlerのバージョンエラーが出たら
To update to the latest version installed on your system, run `bundle update --bundler`. To install the missing version, run `gem install bundler:2.0.2→
$ gem install bundler
で最新のbundlerをインストール
→$ rbenv rehash
→$ bundle -v
でbundlerのバージョン確認
→その後もう一度
$ bundle install --path vendor/bundle
Rails設定
- 新アプリ名のディレクトリを作成
$ cd ~/<ディレクトリ名>
コマンドで、アプリディレクトリに移動$ bundle init
→Gemfileを作成できあがったGemfileのRailsのコメントアウトを外す
gem "rails"
→Gemfileでrailsのバージョンを確認Railsインストール
$ bundle install --path=vendor/bundle
rails new
$ cd ~/<ディレクトリ名>
で、アプリのディレクトリに移動
$ bundle exec rails new . -d mysql --skip-turbolinks --skip-test --skip-coffee
→.はカレントディレクトリを意味する
→gemfileの上書きを聞かれた場合は、yes入力でgemfileを上書き
→うまく行かなければ
$ bundle update
※ mysqlエラー出た場合
以下をやってみる
$ bundle config --local build.mysql2 "--with-ldflags=-L/usr/local/opt/openssl/lib"
$ bundle install
gemのインストール(もう一回)
$ bundle install --path=vendor/bundle
nodeを使う場合(nodeを使わないのであれば飛ばしてください)
nodeのバージョンなどをいじる場合は以下の記事を参照
→https://qiita.com/kenkentarou/items/f27bd4e49af6fe429429※nodeとは
関係性
ruby <-> node
rbenv <-> nodebrewやnvmnodeは一つの言語(node.jsと同義)で、nodeは、言語としても扱うし、サーバーサイドで動くためにコンパイルするシステムとしても使われる
データベースの設定
データベースの設定ファイルを作成
通常の現場では、datebase.yml.defaultというファイルを作成して、設定情報はrootなどセキュリティー上影響ない初期値を入れておき、こちらをgit管理します。
ローカルで作業する際には、datebase.yml.defaultをコピーして、database.ymlというファイルを作成し、自身のPC上の設定に合わせ修正したものを使います。自身の設定に合わせたdatabase.ymlを作成しないと、ローカルでデータベースを走らせることができません。
以下のコマンドを実行し、database.yml.defaultをコピーして、自身の環境に合わせたdatabase.ymlを作成しましょう。
$ cp config/database.yml.default config/database.yml
- database.ymlの設定を自身の環境に合わせて修正 ユーザー名やパスワードの設定
ローカル上にデータベーステーブル作成
$ bundle exec rails db:create
→データベーステーブル作成
$bundle exec rails db:migrate
→migrationが必要なファイルがあれば実行サーバー立ち上げ
bundle exec rails sコマンドを実行し、railsサーバーの立ち上げ。
localhost:3000にアクセスし、完成形にある画面が表示されることを確認
git flowでの管理の場合
git flowがPCに入っている前提で、
$git flow init
→アプリのディレクトリ全体がgit管理対象になる
→→ローカルにmasterとdevelopがbranchが出来るプッシュする前に確認すること
.gitignoreに入れておかなければいけないファイル、ディレクトリが無いかどうか
ex)
/vender等リモートリポジトリにプッシュする
$ git add .
$ git commit -m "inital commit"
$ git remote add origin https://github.com/hoge/hoge.git
$git push --all
→ローカルにあるdevelopとmasterの両方をリモートにあげるレビューをしやすくするためにrails newの段階でgit flow initをしてmasterとdevelopブランチを自身のパブリックリポジトリに対してプッシュしています!
(PCにgit flow入れてなければ
$ brew install git-flow
)
- 投稿日:2020-02-08T22:03:17+09:00
【初学者向け】MacでRuby on Railsの環境構築(http://localhost:3000/を立ち上げるまで)
MacでRuby on Railsの環境構築するときの簡単な手順です!とりあえず、何もないところからrails sでサーバーを立ち上げるまでです!
homebrewをインストール
homebrewでMySQLをインストール
$ brew install mysql
MySQL起動
$ mysql.server start
Starting MySQL
SUCCESS!homebrewを使用して、rbenvおよびruby-buildをインストール(インストール済みの場合は不要)
rbenvインストール
$ brew install rbenv
$ brew install ruby-build
・ruby-buildは、rbenvのプラグイン。(
$rbenv install
コマンドを使うためのプラグイン)
・この後、$ rbenv install
コマンドで、rubyをインストール※注意:
○Homebrewのパスを通す必要あり$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile$ echo 'if which rbenv > /dev/null; then eval "$(rbenv init -)"; fi' >> ~/.bash_profile$ source ~/.bash_profile→このコマンドで設定を保存
Rubyをインストール
rbenvでrubyをインストール
$ rbenv version
を実行し、rbenvで管理されているrubyのバージョンを確認→開発で使用したい該当のバージョンがなければインストールする必要あり。
$ rbenv install 2.6.5
(バージョンは一例)
$ rbenv global 2.6.1
→rbenvのバージョンを切り替える
$ rbenv rehash
→反映
$ ruby -v
→rubyバージョンを確認※rubyのインストール時にエラーが出た場合
If you don't have the version you need, try upgrading ruby-build: brew update && brew upgrade ruby-buildこのエラーが出れば、指摘通り以下のコマンド実行
$brew update
$brew upgrade ruby-build
その後、もう一度トライすると上手くいく
$rbenv install 2.6.5
$ rbenv install 2.6.1
→rbenvでrubyをインストールするRailsを設定
- 新アプリ名のディレクトリを作成
$ cd ~/<ディレクトリ名>
コマンドで、アプリディレクトリに移動$ bundle init
→Gemfileを作成できあがったGemfileのRailsのコメントアウトを外す
gem "rails"
→Gemfileでrailsのバージョンを確認bundlerのインストール
$ gem install bundler
$ bundle -v
→bundlerのバージョンを確認補足
・gemを管理するシステムがRubyGems
・RubyGemsはRubyに標準添付されているシステムなのでRubyが使える状態ならすぐに使うことができる。→rubyに同梱(セット)
→同梱のバージョン確認には
$ gem update --system
・bundlerはgemの一種で、gemをデフォルトのRubyGemsより効率よく管理できるツール。まず最初に、RubyGemsを使ってbundlerをインストール
・bundlerを使ってgemをインストール
・BundlerはGemfileをもとにgemをインストールする。
・gemをシステムにインストールするのではなく、ディレクトリ内で管理する。
・そのためにpathオプションをつける。
$ bundle install --path vendor/bundle
でpathオプションをつける。このことにより、コマンドの頭にbundle execコマンドをつけて、実行する。
・railsもgemの一種Rails(gem)のインストール
$ bundle install --path vendor/bundle
※もし以下のようなbundlerのバージョンエラーが出たら
To update to the latest version installed on your system, run `bundle update --bundler`. To install the missing version, run `gem install bundler:2.0.2→
$ gem install bundler
で最新のbundlerをインストール
→$ rbenv rehash
→$ bundle -v
でbundlerのバージョン確認
→その後もう一度
$ bundle install --path vendor/bundle
rails new
$ cd ~/<ディレクトリ名>
で、アプリのディレクトリに移動
$ bundle exec rails new . -d mysql --skip-turbolinks --skip-test --skip-coffee
→.はカレントディレクトリを意味する
→gemfileの上書きを聞かれた場合は、yes入力でgemfileを上書き
→うまく行かなければ
$ bundle update
※ mysqlエラー出た場合
以下をやってみる
$ bundle config --local build.mysql2 "--with-ldflags=-L/usr/local/opt/openssl/lib"
$ bundle install
gemのインストール(もう一回)
$ bundle install --path=vendor/bundle
nodeを使う場合(nodeを使わないのであれば飛ばしてください)
nodeのバージョンなどをいじる場合は以下の記事を参照
→https://qiita.com/kenkentarou/items/f27bd4e49af6fe429429※nodeとは
関係性
ruby <-> node
rbenv <-> nodebrewやnvmnodeは一つの言語(node.jsと同義)で、nodeは、言語としても扱うし、サーバーサイドで動くためにコンパイルするシステムとしても使われる
データベースの設定
データベースの設定ファイルを作成
通常の現場では、datebase.yml.defaultというファイルを作成して、設定情報はrootなどセキュリティー上影響ない初期値を入れておき、こちらをgit管理します。
ローカルで作業する際には、datebase.yml.defaultをコピーして、database.ymlというファイルを作成し、自身のPC上の設定に合わせ修正したものを使います。自身の設定に合わせたdatabase.ymlを作成しないと、ローカルでデータベースを走らせることができません。
以下のコマンドを実行し、database.yml.defaultをコピーして、自身の環境に合わせたdatabase.ymlを作成しましょう。
$ cp config/database.yml.default config/database.yml
- database.ymlの設定を自身の環境に合わせて修正 ユーザー名やパスワードの設定
ローカル上にデータベーステーブル作成
$ bundle exec rails db:create
→データベーステーブル作成
$bundle exec rails db:migrate
→migrationが必要なファイルがあれば実行サーバー立ち上げ
bundle exec rails sコマンドを実行し、railsサーバーの立ち上げ。
localhost:3000にアクセスし、完成形画面が表示されることを確認
git flowでの管理の場合
git flowがPCに入っている前提で、
(PCにgit flow入れてなければ$ brew install git-flow
でインストール)
$git flow init
→アプリのディレクトリ全体がgit管理対象になる
→→ローカルにmasterとdevelopがbranchが出来るプッシュする前に確認すること
.gitignoreに入れておかなければいけないファイル、ディレクトリが無いかどうか
ex) /vender等
リモートリポジトリにプッシュする
$ git add .
$ git commit -m "inital commit"
$ git remote add origin https://github.com/hoge/hoge.git
$git push --all
→ローカルにあるdevelopとmasterの両方をリモートにあげるレビューをしやすくするためにrails newの段階でgit flow initをしてmasterとdevelopブランチを自身のパブリックリポジトリに対してプッシュしています!
- 投稿日:2020-02-08T22:03:17+09:00
MacでRuby on Railsの環境構築(http://localhost:3000/を立ち上げるまで)
MacでRuby on Railsの環境構築するときの簡単な手順です!とりあえず、何もないところからrails sでサーバーを立ち上げるまでです!
homebrewをインストール
homebrewでMySQLをインストール
$ brew install mysql
MySQL起動
$ mysql.server start
Starting MySQL
SUCCESS!homebrewを使用して、rbenvおよびruby-build(プラグイン)をインストール
rbenvインストール
$ brew install rbenv
$ brew install ruby-build
・ruby-buildは、rbenvのプラグイン。(
$rbenv install
コマンドを使うためのプラグイン)
・rbenv installコマンドで、rubyのver2.6.4をインストール※注意:
○Homebrewのパスを通す必要あり
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
$ echo 'if which rbenv > /dev/null; then eval "$(rbenv init -)"; fi' >> ~/.bash_profile
$ source ~/.bash_profile
→このコマンドで設定を保存Rubyをインストール
rbenvでrubyのバージョン設定
$ rbenv version
を実行し、rbenvで管理されているrubyのバージョンを確認→開発で使用したい該当のバージョンがなければインストールする必要あり。
$ rbenv install 2.6.5
(バージョンは一例)※rubyのインストール時にエラーが出た場合
If you don't have the version you need, try upgrading ruby-build: brew update && brew upgrade ruby-buildこのエラーが出れば、指摘通り以下のコマンド実行
$brew update
$brew upgrade ruby-build
その後、もう一度トライすると上手くいく
$rbenv install 2.6.5
$ rbenv install 2.6.1
→rbenvでrubyをインストールする
$ rbenv global 2.6.1
→rbenvのバージョンを切り替える
$ rbenv rehash
→反映
$ ruby -v
→rubyバージョンを確認gemのインストール
$ gem install bundler
→bundlerインストール$ bundle -v
→bundlerのバージョンを確認補足
・gemを管理するシステムがRubyGems
・RubyGemsはRubyに標準添付されているシステムなのでRubyが使える状態ならすぐに使うことができる。→rubyに同梱(セット)
→同梱のバージョン確認には
$ gem update --system
・bundlerはgemの一種で、gemをデフォルトのRubyGemsより効率よく管理できるツール。まず最初に、RubyGemsを使ってbundlerをインストール
・bundlerを使ってgemをインストール
・BundlerはGemfileをもとにgemをインストールする。
・gemをシステムにインストールするのではなく、ディレクトリ内で管理する。
・そのためにpathオプションをつける。
$ bundle install --path vendor/bundle
でpathオプションをつける。このことにより、コマンドの頭にbundle execコマンドをつけて、実行する。
・railsもgemの一種
$ bundle install --path vendor/bundle
※以下のようなbundlerのバージョンエラーが出たら
To update to the latest version installed on your system, run `bundle update --bundler`. To install the missing version, run `gem install bundler:2.0.2→
$ gem install bundler
で最新のbundlerをインストール
→$ rbenv rehash
→$ bundle -v
でbundlerのバージョン確認
→その後もう一度
$ bundle install --path vendor/bundle
Rails設定
- 新アプリ名のディレクトリを作成
$ cd ~/<ディレクトリ名>
コマンドで、アプリディレクトリに移動$ bundle init
→Gemfileを作成できあがったGemfileのRailsのコメントアウトを外す
gem "rails"
→Gemfileでrailsのバージョンを確認Railsインストール
$ bundle install --path=vendor/bundle
rails new
$ cd ~/<ディレクトリ名>
で、アプリのディレクトリに移動
$ bundle exec rails new . -d mysql --skip-turbolinks --skip-test --skip-coffee
→.はカレントディレクトリを意味する
→gemfileの上書きを聞かれた場合は、yes入力でgemfileを上書き
→うまく行かなければ
$ bundle update
※ mysqlエラー出た場合
以下をやってみる
$ bundle config --local build.mysql2 "--with-ldflags=-L/usr/local/opt/openssl/lib"
$ bundle install
gemのインストール(もう一回)
$ bundle install --path=vendor/bundle
nodeを使う場合(nodeを使わないのであれば飛ばしてください)
nodeのバージョンなどをいじる場合は以下の記事を参照
→https://qiita.com/kenkentarou/items/f27bd4e49af6fe429429※nodeとは
関係性
ruby <-> node
rbenv <-> nodebrewやnvmnodeは一つの言語(node.jsと同義)で、nodeは、言語としても扱うし、サーバーサイドで動くためにコンパイルするシステムとしても使われる
データベースの設定
データベースの設定ファイルを作成
通常の現場では、datebase.yml.defaultというファイルを作成して、設定情報はrootなどセキュリティー上影響ない初期値を入れておき、こちらをgit管理します。
ローカルで作業する際には、datebase.yml.defaultをコピーして、database.ymlというファイルを作成し、自身のPC上の設定に合わせ修正したものを使います。自身の設定に合わせたdatabase.ymlを作成しないと、ローカルでデータベースを走らせることができません。
以下のコマンドを実行し、database.yml.defaultをコピーして、自身の環境に合わせたdatabase.ymlを作成しましょう。
$ cp config/database.yml.default config/database.yml
- database.ymlの設定を自身の環境に合わせて修正 ユーザー名やパスワードの設定
ローカル上にデータベーステーブル作成
$ bundle exec rails db:create
→データベーステーブル作成
$bundle exec rails db:migrate
→migrationが必要なファイルがあれば実行サーバー立ち上げ
bundle exec rails sコマンドを実行し、railsサーバーの立ち上げ。
localhost:3000にアクセスし、完成形にある画面が表示されることを確認
git flowでの管理の場合
git flowがPCに入っている前提で、
$git flow init
→アプリのディレクトリ全体がgit管理対象になる
→→ローカルにmasterとdevelopがbranchが出来るプッシュする前に確認すること
.gitignoreに入れておかなければいけないファイル、ディレクトリが無いかどうか
ex)
/vender等リモートリポジトリにプッシュする
$ git add .
$ git commit -m "inital commit"
$ git remote add origin https://github.com/hoge/hoge.git
$git push --all
→ローカルにあるdevelopとmasterの両方をリモートにあげるレビューをしやすくするためにrails newの段階でgit flow initをしてmasterとdevelopブランチを自身のパブリックリポジトリに対してプッシュしています!
(PCにgit flow入れてなければ
$ brew install git-flow
)
- 投稿日:2020-02-08T21:12:59+09:00
Net::HTTPメソッドを使ってBasic認証を突破しよう
こんにちは。
今回、Rubyでgemを使わずにNet::HTTPメソッドを使ってBasic認証を突破し、スクレイピングしました。あまり文献がなかったので、誰かの参考になればと思いこの記事を書いた次第です。
Rubyのバージョンは2.6.5です。
1行1行丁寧に解説していこうと思います。コードだけ知りたい!という方は完成形のコードを参考に実行してみてください。
完成形のコードはこちら↓scraping.rbrequire "net/http" username, password = "ユーザー名", "パスワード" uri = URI.parse("スクレイピングしたいページのURL") https = Net::HTTP.new(uri.host, uri.port) https.use_ssl = true http_get = Net::HTTP::Get.new(uri.path) http_get.basic_auth(username, password) response = https.request(http_get) response.bodyそれでは、それぞれ一行ずつ、何をしているのか確認していきます。
net/httpライブラリを読み込む
require “net/http”net/httpとは、rubyにデフォルトで備わっているライブラリで汎用データ転送プロトコルHTTPを扱うライブラリです。
汎用データ転送プロトコルとは、要するにホームページを構成しているhtmlファイルや画像ファイルなどの「ホームページの部品」を自分のパソコンにデータとしてダウンロードする手順のことです。HTTPという共通の通信規約を定めることで、インターネットを利用する環境が異なっていても、同じ手順でホームページのデータをやり取りすることができます。
以下の記事を参考にしました。
https://cybersecurity-jp.com/security-measures/25772ユーザー名・パスワードを変数に代入
username, password = “ユーザー名”, “パスワード”URIを作成
uri = URI.parse(“スクレイピングしたいページのURL")URIモジュールのparseメソッドを呼び出し、引数にURLを文字列として与えています。parseメソッドは、与えられたURIから該当するURI::Genericのサブクラスのインスタンスを生成して返します。それを変数uriに代入しています。
※URIとは、URL(web上の住所)とURN(we上での名前、シリアルナンバー)の総称です。
Net::HTTPクラスのインスタンスを生成
https = Net::HTTP.new(uri.host, uri.port)Net::HTTPクラスのnewメソッドを呼び出し、第一引数にuri.hostを、第二引数にuri.portを代入しています。ここで、新しいNet::HTTPクラスのインスタンスを生成しています。
SSL接続を可能にする
https.use_ssl = true新しく生成したNet::HTTPクラスのインスタンスに対して、use_sslをtrueにします。HTTPSを使う場合は、このコードが必要です。
GETリクエストを得る
http_get = Net::HTTP::Get.new(uri.path)Net::HTTP::GETクラスのnewメソッドを呼び出し、引数にuri.pathを渡しています。Net::HTTP::GETクラスはHTTPのGETリクエストを表すクラスです。
uri.pathでは、uriのpathを文字列で返しています。pathとは、URLのドメイン名の下にあるものです。Basic認証を突破したリクエスト送信
http_get.basic_auth(username, password)http_getはNet::HTTP::GETクラスのインスタンスですが、このクラスはNet::HTTPHeaderクラスを継承しているため、Net::HTTPHeaderクラスのbasic_authメソッドを呼び出すことができます。第一引数にusername、第二引数にpasswordを渡します。
basic_authメソッドは、ヘッダをBASIC認証用にセットするメソッドです。responseを受け取る
response = https.request(http_get)responseを受け取ります。正常に接続できている場合は、200が返ってきます。このコードでは、Net::HTTPクラスのインスタンスに対してrequestメソッドを呼び出しており、引数にhttp_getを渡しています。(getリクエスト)
アクセスしたページのHTMLを取得
response.bodyresponseのクラスはNet::HTTPOKクラスですが、Net::HTTPResponseから継承しているbodyメソッドを呼び出すことができます。このメソッドは、レスポンスのbody、つまりアクセスしたページのhtmlを文字列として返します。
以上になります。間違えている点、補足等ございましたら、是非コメントして頂けると幸いです。
また、参考になったよ!と思った方はいいねをお願いします!
- 投稿日:2020-02-08T21:05:43+09:00
【RSpec】spec/rails_helper.rbを和訳&補足してみた
はじめに
rails_helperの設定をこんな風にしていますという記事はあるのですが、そのオプションによって何をしているのか今一つわからなかったので、自分用に和訳&補足してみました。
忘れた頃の自分やRSpec初心者のためになればと思います。正確性に関しては自信ないです!RSpecのバージョンは
3.9
です。(2/9追記)編集リクエストを元に修正しました。
導入
$ rails g rspec:installコマンドを打つと、
.rspec
,spec/rails_helper.rb
,spec/spec_helper.rb
ファイルが生成されます。
rails_helper.rb
はデフォルトではこのようになっています。spec/rails_helper.rb# This file is copied to spec/ when you run 'rails generate rspec:install' require 'spec_helper' ENV['RAILS_ENV'] ||= 'test' require File.expand_path('../config/environment', __dir__) # Prevent database truncation if the environment is production abort("The Rails environment is running in production mode!") if Rails.env.production? require 'rspec/rails' # Add additional requires below this line. Rails is not loaded until this point! # Requires supporting ruby files with custom matchers and macros, etc, in # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are # run as spec files by default. This means that files in spec/support that end # in _spec.rb will both be required and run as specs, causing the specs to be # run twice. It is recommended that you do not name files matching this glob to # end with _spec.rb. You can configure this pattern with the --pattern # option on the command line or in ~/.rspec, .rspec or `.rspec-local`. # # The following line is provided for convenience purposes. It has the downside # of increasing the boot-up time by auto-requiring all files in the support # directory. Alternatively, in the individual `*_spec.rb` files, manually # require only the support files necessary. # # Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f } # Checks for pending migrations and applies them before tests are run. # If you are not using ActiveRecord, you can remove these lines. begin ActiveRecord::Migration.maintain_test_schema! rescue ActiveRecord::PendingMigrationError => e puts e.to_s.strip exit 1 end RSpec.configure do |config| # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures config.fixture_path = "#{::Rails.root}/spec/fixtures" # If you're not using ActiveRecord, or you'd prefer not to run each of your # examples within a transaction, remove the following line or assign false # instead of true. config.use_transactional_fixtures = true # RSpec Rails can automatically mix in different behaviours to your tests # based on their file location, for example enabling you to call `get` and # `post` in specs under `spec/controllers`. # # You can disable this behaviour by removing the line below, and instead # explicitly tag your specs with their type, e.g.: # # RSpec.describe UsersController, :type => :controller do # # ... # end # # The different available types are documented in the features, such as in # https://relishapp.com/rspec/rspec-rails/docs config.infer_spec_type_from_file_location! # Filter lines from Rails gems in backtraces. config.filter_rails_from_backtrace! # arbitrary gems may also be filtered via: # config.filter_gems_from_backtrace("gem name") endこのコメントアウトされた部分が今回のポイントです。
thisが何を示しているのかだとか、ちょっとした用語を噛み砕けるといいなと思います。自分なりに和訳&補足してみた
rails generate rspec:install
を実行すると、このファイルはspec/
ディレクトリにコピーされます。require 'spec_helper' ENV['RAILS_ENV'] ||= 'test' require File.expand_path('../config/environment', __dir__)本番環境のときにデータベースのTRUNCATE1を防ぎます。
abort("The Rails environment is running in production mode!") if Rails.env.production?require 'rspec/rails'追加で
require
する場合はこの下に追加してください。この時点までRailsは読み込まれていません!
spec/support/
配下のファイルを読み込む設定カスタムマッチャやマクロ2などを記述したrubyファイルを読み込みたい場合、
spec/support/
配下に置いてください。
spec/
配下にある_spec.rb
で終わるファイルはbundle exec rspec
コマンドを実行すると自動的に走ります。つまり、これをspec/support/
内に置くと、読み込み時とテスト時の二回実行されることになります。
そのため、spec/support/
に_spec.rb
で終わるファイルを置くのはやめましょう。この様式3はコマンドを打つときに
--pattern
を使うか、~/.rspec
,.rspec
,.rspec-local
で設定できます。以下の行は手間を省くために用意されています。この行を有効にすると
spec/support/
以下の全てのファイルが自動的に読み込まれるため、起動に時間がかかるという難点もあります。
代わりの方法として、それぞれの_spec.rb
ファイルでrequire
を使って必要なファイルだけを読み込む方法もあります。Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f }マイグレーションの設定
保留にされている4マイグレーションを確認し、テストを走らせる前にマイグレーションを適用します。
ActiveRecordを使わない場合、以下の行(
begin
からend
まで)を削除できます。begin ActiveRecord::Migration.maintain_test_schema! rescue ActiveRecord::PendingMigrationError => e puts e.to_s.strip exit 1 end(補足)
自動的にマイグレーションを行い、schemaとマイグレーションファイルに相違がある場合、例外を発生させます。ActiveRecordのfixtureを使用する設定
もしActiveRecordやActiveRecordのfixtureを使用しない場合はこの行は必要ありません。
config.fixture_path = "#{::Rails.root}/spec/fixtures"(補足)例えばFactoryBotを使用する場合が当てはまりますが、他の人の設定を見たところ、わざわざ削除している人は少ないようです。
exampleごとにトランザクションを行う設定
ActiveRecordを使わない、またはトランザクション内で複数のexampleを走らせたい場合は、この行を削除するかオプションを
true
からfalse
にしてください。config.use_transactional_fixtures = true(補足)
デフォルトのtrue
の場合、exampleごとにトランザクションが行われる設定になっています。つまり、exampleが始まるときにはきれいなデータベースが用意され、終わると全てのデータを削除します。
Database Cleanerなどを使って手動で削除する場合や、特定のSpecでだけトランザクションのロールバックを無効にする場合はfalse
にします。exampleについては以下の説明を参考にしてください。
it
はテストをexample
という単位にまとめる役割をします。
it do ... end
の中のエクスペクテーション(期待値と実際の値の比較)がすべてパスすれば、そのexample
はパスしたことになります。
使えるRSpec入門・その1「RSpecの基本的な構文や便利な機能を理解する」ファイルの場所に応じた機能を使用する設定
RSpec Railsでは、ファイルの場所に応じた動作を使用することができます。
例えば、spec/controllers
配下のファイルでは、get
やpost
を使うことができる仕様となっています。以下の行を削除するとこの仕様が無効になります。
代わりに、RSpec.describe UsersController, type: :controller do # ... endの
type: :controller
のように、明示的にタイプを記述することができます。利用可能なタイプは、以下のサイトに記述しています。
https://relishapp.com/rspec/rspec-rails/docsconfig.infer_spec_type_from_file_location!バックトレースをフィルタリングする設定
Railsで読み込まれたgemによるバックトレースをフィルタリングします。
config.filter_rails_from_backtrace!特定のgemをフィルタリングする場合、次の設定をしてください。
config.filter_gems_from_backtrace("gem name")(補足)
テスト失敗時のノイズを減らすための設定です。
テスト実行時に--backtrace
を付けると、フィルタリングされていないバックトレースが表示されます。(和訳はここまで)
ちなみに
FactoryBotやモジュールを使う場合は、以下のように読み込んでください。
RSpec.configure do |config| ... # config.filter_gems_from_backtrace("gem name") # この下に記述している人が多い印象です config.include FactoryBot::Syntax::Methods config.include LoginHelpers endおわりに
自分で翻訳したり調べたりすると、なんとなくRSpecの仕様への理解が深まった気がします。
また、公式ドキュメントはGoogle翻訳でも読みやすかったです。
間違っていた場合はご指摘をお願いします。
テーブルに格納されているデータを全削除すること ↩
ヘルパーメソッド ↩
テスト実行時にどのファイルを実行するか pattern - Configuration - RSpec Core ↩
db:migrate
されていない状態 ↩
- 投稿日:2020-02-08T19:25:02+09:00
今日はクライアントサイドでセッション管理してもいいのか!!(あまりよくない)
これはなに?
セッションをクライアントサイドだけで管理するのは難しいのでやめようね。という話をする機会が稀にあるのですがどういうときに困るんだっけ?またはどういう値はクライアントサイドに持っていいんだっけ?というのを毎回自分で考えるのが面倒なので書きました。
ここではクライアントサイドだけでセッション管理するのが難しい理由について説明します。そのあとに翻って一般的にセッションはどういう性質を持っているのか(あるいは持っているべきか)という話をします。クライアントサイドだけでやるセッション管理の例
Railsにはセッションストレージとして、CookieStoreというものがあります。説明に関しては
Railsセキュリティガイド: 2.3 セッションストレージ から引用しますがRailsのCookieStoreはクライアント側のcookieにセッションハッシュを保存します。サーバーはこのセッションハッシュをcookieから取得することで、セッションIDの必要性を解消します。こうすることで、アプリケーションのスピードは著しく向上しますが、このストレージオプションについては議論の余地があるため、セキュリティ上の意味やストレージでの制約について以下の点を十分考えておかなければなりません
例えば、ログインが必要な機能にアクセスするごとに「このユーザはログインしてるんだっけ?」というチェックのためにサーバ側のストレージ(DB/KVSなど)にアクセスが走ると、同時接続数が多いアプリケーションだとそれだけでDBのread負荷が上がる可能性があります。このような時に例えばCookieStoreを使うとリクエストに乗ってくるcookieを見るだけで済むのでサーバサイドの負荷を軽減することができる、という利点があります。
あとは、jwtの中にセッション情報を全部ぶち込んでいるケースとかも同様だと思ってもらって良さそうです。Cookie Storeを使うと難しいポイント
CookieStoreを使うとパフォーマンス向上してよかったね〜〜って話で終わるかというとそうではなくて、クライアントサイドでcookieを管理しているため前提としてcookieの有効状態を制御することは(少なくともサーバサイドにストレージがある時にくらべて)難しいです。
上で引用した箇所の下にも
- セッションcookieはひとりでに失効することはないため、悪用目的で使い回される可能性もあります。保存済みのタイムスタンプを利用して古いセッションcookieをアプリケーションで失効させるのもよい方法かもしれません。
という文章があります。
Cookie Storeに関して想定される攻撃例
具体的にどういう攻撃が考えられるか、またその対策については railsguides.jp のRailsセキュリティガイドの中から1つ、別のサイトから1つ紹介します。
再生攻撃
Railsセキュリティガイド: 2.5 CookieStoreセッションに対する再生攻撃 から引用します
再生攻撃のしくみは次のとおりです。
* ユーザーがクレジットを受け取る。総額はセッションに保存されているとする (これはあくまで説明のためのものであり、やってはいけません)。
* ユーザーがクレジットで何かを購入する。
* つかった分減ったクレジットがセッションに保存される。
* ここでユーザーの暗黒面が発動する。最初にブラウザに保存されていたcookieをコピーしてあったものを、現在のブラウザのcookieと差し替える。
* ユーザーのクレジット額が元に戻る。とあり、これについての対策は
結論から言うと、 この種のデータはセッションではなくデータベースに保存するのが最善です。この場合であれば、クレジットをデータベースに保存し、logged_in_user_idをセッションに保存します。
上の説明にも
(これはあくまで説明のためのものであり、やってはいけません)。
と書いてあるように、この例では自明ですが色々なタイミングで「ユーザは任意のタイミングまでセッションを巻き戻せる状態だけどこれセッションに持たせていいんだっけ?」みたいなことを考えていく必要があります。セッションハイジャックされた時に追い出せないよ問題
また、セッション管理がクライアントサイドで完結しているとログアウト/パスワード変更などに対してセッションを無効化する処理ができないという問題があります。
Rails SessionにCookieStore使った時の問題点 から引用しますが(これの元ネタの記事はどうも消されてしまっているようです)server-sideではstate管理しないので、当然remoteでセッションの無効化はできません。 つまりログアウトしてもsession cookieのtoken自体は無効化されません。
という話があり、特に困りそうなポイントとしてはその後に書いてある、
問題は、session cookieが無効化できないのに、それが永遠に有効であること。 FBとかY!Jとかでやってる「パスワード変更したら既存のsessionが無効になる」ってのはRailsは一切面倒見てくれないので、パスワード変えても漏洩したsession cookieは有効なままです。 なので、ひとたびsession cookieが漏れたら、完全にアウト。 なにやってもアカウント乗っ取られたまんま。永遠に。
こういうケースです。
で、これはどう対策すればいいの?という話ですが、この記事の中ではセッションにnonceをつければいいじゃないというアイデアが書いています。(これ自体はジャストアイデア的に書かれていて、その後にもっとシンプルで効果的な対策が書かれています)
OpenID Connectの名前にも触れていることからDBにnonceを保存して、セッション中にnonceがあったらDBと照合する、というような実装を想像しました。実はRailsセキュリティガイドの再生攻撃のあたりにもnonceを使えば防げるっちゃ防げるみたいな話は書いてます(この話は後ほど触れます)
そうすると、攻撃者が古いcookieを持ってきても「そのnonceはもうさっき見たので無理で〜す」みたいな感じで弾けるんですが、ここで みたいな顔になるポイントがあります。人類はどうしてcookie storeを使っていたのか
という話に立ち戻るとRailsセキュリティガイドの中に
サーバーはこのセッションハッシュをcookieから取得することで、セッションIDの必要性を解消します。こうすることで、アプリケーションのスピードは著しく向上しますが
ということでクライアントサイドにセッションを完結させるのはレスポンス速度向上のためなんですね(ここが違うとこの後の話もおかしなことになりますが...)
再生攻撃のあたりでnonceって単語が出てるところを見るとこの再生攻撃は、セッションにnonce (1回限りのランダムな値) を含めておくことで防ぐことができます。nonceが有効なのは1回限りであり、サーバーはnonceが有効かどうかを常に追跡し続ける必要があります。複数のアプリケーションサーバーで構成された合いの子アプリケーションの場合、状況はさらに複雑になります。nonceをデータベースに保存してしまうと、せっかくデータベースへのアクセスを避けるために設置したCookieStoreを使う意味がなくなってしまいます。
こういう話があり、個人的にはCookieStoreの利点が薄れる + 実装の複雑度が増すので、nonceを使うというジャッジをするケースは少ないかなと思います
では、パスワードリセットなどの後にセッションを無効化するには?
CookieStore単体では(そして、クライアントサイドに完結したセッションストレージでは)セッションハイジャック対策として行われるセッション無効化ができないということがわかりました。
では、どうすればいいのかというとクライアントサイドでセッションを管理することを諦めてしまうのが一番簡単ではないかと思います。
引用元のサイトでも、このように触れられています(パフォーマンスに関しては、どういうケースでどういうボトルネックがあって、セッションの一部だけをクライアントサイドに逃がすとどういう風に解消しそうか、という話はまた別途議論が待たれる)もしくはCookieStoreの代わりにMemcacheStore使うようにしてもいいです。 sessionをserver-sideで管理するようにさえすれば、この問題はそもそも発生しないですし。 パフォーマンスに影響しますけど。
セッションってどうあるべき?
ここまで、セッションをクライアントサイドだけで管理すると落とし穴あるよ!そもそもセッションの無効化ができないのである程度複雑な機能が入ってくるとクライアントサイドだけでの管理は無理だよ!って話をしましたが、じゃあお前はセッションをどうしたいの?というと
巻き戻ってもいい値を除いてユーザの状態はサーバサイドで管理すべき
だと思います。
ここでいう巻き戻ってもいい(または巻き戻っても特に変化しない)値の例としては
- ユーザID/名前
- ログイン日時
などが考えられます。このあたりをクライアントサイドに持たせてうまくストレージへのアクセス方法を減らすことはできるかもしれません
結論
ということでサーバサイドにセッションのストレージ(DBなりKVSなり)は用意したほうが良いと思います
参考
jwtでセッションを管理するのはやめようね、という話: Stop using JWT for sessions, part 2: Why your solution doesn't work - joepie91's Ramblings
- 投稿日:2020-02-08T19:23:12+09:00
Gemfile version 指定
- 投稿日:2020-02-08T19:23:03+09:00
コード書いたことないPdMやPOに捧ぐ、Rails on Dockerハンズオン vol.4 - Static pages -
この記事はなにか?
この記事は私が社内のプログラミング未経験者、ビギナー向けに開催しているRuby on Rails on Dockerハンズオンの内容をまとめたものです。ていうかこの記事を基にそのままハンズオンします。ハンズオンは
1回の内容は喋りながらやると大体40~50分くらいになっています。お昼休みに有志でやっているからです。
現在進行形なので週1ペースで記事投稿していけるように頑張ります。
ビギナーの方のお役にたったり、同じように有志のハンズオンをしようとしている人の参考になれば幸いです。
他のハンズオンへのリンク
・ Vol.1 - Introduction -
・ Vol.2 - Hello, Rails on Docker -
・ Vol.3 - Scaffold, RESTful, MVC -
・ Vol.4 - Static pages -
$
,#
,>
について
$
: ローカルでコマンドを実行するときは、頭に$
をつけています。
#
: コンテナの中でコマンドを実行するときは、頭に#
をつけています。
>
: Rails console内でコマンド(Rubyプログラム)を実行するときは、頭に>
をつけています。
はじめに
第4回目は、Static pages(静的なページ)を作ることにチャレンジしてみましょう!
そもそも静的なページとは、アクセスするユーザーや時間帯に関係なく同じコンテンツが表示されるページのことです。対義語は動的なページは例えばマイページのようなユーザーごとにコンテンツが変わってくるようなページのことですね。
静的なページのみで構築されているサイトを『Webサイト』、動的なページも存在するサイトを『Webアプリケーション』と呼び分けたりします。今回は、静的なトップページを作っていきますよ!
Railsの初期設定
とその前に。
今回は日本で使われ日本で運用するアプリを想定するのですが、Railsはデフォルトでは英語が使われていたりUTC(協定世界時)が使われていたりと、そのまま日本でサービス展開しようとすると面倒な部分があります。
最初にこの設定をローカライズ(日本化)しておきましょう!Timezoneの設定
Dockerfile
やdocker-compose.yml
でコンテナのタイムゾーンは日本に設定していましたね(TZ=Asia/Tokyo
)。Railsアプリも同じように設定してあげます。config/application.rb... class Application < Rails::Application ... config.time_zone = 'Tokyo' config.active_record.default_timezone = :local ... end
time_zone
はRailsアプリが時間を扱うときにどのtimezoneで動くかを指定する設定でデフォルトだとUTCになってます。例えばTime.current
で現在の時間を表示できたりするのですが、この結果をどのtimezoneで表示するかがこの設定で決まります。この後、Modelを保存したりするときなどもその作成日時などがこのtimezoneで設定されます。
active_record.default_timezone
はDBに書き込まれている時間をどのtimezoneとして扱うかの設定です。言語の設定
Railsではエラーメッセージなどが準備されているのですが、デフォルトでは英語で定義されています。日本でサービス展開する場合「?」となってしまうので、日本語化します。
config/application.rb... class Application < Rails::Application ... config.i18n.default_locale = :ja ... endこの設定で、
config/locales/
にあるyamlファイルの中からja:
で定義されている文字列が表示されるようになるんです。日本語版のファイルを作ってくださっている方がいらっしゃいますのでベースとして利用させていただきましょう。rails-i18n/ja.yml at master · svenfuchs/rails-i18n
こちらのファイルを
config/locales/ja.yml
として保存することで日本語化完了です!ちなみに『i18n』は『internationalization』のことで『国際化』と訳されます。アクセスする国によって時間や言語を変えたりすることです(今回は日本オンリーに対応ですが...笑い)。頭の『i』とお尻の『n』の間に18文字あるので『i18n』と表現します。技術系だとこういうの最近多いっすよね?
Bootstrap
まだまだ静的なページの作成には入りませんよ!次は、Bootstrapを使えるようにしていきましょう。
BootstrapはTwitter社が開発したCSSフレームワークです。
レスポンシブデザインに標準で対応されており、CSSだけでなくJavascript(jQuery)も含まれています。
多くの人に使われているためインターネット上に情報があふれていますので初心者にも安心です。一方で、多くの人に使われているため似通ったデザインになってしまうので少し慣れてくると敬遠されがちな印象です。Boostrapをyarnでインストール
Rails5まではgemでインストールするのが主流だったと思うのですが、Rails6ではWebpackerが必須になったこともありパッケージマネージャー経由でインストールするのが主流になっていくと思われますのでその方法で。
まだコンテナを立ち上げていなかったですね。それではコンテナを立ち上げてコンテナの中でコマンドを実行していきましょう!
$ docker-compose up -d $ docker-compose exec web ash# yarn add bootstrap jquery popper.jsこれでBootstrap関連のライブラリをインストールできたのでRailsアプリで使えるように設定していきます。
まずapplication.jsでBootstrapを読み込みます。
app/javascript/packs/application.js... require("bootstrap") ...次にBootstrapをSCSSで読み込むために
application.css
をapplication.scss
にリネームします。# mv app/assets/stylesheets/application.css app/assets/stylesheets/application.scssファイルの中身はCSSの記法で書かれているものなので一度全て削除して以下のように書き換えましょう。
app/assets/stylesheets/application.scss@import 'bootstrap/scss/bootstrap';最後にBootstrapと依存関係にある
jQuery
とpopper.js
の設定を追加してあげます。config/webpack/environment.jsconst { environment } = require('@rails/webpack') const webpack = require('webpack') environment.plugins.append('Provide', new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery', Popper: ['popper.js', 'default'] })) module.exports = environmentやばいですね。たったこれだけでBootstrapの準備が整いました。Bootstrapで用意されているあらゆるスタイルシートやjavascriptが利用可能になっちゃいました。
BootstrapがどんなことができるかはBootstrap公式のDocumentationを見るのが一番いいと思っているのですが、これだけの表現が上の設定でできるようになってしまったのです。Topページを作成する
いよいよ静的なページを作っていきます。
静的なページではModelのような動的なものはいらないのでMVCのVCがあればOKですね。
VC(ルーティングも!)はrails generate controller
コマンドで作成することができます。今回はstatic_pages
controllerでhome
actionを作ってみましょう。# rails generate controller static_pages home
rails generate controller NAME [action action]
の形式でコマンドを実行できます。actionは複数指定可能です。
慣習的にNAME
は複数形を用います。このコマンドだけですでに
http://localhost:3000/static_pages/home
へのRouting, Controller#Action, Viewが出来上がっているのでアクセスできるようになっていますね。
このコマンドで作成・更新されたファイルの中身をちょっとみていきましょう!
Routing
config/routes.rbRails.application.routes.draw do get 'static_pages/home' end
get 'static_pages/home'
の行が追加されてます。前回はresouces
メソッドを使ったルーティングの指定の仕方でしたが、このように一つ一つのルートを定義することもできるのです。
これだけで「static_pages/home
のパスにget
メソッドできたリクエストをstatic_pages
controllerのhome
actionにルーティングする」ことを定義しています。Controller
app/controllers/static_pages_controller.rbclass StaticPagesController < ApplicationController def home end end
home
アクションが定義されていますね。特段処理はないので、app/views/static_pages/home.html.erb
をレンダリングするのみです。View
app/views/static_pages/home.html.erb<h1>StaticPages#home</h1> <p>Find me in app/views/static_pages/home.html.erb</p>
app/views/static_pages/home.html.erb
ファイルが生成されています。
中身も先ほどhttp://localhost:3000/static_pages/home
と同じものが表示されていることがわかりますね。ルートパスをTopページにする
現状ルートパス(
http://localhost:3000
)にアクセスするとHello worldのページが表示されています。本当はこのURLにアクセスしたときにTopページを表示させたいのでルーティングの設定を更新していきます。config/routes.rbRails.application.routes.draw do root 'static_pages#home' endルートパスの設定の仕方は`root '[controller_name]#[action_name]'とするだけです!
試しに
http://localhost:3000/
にアクセスしてみてください。Topページが表示されるようになりましたね。
またhttp://localhost:3000/static_pages/home
にもう一度アクセスしてみましょう。get 'static_pages/home'
を削除してるので以下のようにそんなルートないよとエラーになります。
Bootstrapでページを装飾する
にしても味気ないページですよね。ということでBootstrapのCSSを使いながらいい感じのページに装飾していきましょう!
CSSはHTMLのスタイル(色とか大きさとか)をまとめた変数みたいなものです。HTMLタグ(<h1>とか<p>とか)にclass属性をつけることでそのスタイルが適用されます。SCSSはCSSを書きやすくしたものです(なので本質はCSS)。
<h1 class="hoge">Hello.</h1>.hoge { font-size: 32px; color: red; }これで32pxの赤文字で"Hello."が表示されるようになるってイメージですね。
Topページのコンテンツを装飾する
ではまず、
app/views/static_pages/home.html.erb
を更新していい感じのTopページを作っていきましょう。app/views/static_pages/home.html.erb<div class="jumbotron mb-0"> <div class="container text-center"> <h1>Welcome to Sample App.</h1> <h2>Twitterみたいなアプリです。</h2> <%= link_to "Sign up now!", "#", class: "btn btn-lg btn-primary mt-5" %> </div> </div>ここに出てくる
jumbotron
,container
,text-center
,btn
,btn-lg
,btn-primary
,mt-5
はすべてBootstrapのclassです。Bootstrapのclassの使い方はしっかりと公式のDocumantationがあるのでそちらをみてみましょう。実際に自分で何かを作るとなるとこういう公式ドキュメントと向き合うことが一番の近道だったりするのでここでは説明しないっす。1つだけ、ERBコードがありますね。
app/views/static_pages/home.html.erb<%= link_to "Sing up now!", "#", class: "btn btn-lg btn-primary mt-5" %>
link_to
はリンクを作るためのメソッドです。HTML的にいえば<a>
タグを作ってくれるということです。
link_to [表示文字], [リンク先], [options]
の構文になってまして、今回の例だと以下のような<a>
タグが出来上がります。<a href="#" class="btn btn-lg btn-primary mt-5">Sign up now!</a>リンク文字列やリンク先などに変数を指定でき(例えばルートパスはroot_pathが変数として割り当てられている)るのでERBファイルでリンクを作成する場合多用するコードなので覚えておきましょう。
リンク先として#を指定していますが、まだ遷移先のページを作成していないので仮置きしているだけです(#を指定しておくとリンクを押しても今いるページから遷移しないです。)編集ができたらトップページをリロードしてみましょう。以下のページになっていれば成功です!
ふへー。ぽくなってきましたね。
ただHeaderとかFooterとかも欲しくなってきました。
ヘッダーを装飾する
突然ですが、ブラウザでトップページのソースコードをみてみましょう。(右クリックで「ページのソースを表示する」を選択するとみれます。)
app/views/static_pages/home.html.erb
に全く書いた記憶がないもの、例えば<head>
タグとかがありますよね。Topページ<!DOCTYPE html> <html> <head> <title>App</title> <meta name="csrf-param" content="authenticity_token" /> <meta name="csrf-token" content="3702TRDf2wiVe1ErXV1NWoksR1sWw1+LO31miZuonos9K8zOmMwJ+VotF3ZXCPRP+g9IiKoZovoA8HpIjLjqmA==" /> <link rel="stylesheet" media="all" href="/assets/application.debug-9519f0a34796e622c941135a612890361ca8a44f05d715927aaa1575ce29c235.css" data-turbolinks-track="reload" /> <script src="/packs/js/application-923746675d9ea6d857bd.js" data-turbolinks-track="reload"></script> </head> <body> <div class="jumbotron"> <div class="container text-center"> <h1>Welcome to Sample App.</h1> <h2>Twitterみたいなアプリです。</h2> <a class="btn btn-lg btn-primary mt-5" href="#">Sign up now!</a> </div> </div> </body> </html>これはなんでしょう?
実はRailsアプリケーションではViewファイルの中でもLayoutと呼ばれるやつがいます。こいつはなにかというと、ページで共通な部分を表現してくれるものです。デフォルトで
app/views/layouts/application.html.erb
のレイアウトファイルが利用されています。ちょっと中を覗いてみましょう。app/views/layouts/application.html.erb<!DOCTYPE html> <html> <head> <title>App</title> <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %> </head> <body> <%= yield %> </body> </html>ERBコードが入っているので少し表現は違いますが、Topページのソースコードと似た構文をしていることがわかります。実は最終的に表示されているHTMLは
app/views/static_pages/home.html.erb
がこのapp/views/layouts/application.html.erb
の<%= yield %>
に代入されたものです。少しだけ
app/views/layouts/application.html.erb
の中身をご紹介。
まず全体の構文ですが、HTMLの基本の構文が記述されていますね。<!DOCTYPE html> <html> <head> ... </head> <body> ... </body> </html>
<body>
の中のyeild
については上で話した通りで、actionのviewファイルの中身が代入されるようになっています。
<head>
の中はどうでしょうか?title
title
はブラウザのタブのところに表示される文字列です。今だとApp
と指定されているのでそれが表示されていますね。scrf_meta_tags
CSRF(Cross-Site Request Forgery)対策のための記述です。今回のTopページではあまり活躍しませんが、POSTリクエストを飛ばすページで活躍します。簡単にいえば、正しいユーザーからのリクエストなのかというリクエストの真正性(Authenticity)を検証するために必要なことを開発者が意識せずともRailsがカバーしてくれるようになるとのこと。
csp_meta_tag
CSP(Content Security Policy)に必要なタグを生成してくれるコードです。CSPはXSS(Cross Site Scripting)を防ぐためのもので、コンテンツの提供元や取得方法を制限する手法です。別途、制限を設定することで動作するようになります。ここでは紹介程度。
stylesheet_link_tag
stylesheetの読み込み。
app/assets/stylesheets/application.css[scss]
を読み込んでくれている。javascript_pack_tag
javascriptの読み込み。
app/javascript/packs/application.js
を読み込んでくれている。ってな具合です。
さて、長々と話してしまいましたが、本当に言いたかったことは『ヘッダーやフッターは各ページ個別のものではなく、サイトで共通のものであるはずなので、レイアウトファイルに記述するんだよ』ということです。
では、ヘッダーを
app/views/layouts/application.html.erb
に記述します!app/views/layouts/application.html.erb... <body> <header class="navbar navbar-dark navbar-expand bg-dark"> <div class="container"> <%= link_to "sample app", root_path, class: "navbar-brand" %> <ul class="navbar-nav"> <li class="nav-item"><%= link_to "Home", root_path, class: "nav-link" %></li> <li class="nav-item"><%= link_to "Sign in", "#", class: "nav-link" %></li> </ul> </div> </header> <%= yield %> </body> ...このあたりもBootstrapの公式ドキュメントを参考に記述してみました => Navbar · Bootstrap
Topページにアクセスしてみましょう。以下のようになっていれば成功です!
おー、どんどんぽくなってきましたね!フッターを装飾する
最後にフッターも付けちゃいましょう!今回はコピーライトを書いているくらいのフッターをば。
フッターは
<footer>
タグを使います。app/views/layouts/application.html.erb... <body> ... <%= yield %> <footer class="bg-dark"> <p class="text-center text-white py-2 mb-0">(c) Hoge Inc. All Rights Reserved.</p> </footer> </body> ...特別難しいところはありませんね。再度Topページにアクセスします。
おー、ぽい。ぽいのですが、ブラウザの下の方に余白ができているのが気になりますね...
表示するコンテンツがブラウザの縦サイズに合わない場合は、フッターは画面の最下部に表示されるようにしたい...ということで少しCSSをいじってみましょう。
app/assets/stylesheets/application.scss@import 'bootstrap/scss/bootstrap'; body { display: flex; flex-direction: column; min-height: 100vh; } footer { margin-top: auto; }本当はfooter用のファイルを作って
appliction.scss
で@import
する方が整理されるのでいいのですが、今回は分量も少ないのでひとまずapplication.scss
に記述します。これで何をしているかというと、まず
body
タグに対してdisplay: flex;
をあててます。これはFlexboxという要素をきれいに横並びや縦並びでレイアウトしてくれるレイアウトモジュールです。
flex-direction: column;
と定義しているので縦方向に要素を並べてくれます。
そして、min-height: 100vh;
を定義しているのでbody
タグで囲まれた要素は最低ブラウザいっぱいの高さを持つ要素に指定されたことになります。今、
body
タグの中にはheader
,<%= yeild %>の中のdiv
,footer
の3つの要素が同じレベルに存在しています。これを縦方向に並べていることになるんですね。
この時、Flexboxの親要素(今回だとbody
)をコンテナ、子要素(今回だとheader
,div
,footer
)をアイテムと呼びます。
footer
にはmargin-top: auto;
が定義されています。これは親要素に対して、この要素の上部に最大限のmargin(余白)を付与することを表していて、簡単に言うと親要素の一番下に配置するってことになります。
今、親要素のbody
は縦に最小で100vhの高さを持ちます。コンテンツが足りない場合はbody
は100vhの高さになるのでfooter
は100vhの一番下に位置します。コンテンツが100vh以上の場合はbody
もheader
,div
,footer
の3要素の高さの合計が高さになるので、footer
は自然な形でdiv
のすぐ下に配置されることになります。さてさて、ではTopページをリロードしてみてください!
とてもいい感じになりましたね!スマートフォンに対応する
PCだといい感じに表示されるのですが、スマートフォンだと実はとても見にくい状態になっています。ChromeのDeveloper toolsなどで確かめてみてください(Chrome DevTools での Device Mode によるモバイル端末のシミュレート)
BootstrapはもともとResponsive Design(ブラウザの横幅に合わせてCSSが変わる)に対応しているので、Viewport設定の1行を
head
タグ内に挿入するだけでいい感じのデザインになります。app/views/layouts/application.html.erb... <head> ... <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> ... </head> ...呪文みたいな感じで書いちゃうことが多いのですが、意味は「もう逃げない。HTMLのviewportをちゃんと理解する - Qiita」の記事が参考になりました。
スマホモードでリロードしてみましょう。
文字の大きさもいい感じになりましたね。Google Fontsでフォントを変える
んー。デザインはよくなってきたけどフォントがデフォルトっぽくて気になるなぁ...
フォントはこのアプリを使うユーザーの端末にフォントがインストールされていないと使えなかったりするのであんまり冒険できないところだったりします。アプリケーションの中でフォントを配布してもいいのですが、まぁ若干面倒ですね。
そこで便利なのがGoogle Fontsです。『Webフォント』と呼ばれますが、インターネット上ですでに公開されているため端末のインストールなしで利用でき、どの端末からでも同じフォントで表現ができるようになります。
app/assets/stylesheets/application.scss
でGoogle Fontsの中からNoto Sans JP
をインポートしてbody
のfont-family
に指定するだけなんですね簡単です。app/assets/stylesheets/application.scss@import 'bootstrap/scss/bootstrap'; // Google Fontsから"Noto Sans JP"をインポート @import url('https://fonts.googleapis.com/css?family=Noto+Sans+JP&display=swap'); body { // "Noto Sans JP"を全体のデフォルトフォントに指定 font-family: 'Noto Sans JP', sans-serif!important; display: flex; flex-direction: column; min-height: 100vh; } footer { margin-top: auto; }少し特別な書き方として、
font-family
のところで!important
と末尾に記述してます。
!important
はCSSの適用優先度を決めるための記述で、最優先で反映させるものに使います。つまり、他にfont-family
を記述したとしてもこいつが適用されるってことです。
Bootstrapでもfont-family
を定義してくれていたりするので、こっちを最優先にするために記述してます。Google Fontsのサイトでお好きなフォントを探してみて適用してみてください!日本語フォントの場合は、『Language』で『Japanese』を選択すれば日本語フォントありに絞られます。『+』で使用フォントを追加するとインポートの仕方とかも教えてくれるので気軽に使えます。もっと詳しくはこちらの記事から↓
【2019年版】Google Fontsの使い方:初心者向けに解説!まとめ
今回は、Railsの初期設定とBootstrapのインストール、Topページのデザインをやってみました。
静的なページではありますが、コーディングした内容が反映されてどんどん変わっていくのを実感する楽しみを感じてもらえたんじゃないかと思います。次回は今回触れていなかったModelが主役です。Modelの作成からModelに用意されているメソッドを使ってデータの作成や更新を遊んでみようと思います。
では、次回も乞うご期待!ここまでお読みいただきありがとうございました!
Reference
- Ruby on Rails チュートリアル:実例を使って Rails を学ぼう
- Railsタイムゾーンまとめ - Qiita
- rails-i18n/ja.yml at master · svenfuchs/rails-i18n
- もう迷わない!CSS Flexboxの使い方を徹底解説 | Web Design Trends
- 【2019年版】Google Fontsの使い方:初心者向けに解説!
P.S. 間違っているところ、抜けているところ、説明の仕方を変えるとよりわかりやすくなるところなどありましたら、優しくアドバイスいただけると助かります。
- 投稿日:2020-02-08T17:21:56+09:00
[Rails]Ajaxを用いた非同期投稿機能といいね機能の実装
実装すること
Ajaxを用いて非同期通信を行い、下記のコードを書いていきます。
①新規の投稿が新着投稿一覧の一番前に画面のリロードなしで表示される
②いいねのハートの色といいね数を画面のリロードなしで変更するER図
User:Item = 1:N
Item:Like = 1:N
User:Like = 1:N
LikeテーブルがItemとUserの中間テーブルになります。
ページ設計
・[items/index]投稿一覧でいいねができる
・[items/show]投稿詳細でいいねができるモデルの作成
今回はUserモデル、Itemモデル、Likeモデルを作成します。
Userモデルはdeviseを使用してモデルを作成していきます。deviseとjQueryの導入・Userモデルの作成
Gemfile.gem 'devise' gem 'jquery-rails'$ bundle install $ rails g devise:install $ rails g devise user $ rails g devise:viewsItemモデル・Likeモデルの作成
$ rails g model item title:string image_id:string user_id:integer $ rails g model like item_id:integer user_id:integerアソシエーションの確認
Userモデル
userはそれぞれたくさんのitemを投稿をすることができ、投稿にいいねができるというイメージです。
dependent: :destroy
は、itemがuserに依存していることから、userが消えればitemも消えるようにするためです。app/models/user.rbclass User < ApplicationRecord devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable, :omniauthable has_many :items, dependent: :destroy has_many :likes, dependent: :destroy #既にいいねしているかどうか def already_liked?(item) self.likes.exists?(item_id: item.id) end endItemモデル
app/models/item.rbclass Item < ApplicationRecord belongs_to :user has_many :likes, dependent: :destroy endLikeモデル
validates_uniqueness_of
によって、item_idとuser_idの組が1組しかないようにバリデーションをかけます。app/models/like.rbclass Like < ApplicationRecord belongs_to :item belongs_to :user validates_uniqueness_of :item_id, scope: :user_id endコントローラーの作成
$ rails g controller users $ rails g controller items index show $ rails g controller likesルーティングの作成
投稿一覧、投稿詳細、投稿作成、投稿へのいいね、いいねの取り消しができるようにしていきます。
config/routes.rbRails.application.routes.draw do devise_for :users root 'items#index' resources :users resources :items, only: [:index, :show, :create] do resource :likes, only: [:create, :destroy] end endコントローラーとビューの編集(新規投稿・投稿一覧・投稿詳細)
[新規投稿]非同期で投稿一覧の新着順の一番最新に表示されるようにする
[投稿一覧]新着順で8投稿だけ表示されるようにするitems_controller.rb
app/controllers/items_controller.rbclass ItemsController < ApplicationController def new @item = Item.new end def create @item = Item.new(item_params) if @item.save @items = Item.order(created_at: :desc).limit(8) render :create else render :new end end end def index #新着順 @items = Item.order(created_at: :desc).limit(8) end def show @item = Item.find(params[:id]) end private def item_params params.require(:item).permit(:title, :user_id) end enditems/index.html.erb
投稿部分はパーシャルにします。
app/views/items/index.html<div id="item_index_new"> <%= render 'item_new', items: @items %> </div>items/_item_new.html.erb
いいね部分はパーシャルにします。
id="index_like_<%= item.id %>"
この部分で投稿それぞれに一位な値を付与しています。app/views/items/_item_new.html<h2>新着投稿</h2> <% items.each do |item| %> <%= link_to item_path(item) do %> <%= attachment_image_tag item, :image, size: "190x190", class: "coffee_image_media" %> <% end %> <%= link_to user_path(item.user_id) do %> <%= attachment_image_tag item.user, :profile_image, fallback: "no_image.jpg", class:"profile-img-circle", size: "30x30" %> <%= item.user.name %> <% end %> <div id="index_like_<%= item.id %>"> <%= render 'likes/like', item: item %> </div> <% end %>items/show.html.erb
いいね部分はパーシャルにします。
id="show_like_<%= item.id %>"
この部分で投稿それぞれに一位な値を付与しています。app/views/items/show.html<h1>投稿詳細ページ</h1> <%= attachment_image_tag @item.user, :profile_image, fallback: "no_image.jpg", class:"profile-img-circle", size: "100x100" %> <%= @item.user.name %> <div id="show_like_<%= @item.id %>"> <%= render 'likes/like', item: @item %> </div>items/new.html.erb
hidden_field
でuser_idにcurrent_user.idを代入しています。app/views/items/new.html<%= form_for(@item, url: items_path, remote: true) do |f| %> <p>画像投稿</p> <%= f.attachment_field :image %> <p>タイトル</p> <%= f.text_field :name, class: "form-control" % <%= f.hidden_field :user_id, :value => current_user.id %> <%= f.submit "保存", class:"form-control" %> <% end %>items/create.js.erb
id=item_index_new
の一番前に追加します(後ろに追加するときはappend/全体の差し替えはhtml)
items_controller.rbのcreateアクションが呼ばれた時は、views/items/create.js.erbが呼ばれます。他のコントローラの時も同様で、likes_controller.rbのindexアクションが呼ばれた時は、views/items/index.js.erbが呼ばれます。
※ファイルがあれば呼び出されます。app/views/items/create.js$("#item_index_new").html("<%= escape_javascript(render 'users/items/item_new', items: @items) %>")コントローラとビューの編集(いいね)
likes_controller.rb
app/controllers/likes_controller.rbclass LikesController < ApplicationController before_action :item_params def create like = current_user.likes.new(item_id: @item.id) like.save end def destroy @like = Like.find_by(user_id: current_user.id, item_id: @item.id).destroy end private def item_params @item = Item.find(params[:item_id]) end endlikes/_like.html.erb
・今回のいいね処理ではすでにあるパーシャルを切り替えるだけなので、何も返さないが、表示は切り替えます。切り替えるのにjsを使います。
・if user_signed_in?
で、もしユーザーがログインしていなければ、ハートが表示はされるがいいねはできないようにしています。
・already_liked?
で、Userモデルのメソッドを呼び出し、既にいいねしているかどうかを確認しています。
・remote: true
で、リンクを非同期化しています。
・item.likes.count
で、いいね数を取っています。
・fa fa-heart
は、いいねの表示にfontawesomeを使用しています。app/views/likes/_like.html<% if user_signed_in? %> <% if current_user.already_liked?(item) %> <%= link_to item_likes_path(item), method: :delete, remote: true do %> <i class="fa fa-heart" aria-hidden="true" style="color: red;"> <%= item.likes.count %> </i> <% end %> <% else %> <%= link_to users_item_likes_path(item), method: :post, remote: true do %> <i class="fa fa-heart" aria-hidden="true" style="color: #C0C0C0;"> <%= item.likes.count %> </i> <% end %> <% end %> <% else %> <i class="fa fa-heart" aria-hidden="true"> <%= item.likes.count %> </i> <% end %>likes/create.js.erb
いいねをする
app/views/likes/create.js//投稿一覧いいね差し替え $("#index_like_<%= @item.id %>").html("<%= j(render partial: 'likes/like', locals: { item: @item}) %>"); //投稿詳細いいね差し替え $("#show_like_<%= @item.id %>").html("<%= j(render partial: 'likes/like', locals: { item: @item}) %>");likes/destroy.js.erb
いいねを取り消す
app/views/likes/destroy.js//投稿一覧いいね差し替え $("#index_like_<%= @item.id %>").html("<%= j(render partial: 'likes/like', locals: { item: @item}) %>"); //投稿詳細いいね差し替え $("#show/like_<%= @item.id %>").html("<%= j(render partial: 'likes/like', locals: { item: @item}) %>");最後に
最後までご覧いただきありがとうございます。
初学者ですので間違っていたり、分かりづらい部分もあるかと思います。
何かお気付きの点がございましたら、お気軽にコメントいただけると幸いです。
- 投稿日:2020-02-08T17:21:56+09:00
[Rails]Ajaxを用いて非同期で投稿機能といいね機能の実装
実装すること
Ajaxを用いて非同期通信を行い、下記のコードを書いていきます。
①新規の投稿が新着投稿一覧の一番前に画面のリロードなしで表示される
②いいねのハートの色といいね数を画面のリロードなしで変更するER図
User:Item = 1:N
Item:Like = 1:N
User:Like = 1:N
LikeテーブルがItemとUserの中間テーブルになります。
ページ設計
・[items/index]投稿一覧でいいねができる
・[items/show]投稿詳細でいいねができるモデルの作成
今回はUserモデル、Itemモデル、Likeモデルを作成します。
Userモデルはdeviseを使用してモデルを作成していきます。devise・jQuery・refile,refile-mini_magickの導入・Userモデルの作成
Gemfile.gem 'devise' gem 'jquery-rails' gem "refile", require: "refile/rails", github: 'manfe/refile' gem "refile-mini_magick"$ bundle install $ rails g devise:install $ rails g devise user $ rails g devise:viewsItemモデル・Likeモデルの作成
$ rails g model item title:string image_id:string user_id:integer $ rails g model like item_id:integer user_id:integerアソシエーションの確認
Userモデル
userはそれぞれたくさんのitemを投稿をすることができ、投稿にいいねができるというイメージです。
dependent: :destroy
は、itemがuserに依存していることから、userが消えればitemも消えるようにするためです。app/models/user.rbclass User < ApplicationRecord devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable, :omniauthable has_many :items, dependent: :destroy has_many :likes, dependent: :destroy #既にいいねしているかどうか def already_liked?(item) self.likes.exists?(item_id: item.id) end endItemモデル
app/models/item.rbclass Item < ApplicationRecord belongs_to :user has_many :likes, dependent: :destroy #refile attachment :image endLikeモデル
validates_uniqueness_of
によって、item_idとuser_idの組が1組しかないようにバリデーションをかけます。app/models/like.rbclass Like < ApplicationRecord belongs_to :item belongs_to :user validates_uniqueness_of :item_id, scope: :user_id endコントローラーの作成
$ rails g controller users $ rails g controller items index show $ rails g controller likesルーティングの作成
投稿一覧、投稿詳細、投稿作成、投稿へのいいね、いいねの取り消しができるようにしていきます。
config/routes.rbRails.application.routes.draw do devise_for :users root 'items#index' resources :users resources :items, only: [:index, :show, :create] do resource :likes, only: [:create, :destroy] end endコントローラーとビューの編集(新規投稿・投稿一覧・投稿詳細)
[新規投稿]非同期で投稿一覧の新着順の一番最新に表示されるようにする
[投稿一覧]新着順で8投稿だけ表示されるようにするitems_controller.rb
app/controllers/items_controller.rbclass ItemsController < ApplicationController def new @item = Item.new end def create @item = Item.new(item_params) if @item.save @items = Item.order(created_at: :desc).limit(8) render :create else render :new end end end def index #新着順 @items = Item.order(created_at: :desc).limit(8) end def show @item = Item.find(params[:id]) end private def item_params params.require(:item).permit(:title, :user_id) end enditems/index.html.erb
投稿部分はパーシャルにします。
app/views/items/index.html<div id="item_index_new"> <%= render 'item_new', items: @items %> </div>items/_item_new.html.erb
いいね部分はパーシャルにします。
id="index_like_<%= item.id %>"
この部分で投稿それぞれに一位な値を付与しています。app/views/items/_item_new.html<h2>新着投稿</h2> <% items.each do |item| %> <%= link_to item_path(item) do %> <%= attachment_image_tag item, :image, size: "190x190", class: "coffee_image_media" %> <% end %> <%= link_to user_path(item.user_id) do %> <%= attachment_image_tag item.user, :profile_image, fallback: "no_image.jpg", class:"profile-img-circle", size: "30x30" %> <%= item.user.name %> <% end %> <div id="index_like_<%= item.id %>"> <%= render 'likes/like', item: item %> </div> <% end %>items/show.html.erb
いいね部分はパーシャルにします。
id="show_like_<%= item.id %>"
この部分で投稿それぞれに一位な値を付与しています。app/views/items/show.html<h1>投稿詳細ページ</h1> <%= attachment_image_tag @item.user, :profile_image, fallback: "no_image.jpg", class:"profile-img-circle", size: "100x100" %> <%= @item.user.name %> <div id="show_like_<%= @item.id %>"> <%= render 'likes/like', item: @item %> </div>items/new.html.erb
hidden_field
でuser_idにcurrent_user.idを代入しています。app/views/items/new.html<%= form_for(@item, url: items_path, remote: true) do |f| %> <p>画像投稿</p> <%= f.attachment_field :image %> <p>タイトル</p> <%= f.text_field :name, class: "form-control" % <%= f.hidden_field :user_id, :value => current_user.id %> <%= f.submit "保存", class:"form-control" %> <% end %>items/create.js.erb
id=item_index_new
の一番前に追加されるように、内容を差し替えます。
items_controller.rbのcreateアクションが呼ばれた時は、views/items/create.js.erbが呼ばれます。他のコントローラの時も同様で、likes_controller.rbのindexアクションが呼ばれた時は、views/items/index.js.erbが呼ばれます。
※ファイルがあれば呼び出されます。app/views/items/create.js$("#item_index_new").html("<%= escape_javascript(render 'items/item_new', items: @items) %>")コントローラとビューの編集(いいね)
likes_controller.rb
app/controllers/likes_controller.rbclass LikesController < ApplicationController before_action :item_params def create like = current_user.likes.new(item_id: @item.id) like.save end def destroy @like = Like.find_by(user_id: current_user.id, item_id: @item.id).destroy end private def item_params @item = Item.find(params[:item_id]) end endlikes/_like.html.erb
・今回のいいね処理ではすでにあるパーシャルを切り替えるだけなので、何も返さないが、表示は切り替えます。切り替えるのにjsを使います。
・if user_signed_in?
で、もしユーザーがログインしていなければ、ハートが表示はされるがいいねはできないようにしています。
・already_liked?
で、Userモデルのメソッドを呼び出し、既にいいねしているかどうかを確認しています。
・remote: true
で、リンクを非同期化しています。
・item.likes.count
で、いいね数を取っています。
・fa fa-heart
は、いいねの表示にfontawesomeを使用しています。app/views/likes/_like.html<% if user_signed_in? %> <% if current_user.already_liked?(item) %> <%= link_to item_likes_path(item), method: :delete, remote: true do %> <i class="fa fa-heart" aria-hidden="true" style="color: red;"> <%= item.likes.count %> </i> <% end %> <% else %> <%= link_to users_item_likes_path(item), method: :post, remote: true do %> <i class="fa fa-heart" aria-hidden="true" style="color: #C0C0C0;"> <%= item.likes.count %> </i> <% end %> <% end %> <% else %> <i class="fa fa-heart" aria-hidden="true"> <%= item.likes.count %> </i> <% end %>likes/create.js.erb
いいねをする
app/views/likes/create.js//投稿一覧いいね差し替え $("#index_like_<%= @item.id %>").html("<%= j(render partial: 'likes/like', locals: { item: @item}) %>"); //投稿詳細いいね差し替え $("#show_like_<%= @item.id %>").html("<%= j(render partial: 'likes/like', locals: { item: @item}) %>");likes/destroy.js.erb
いいねを取り消す
app/views/likes/destroy.js//投稿一覧いいね差し替え $("#index_like_<%= @item.id %>").html("<%= j(render partial: 'likes/like', locals: { item: @item}) %>"); //投稿詳細いいね差し替え $("#show/like_<%= @item.id %>").html("<%= j(render partial: 'likes/like', locals: { item: @item}) %>");最後に
最後までご覧いただきありがとうございます。
初学者ですので間違っていたり、分かりづらい部分もあるかと思います。
何かお気付きの点がございましたら、お気軽にコメントいただけると幸いです。
- 投稿日:2020-02-08T13:26:04+09:00
Mysql2::Error at /auth/twitter/callback Incorrect string value: 【Rails】
Mysql2::Error at /auth/twitter/callback Incorrect string value:
MtSQLのエラーで、保存する値に絵文字が含まれていると起きる。
絵文字を扱うことが出来る文字コードは
utf8mb4
だがデータベースに設定されていた文字コードはutf8
だった。それが原因。MySQLに設定された文字コードを調べる
$ show variables like "chara%";文字コードを変更する
MySQLのファイルはルートディレクトリの/etc/my.cnfにある。
$ vim my.cnf以下のように
utf8
をutf8mb4
に変更と追記。[mysqld] (省略) character-set-server=utf8mb4 [client] default-character-set=utf8mb4DBの再起動
$ systemctl restart mysqld.serviceRailsAppのdatabase.ymlを編集
default: &default adapter: mysql2 encoding: utf8mb4 pool: 5 username: root password: <%= ENV['MYSQL2_PASSWORD'] %> host: localhost
encoding:
の部分をutf8mb4
に変更する。データベースの作り直し
私は既存のデータベースの文字コードを変更したので、データベースも今一度作り直す必要があった。
$ rails db:drop $ rails db:create $ rails db:migrate当然ではあるが、格納していたデータはすべて消える。マイグレーションファイルは残っているため
rails db:migrate
すればモデルは直ぐに作り直せる。参考
- 投稿日:2020-02-08T12:13:59+09:00
Rails レイアウトテンプレート
アプリケーションのビューファイルの共通部分をまとめたもの。
yield メソッド
bodyタグ(
...)の間に、<%= yield %>と記載がある。この記述があることによって、「ここに全てのビューファイルが集約される」という仕組みができる。
例
上記のようにapplication.html.erbに記述
そうするとOur Blogと新規投稿のヘッダー表示はどの画面でも共通。リセットCSS
CSSファイルはapp/assets/stylesheets/というディレクトリに配置する。application.html.erbのstylesheet_link_tagの部分にapplicationと書いてあるので、このHTMLからはapp/assets/stylesheets/application.cssというCSSファイルを読み込むことを示す。
application.cssを開く
require_tree
引数として与えられたディレクトリ以下のCSSファイルをアルファベット順に全て読み込む。引数.(ドット)はカレントディレクトリを表す。
この記述によってapp/assets/stylesheetsというディレクトリにあるCSSファイルやSCSSファイルは全て読み込まれることになる。
- 投稿日:2020-02-08T11:57:57+09:00
Rails マイグレーションファイルの変更
- 投稿日:2020-02-08T10:39:57+09:00
RESTを分解してみた
はじめに
Railsチュートリアルをやっていて
「セッションをRESTfulなリソースとしてモデリングできると、他のRESTfulリソースと統一的に理解できて便利です。」
みたいな文章があってRESTとかRESTfulとかの意味を調べてもしっくりこなかったので自分なりにRESTを噛み砕いてアウトプットしようと思います。結論
まずはじめに結論から述べると、
「クライアントとサーバー間で行われる送受信は、決められた形式(HTTP)で行われている。ということを表現したもの。」
という自分なりの解釈になりました。
経緯
上記の結論に至った経緯を書いて行きます。
まずは自分なりにREST(Representational State Transfer)を英訳してみました。Google翻訳にてシンプルに『Representational State Transfer』と翻訳
↓↓
結果「代表的な状態の移転」
というなんのこっちゃ分からない表現に。。。そこで一つ一つを分解して英訳を実行。
①Representational→代表的な、具象の(はっきりした姿、形)
②State→状態
③Transfer→移送、移転(送受信のやりとりをあらわしてる??)つなぎ合わせると
『はっきりした形式での送受信』
↓
『決められた形式(HTTP)で送受信のやりとりをするシステムのこと??』
- HTTPについてはざっくりいうと通信するときのお約束事です。詳細は調べてみてください。
- ちなみに送受信はクライアントとサーバーとの間で行われることを指します。
以上の結果から
RESTってクライアントとサーバー間で行われる送受信が決められた形式(HTTP)で行われていることを表しているのかなあと。最後に
ちなみにRESTの由来はFieldingが大学院のときに発表した論文内での造語であるらしい。
造語ってことは正式な定義みたいなのは存在しないのかな?詳しいかた、知ってるよ!なかた、アドバイスや指摘していただけると嬉しいです。
以上となります。ではまた。
- 投稿日:2020-02-08T10:36:19+09:00
【Rails】param is missing or the value is empty:について
問題箇所のコード
controller.rbdef create @collect = Collect.create(collect_params) if @collect.save redirect_to "/group/show/#{@collect.group_id}" else render "new" end end (省略) private def collect_params params.require(:collect).permit(:url, :group_id) end主にcollect_paramsが悪いはず。
param is missing or the value is empty:
意味は単純にparamsが存在しないか空ですとなる。
パラメータを確認するとこんな感じ。ちゃんと情報渡せてるように見えるけどfalseになってる。
<ActionController::Parameters {"authenticity_token"=>"Ze5qfbN7z11+vpQ0BRGuCOV8U6m9e/aH6B7QuXFkJ13sXD66fpls3yxpB8fxymNI3XqOR6KiBmemdPRC++aVTw==", "url"=>"https://(省略)", "group_id"=>"5", "commit"=>"Save ", "controller"=>"collect", "action"=>"create"} permitted: false>原因
params.require(:collect)
の部分が不要なんだと思う。
(もともとCollectテーブルに保存されるものとして送信されているのに、params.require(:collect)
と書くとだぶるのかなあ)解決
controller.rbprivate def collect_params params.permit(:url, :group_id) end
params.require(:collect)
を削除した。参考は下記のURL
- 投稿日:2020-02-08T09:31:53+09:00
MacでDocker+Rails+MySQL環境構築手順メモ
勉強するときに環境構築にかなりつまづいたので、その手順をメモしときたいと思います。
環境構築手順
前提として、rails開発用のディレクトリが用意されているものとします。
以下の手順に従ってファイル作成やコマンドを実行していけば問題なく手順は整うと思います。
Dockerfileの作成(以下をコピペ)
# rubyはお好みのバージョンで(ローカルのバージョンがいいと思います) FROM ruby:2.6.3 # 必要なパッケージのインストール(基本的に必要になってくるものだと思うので削らないこと) RUN apt-get update -qq && \ apt-get install -y build-essential \ libpq-dev \ nodejs # 作業ディレクトリの作成、設定 RUN mkdir /app_name ##作業ディレクトリ名をAPP_ROOTに割り当てて、以下$APP_ROOTで参照 ENV APP_ROOT /app_name WORKDIR $APP_ROOT # ホスト側(ローカル)のGemfileを追加する ADD ./Gemfile $APP_ROOT/Gemfile ADD ./Gemfile.lock $APP_ROOT/Gemfile.lock # Gemfileのbundle install RUN bundle install ADD . $APP_ROOTGemfileの作成(以下をコピペ)
source 'https://rubygems.org' #好きなバージョンを指定 gem 'rails', '5.2.2'空のGemfile.lockの作成
何も書かなくていいです。
docker-compose.ymlを作成(以下をコピペ)
docker-compose.ymlversion: '3' services: db: image: mysql:8.0.17 command: mysqld --default-authentication-plugin=mysql_native_password volumes: - ./db/mysql_data:/var/lib/mysql environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: root ports: - "4306:3306" web: build: . command: bundle exec rails s -p 3000 -b '0.0.0.0' volumes: - .:/app_name ports: - "3000:3000" links: - dbターミナルでrails newを実行
$ docker-compose run web rails new . --force --database=mysql --skip-bundle上のコマンドを打ってしばらく待つ
database.ymlを修正(一度全て消してから以下をコピペ)
database.ymldefault: &default adapter: mysql2 encoding: utf8 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: <%= ENV.fetch("MYSQL_USERNAME", "root") %> password: <%= ENV.fetch("MYSQL_PASSWORD", "root") %> host: <%= ENV.fetch("MYSQL_HOST", "db") %> development: <<: *default database: app_name_development test: <<: *default database: app_name_test production: <<: *default database: app_name_production username: app_name password: <%= ENV['APP_NAME_DATABASE_PASSWORD'] %>ターミナルでDockerを起動
docker-compose buildしばらく待つ。できたら以下を実行。
docker-compose upDB作成
別のターミナルを開いて以下を実行
docker-compose run web rails db:createブラウザでlocalhost:3000にアクセス
サーバーが起動してたらおk。
サーバーを止める
docker-compose downサーバーを再起動
docker-compose upまとめ
これでrailsアプリ作成の準備が整うはずです。
環境構築は完全に初見ごろしだと思うので誰かの助けに慣れたら嬉しいです。
また、何かおかしい点とかあれば言って頂けるとありがたいです。お付き合い頂きありがとうございました。
参考サイト
- 投稿日:2020-02-08T03:46:36+09:00
外部キー作成の時の注意事項
テーマ「データベース 頭文字は大文字か小文字か」
この投稿はRailsのデータべース作成に関する記事です。
データベース周辺は間違えると後のアプリ開発に悪い影響を与えてしまいます。
このミスを参考にみなさんは出来る限りデータベース周辺では間違わないようにしてください!今回の主犯
外部キーの頭文字を大文字にしてしまったため、seed.rbからデータを入力できなくなった。
実物はこちらです。Faculity_id←これ。。。本来はfaculity_idとなるべきはずscheme.rbcreate_table "majors", force: :cascade do |t| t.index ["Faculity_id"], name: "index_majors_on_faculity_id" endミスの原因
1ヶ月ぶりにRailsを触り、フレームワークの使い方を忘れてしまったこと
(初心者は毎日触らなけれぼなりませんね。思い知りました?)ターミナルで入力した間違いのコマンド
referencesの前を大文字にしてしまったため、外部キーも大文字になった。
terminal.$ rails g migration AddFaculityToMajors Faculity:references本来は$ rails g migration Add(頭文字大カラム名)To(頭文字大テーブル名) (小文字カラム名):references
というコマンドの構成です。(下記のコマンドが正しい)terminal.$ rails g migration AddFaculityToMajors faculity:referencesseed.rbからデータを入力すると。。。
seed.rbからデータが送れない。外部キーが合わないため当然ちゃ当然ですね。
scheme.rbcreate_table "majors", force: :cascade do |t| t.index ["Faculity_id"], name: "index_majors_on_faculity_id" endこちらは小文字なので、キーが探せずrollbackされてしまった。
seed.rbMajor.create!(id: 1, name: '経営', faculity_id: 1)ここでもう1つ問題が。。。Faculity_id(主犯)が消えない
Faculity_idの外部キーを削除するために、削除コマンドを試したが、、、
terminal.$ rails g migration RemoveFaculity_idFromMajor faculity:references Running via Spring preloader in process 8806 invoke active_record create db/migrate/20200207160003_remove_faculity_id_from_major.rb $ rails db:migrate == 20200207160003 RemoveFaculityIdFromMajor: migrating ======================== -- remove_reference(:majors, :faculity, {:foreign_key=>true}) -> 0.0078s == 20200207160003 RemoveFaculityIdFromMajor: migrated (0.0083s) ===============scheme.rbcreate_table "majors", force: :cascade do |t| t.index ["Faculity_id"], name: "index_majors_on_faculity_id" end依然として、Faculity_idがある。なぜーーーーー(ぴえーん)
もうこれはテーブル丸ごと消してしまおう!!!!!(テーブル削除に初挑戦)
削除コマンドはこちらを参考にしました。
[Rails]不要になったmodelの削除方法(https://bokuranotameno.com/post-9880/)terminal.$ rails g migration drop_table_(テーブル名複数形)migrationファイルに削除するテーブル名を記述し$ rails db:migrateを実行します。
20200207073446_○○_majors.rbdef change drop_table :majors(テーブル名複数形) end一連のコマンドの流れ
terminal.$ rails g migration drop_table_majors Running via Spring preloader in process 9120 invoke active_record create db/migrate/20200207160634_drop_table_majors.rb $ rails db:migrate == 20200207160634 DropTableMajors: migrating ============================== == 20200207160634 DropTableMajors: migrating ================================== -- drop_table(:majors) -> 0.0020sこれでようやくFaculity_idは削除されました!!!(テーブルごと消えてしまったけど(笑))
modelの関連ファイルも削除します。
・modelファイルの削除 app/models/major.rb
・testファイルの削除 test/models/major_test.rb
・fixturesファイルの削除 test/fixtures/majors.yml新しいmodelを作成しよう!だがまた新たな問題が、、、
terminal.$ rails g model Major name:string faculity:references Running via Spring preloader in process 9606 invoke active_record conflict db/migrate/20200207161950_create_majors.rb Another migration is already named create_majors: /home/ec2-user/environment/review/db/migrate/20200207072743_create_majors.rb. Use --force to replace this migration or --skip to ignore conflicted file.どうやらmigrateフォルダ内にある前回Major modelを作成したmigrationファイルが残っていることが原因
migrateフォルダからmigrationファイルを削除すると、herokuにアップした時などデータベースが上手く作られない可能性があるが、新しくmodelを作成するので大丈夫だと信じる
migrationファイル削除後、もう一度試す!!!
$ rails g model Major name:string faculity:references Running via Spring preloader in process 10134 invoke active_record create db/migrate/20200207162947_create_majors.rb create app/models/major.rb invoke test_unit create test/models/major_test.rb create test/fixtures/majors.yml $ rails db:migrate == 20200207162947 CreateMajors: migrating ===================================== -- create_table(:majors) -> 0.0034s == 20200207162947 CreateMajors: migrated (0.0040s) ============================無事できた!!!
seedファイルも大丈夫かどうか確かめる。
terminal.$ rails db:seedこちらも無事エラーが出ない!!!!!
(教訓)データベースを作成する時大文字小文字に注意
モデルの作成は大文字
terminal.$ rails g model User(大文字)カラムの作成は小文字
terminal.$ rails g migration AddPostToUser user:references(小文字)皆さんも、これらの項目に注意をして、楽しくアプリ開発してください!!!
これが参考にされば幸いです!
- 投稿日:2020-02-08T01:59:07+09:00
【Rails】mechanizeを使えばrailsスクレイピングが余裕な件
スクレイピングに初挑戦したとき、検索上位にあがるサイトはどれもnokogiriを使用するのですが、dockerだとうまく利用できず四苦八苦していました。
その中で見つけたのがmechanizeというgemです。
自分用メモのためにも今回はコードを書いときたいと思います。
前提
railsアプリは作成済み
gem mechanize インストール済みController
controllerでは.getメソッドとsearchメソッドを使ってページ情報を取得します。
def home agent = Mechanize.new page = agent.get("https://nemlog.nem.social/") @elements= page.search('.visit-w') endあとはインスタンス変数@elementsを利用してviewに表示するだけです。
View
<%@elements.each do|element|%> <ul> <li> <%=link_to(element.inner_text,element[:href])%> </li> </ul> <%end%>.inner_textはテキスト部分だけ抜き出してくれます。
また、element[:href]は指定したhtml(この場合はclass=visit-w)の属性を抜き出します。まとめ
結論、mechanizeは神ですね。
スクレイピングをお手軽に試してみたい人はぜひ使ってみてください。おまけ
これを利用して簡単なnemlolgのスクレイピング サイトを作りました。
コードも確認できるので参考になればと思います。
- 投稿日:2020-02-08T01:38:09+09:00
rubocopで複数行の引数の最後の値にカンマを付ける
複数行の配列や引数を書くとき
最後にカンマを付けない派
seeds.rbUser.create!( name: 'foo bar', email: 'foo@baa.com', password: 'password', password_confirmation: 'password' )
rubocop -a
で自動整形すると、デフォルトでは上記のように整形されます。
ruby以外の言語だと、引数や配列の最後にはカンマを付けるとエラーになったりするので、一般的に使われている書き方だと思います。最後にカンマを付ける派
seeds.rbUser.create!( name: 'foo bar', email: 'foo@baa.com', password: 'password', password_confirmation: 'password', )分かりづらいですが、先程の例と比べると、最後の引数の後にカンマが付いているのが分かります。
rubyでは上記のように最後の引数にカンマを付けても問題なく動きます。
慣れていないと気持ち悪く感じるかもしれませんが、引数を追加したい時などに素早くコピペできるので便利だったりします。最後にカンマを付けるためのrubocopの設定
Style/TrailingCommaIn~~
を設定する引数/配列/ハッシュでそれぞれ設定するスタイルが異なります。
種類 スタイル名 引数 Style/TrailingCommaInArguments 配列 Style/TrailingCommaInHashLiteral ハッシュ Style/TrailingCommaInArrayLiteral それぞれ
EnforcedStyleForMultiline
プロパティをcomma
またはconsistent_comma
に設定すればOKです。rubocop.ymlStyle/TrailingCommaInArguments: EnforcedStyleForMultiline: comma Style/TrailingCommaInArrayLiteral: EnforcedStyleForMultiline: comma Style/TrailingCommaInHashLiteral: EnforcedStyleForMultiline: commaこれで
rubocop -a
を実行したときに引数/配列/ハッシュの値にカンマがつくようになります。comma
をconsistent_comma
にしても同様の結果が得られます。
consistent_comma
とcomma
の違いこちらのようなドキュメントを読んでも違いがわからなかったのでいくつか試してみたところ、複数行に引数を書きつつも、1行に複数の引数がある場合に違いが出る事がわかりました。
seeds.rb# consistent_comma User.create!( name: 'foo bar', email: 'foo@baa.com', password: 'password', password_confirmation: 'password', ) # comma User.create!( name: 'foo bar', email: 'foo@baa.com', password: 'password', password_confirmation: 'password' )分かりづらいですが、最後2つの引数を同じ行に書いています。
このようなときconsistent_comma
だと最後の引数にカンマが付き、comma
だと付きません。ちょっとハマったところ
下記の様にメソッドの行から引数を書き始めると、どの設定にしても下記のように整形されてしまいますのでご注意ください。
seeds.rbUser.create!(name: 'foo bar', email: 'foo@baa.com', password: 'password', password_confirmation: 'password')なお古いバージョンのrubocopでは引数/配列/ハッシュでのカンマ有無をすべて
Style/TrailingComma
で設定していたようですが、現在こちらは削除されているので使えません。参考
- 投稿日:2020-02-08T01:38:09+09:00
【RuboCop】複数行の引数の最後の値にカンマを付けたい
複数行の配列や引数を書くとき
最後にカンマを付けない派
seeds.rbUser.create!( name: 'foo bar', email: 'foo@baa.com', password: 'password', password_confirmation: 'password' )
rubocop -a
で自動整形すると、デフォルトでは上記のように整形されます。
ruby以外の言語だと、引数や配列の最後にはカンマを付けるとエラーになったりするので、一般的に使われている書き方だと思います。最後にカンマを付ける派
seeds.rbUser.create!( name: 'foo bar', email: 'foo@baa.com', password: 'password', password_confirmation: 'password', )分かりづらいですが、先程の例と比べると、最後の引数の後にカンマが付いているのが分かります。
rubyでは上記のように最後の引数にカンマを付けても問題なく動きます。
慣れていないと気持ち悪く感じるかもしれませんが、引数を追加したい時などに素早くコピペできるので便利だったりします。最後にカンマを付けるためのrubocopの設定
Style/TrailingCommaIn~~
を設定する引数/配列/ハッシュでそれぞれ設定するスタイルが異なります。
種類 スタイル名 引数 Style/TrailingCommaInArguments 配列 Style/TrailingCommaInHashLiteral ハッシュ Style/TrailingCommaInArrayLiteral それぞれ
EnforcedStyleForMultiline
プロパティをcomma
またはconsistent_comma
に設定すればOKです。rubocop.ymlStyle/TrailingCommaInArguments: EnforcedStyleForMultiline: comma Style/TrailingCommaInArrayLiteral: EnforcedStyleForMultiline: comma Style/TrailingCommaInHashLiteral: EnforcedStyleForMultiline: commaこれで
rubocop -a
を実行したときに引数/配列/ハッシュの値にカンマがつくようになります。comma
をconsistent_comma
にしても同様の結果が得られます。
consistent_comma
とcomma
の違いこちらのようなドキュメントを読んでも違いがわからなかったのでいくつか試してみたところ、複数行に引数を書きつつも、1行に複数の引数がある場合に違いが出る事がわかりました。
seeds.rb# consistent_comma User.create!( name: 'foo bar', email: 'foo@baa.com', password: 'password', password_confirmation: 'password', ) # comma User.create!( name: 'foo bar', email: 'foo@baa.com', password: 'password', password_confirmation: 'password' )分かりづらいですが、最後2つの引数を同じ行に書いています。
このようなときconsistent_comma
だと最後の引数にカンマが付き、comma
だと付きません。ちょっとハマったところ
下記の様にメソッドの行から引数を書き始めると、どの設定にしても下記のように整形されてしまいますのでご注意ください。
seeds.rbUser.create!(name: 'foo bar', email: 'foo@baa.com', password: 'password', password_confirmation: 'password')なお古いバージョンのrubocopでは引数/配列/ハッシュでのカンマ有無をすべて
Style/TrailingComma
で設定していたようですが、現在こちらは削除されているので使えません。参考
- 投稿日:2020-02-08T00:35:52+09:00
【Rails6】ActiveStorage::Blob#purge すると FrozenError
環境
- Ruby: 2.6.3
- Rails: 6.0.2.1
入力エラー時の purge でエラーが発生するようになった
こんな感じの has_one_attached の項目を持つmodelで、
post.rbclass Post < ApplicationRecord has_one_attached :image # 他にもいくつか項目・・ end入力エラー時に has_one_attached の項目を purge するとエラーが発生しました。
※Rails5.2.4.1までは正常に動作していましたposts_controller.rbdef create Post.create!(post_params) rescue ActiveRecord::RecordInvalid => e post = e.record post.image&.purge # <= ここでエラー!! endエラーメッセージはこんな感じでした。
FrozenError can't modify frozen Hashほう、Rails6からFrozenになったのか?
調査してみると既にissueがありました。
ActiveStoragehas_one_attached
not purging attachments · Issue #37069 · rails/rails以下のコメントに原因がめちゃめちゃ詳しく書いてありました。
https://github.com/rails/rails/issues/37069#issuecomment-525972179
今回の例に合わせて要約すると、
ActiveStorage::Blob#purge
は、blobレコード(imageの情報を持っているレコード)を destroy し、そのレコードの項目をFreezeします。/activestorage/app/models/active_storage/blob.rb# https://github.com/rails/rails/blob/v6.0.2.1/activestorage/app/models/active_storage/blob.rb#L232-L239 def purge destroy # <= ココ!! delete rescue ActiveRecord::InvalidForeignKey end
- その後、
delete
で保存されたファイルを削除しますが、Postレコードが保存されていないため、blobにはキーがありません。/activestorage/app/models/active_storage/blob.rb# https://github.com/rails/rails/blob/v6.0.2.1/activestorage/app/models/active_storage/blob.rb#L227-L230 def delete service.delete(key) # <= ココ!! #key を呼び出す service.delete_prefixed("variants/#{key}/") if image? end
ActiveStorage::Blob#delete
は#key
を呼び出します。#key
は、blob にまだキーがない場合にキーを生成しようとします。 生成されたキーをblobのFreezeされた項目に設定しようとするとエラー発生します。/activestorage/app/models/active_storage/blob.rb# https://github.com/rails/rails/blob/v6.0.2.1/activestorage/app/models/active_storage/blob.rb#L115-L118 def key # We can't wait until the record is first saved to have a key for it self[:key] ||= self.class.generate_unique_secure_token # ^ self[:key]が無いので右辺が実行されるが、selfの項目はFreeze済みなので FrozenError!! end原因はそんなところです。
回避策としては、
#purge
を呼ぶ代わりにnil
を設定すればOKです。posts_controller.rbdef create Post.create!(post_params) rescue ActiveRecord::RecordInvalid => e post = e.record post.image& = nil # <= purge を呼ぶ代わりに nil 設定で回避 end異常系のテストもしっかりしとかないとダメですね。
- 投稿日:2020-02-08T00:04:16+09:00
Rails6 テーブル作成時のmigrateでエラーが出た話
目的
- テーブル作成を行なった際にmigrateでエラーが出た話をまとめる
エラー内容
下記コマンドでモデルファイルを作成した。
$ rails g model user name:stringその後、下記コマンドでmigreteを実行した。
$ rails db:migrate下記のエラーが発生した
[14:50:10]MacBook-miriwo~/workspace/study/rails/tropical_fish_sns_of_rails$ rails db:migrate == 20200206055010 CreateUsers: migrating ====================================== -- create_table(:users) rails aborted! StandardError: An error has occurred, all later migrations canceled: Mysql2::Error: Table 'users' already exists /Users/admin/workspace/study/rails/tropical_fish_sns_of_rails/db/migrate/20200206055010_create_users.rb:3:in `change' /Users/admin/workspace/study/rails/tropical_fish_sns_of_rails/bin/rails:9:in `<top (required)>' /Users/admin/workspace/study/rails/tropical_fish_sns_of_rails/bin/spring:15:in `<top (required)>' bin/rails:3:in `load' bin/rails:3:in `<main>' Caused by: ActiveRecord::StatementInvalid: Mysql2::Error: Table 'users' already exists /Users/admin/workspace/study/rails/tropical_fish_sns_of_rails/db/migrate/20200206055010_create_users.rb:3:in `change' /Users/admin/workspace/study/rails/tropical_fish_sns_of_rails/bin/rails:9:in `<top (required)>' /Users/admin/workspace/study/rails/tropical_fish_sns_of_rails/bin/spring:15:in `<top (required)>' bin/rails:3:in `load' bin/rails:3:in `<main>' Caused by: Mysql2::Error: Table 'users' already exists /Users/admin/workspace/study/rails/tropical_fish_sns_of_rails/db/migrate/20200206055010_create_users.rb:3:in `change' /Users/admin/workspace/study/rails/tropical_fish_sns_of_rails/bin/rails:9:in `<top (required)>' /Users/admin/workspace/study/rails/tropical_fish_sns_of_rails/bin/spring:15:in `<top (required)>' bin/rails:3:in `load' bin/rails:3:in `<main>' Tasks: TOP => db:migrate (See full trace by running task with --trace)原因調査
下記コマンドを実行して現在のmigrateのステータスを確認した。
$ rails db:migrate:status以前にmigrateを実施した際のmigrationファイルのが無いことが判明した。
[14:52:57]MacBook- miriwo~/workspace/study/rails/tropical_fish_sns_of_rails/db/migrate$ rails db:migrate:status database: tropical_fish_sns_development Status Migration ID Migration Name -------------------------------------------------- up 20200206025324 ********** NO FILE ********** up 20200206025916 ********** NO FILE ********** down 20200206055010 Create users解決法
下記コマンドを実行してDBのリセットを行なった。
$ rails db:migrate:resetDBの状態を確認した。
[14:54:33]MacBook-miriwo~/workspace/study/rails/tropical_fish_sns_of_rails/db/migrate$ rake db:migrate:status (in /Users/admin/workspace/study/rails/tropical_fish_sns_of_rails) database: tropical_fish_sns_development Status Migration ID Migration Name -------------------------------------------------- up 20200206055010 Create users正常に反映されていることをがわかった。