個人的リマインド用
参考
Ruby on Rails チュートリアル プロダクト開発の0→1を学ぼう
3 ほぼ静的なページの作成
※最初に注意点
rails newのとき--skip-bundleをすると、RailsでJSを利用する上で必要なパッケージのインストールもスキップする。(どんなパッケージかは後々8とかで出てくる)
gemインストールの際に
bundle _2.3.14_ config set --local without 'production'
上記のコマンドでproduction環境でしか使わないgemはインストールしないようにできる
例えばPostgreSQL用のpgというgemをdevelopment環境にインストールせず、
代わりにSQLiteがdevelopment環境とtest環境で使われるようになる。
一般的にはdevelopment環境とproduction環境は極力同じに保ち、環境ごとの違いを作らないようにするのが理想的。しかし、SQLiteの方がPostgreSQLよりもローカルでのインストールや設定がずっと楽なので、今回は異なるデータベースを使うことにする。
静的ページの作成
まずはStaticPages(キャメルケース)コントローラを作成。今回使うアクションはhome,help,aboutの3つ。学習のため、aboutは後々作成することに。
※コントローラ名をキャメルケースにするのは慣習。StaticPagesからstatic_pages_controllerが生成される。
rails g controller StaticPages home help
※コントローラー名は複数形!その後にアクション名を書けば自動で生成される。
豆知識
| 完全なコマンド | 短縮系 |
|---|---|
| rails server | rails s |
| rails console | rails c |
| rails generate | rails g |
| rails test | rails t |
| bundle install | bundle |
元に戻す方法
コントローラを生成した後にやり直したいとき。コントローラ以外にも大量のファイルが生成されるためただ消すだけでは足りない。そういうときはdestroyを使う。
rails generate controller StaticPages home help
rails destroy controller StaticPages home help
rails generate model User name:string email:string
rails destroy model User
マイグレーションの変更を元に戻したいとき
rails db:migrate
rails db:rollback 1つ前に戻したいとき
rails db:migrate VERSION=0 最初の状態に戻したいとき
routesファイルについて
controllerを作成した際にroutesファイルが変更されている。
config/routes.rb
Rails.application.routes.draw do
get "static_pages/home"
get "static_pages/help"
root "application#hello"
end
get /static_pages/homeに注目
このルールは、/static_pages/homeというURLに対するリクエストを、StaticPagesコントローラのhomeアクションと結びつけている。今回はgetと書かれているから、HTTPリクエストのGETリクエストを受け取った時に対応するアクションを結びつけている。
GETやその他のHTTPメソッドについて
HTTPには4つの基本的な操作があり、それぞれGET,POST,PATCH,DELETEという4つの動詞に対応づけられている。
GETは最頻出のHTTPリクエストで、主にWeb上のデータを読み取る時に使われる。例えば、Webサイトを開く時とかにGETリクエストをサイトに送信する。
POSTは、ページ上のフォームに入力した値を、ブラウザから送信する時に使われる。何かを作成するとき、例えばユーザー登録フォームで新しいユーザーを作成する際にPOSTリクエストを送信する。
他にもPATCHは更新の際、DELETEは削除の際に使われる。これらは、ブラウザがネイティブには送信しないものである。しかしRailsなどのフレームワークは、ブラウザがこれらの操作のリクエストを送信しているかのように見せかける技術を駆使し、PATCHとDELETEという操作を実現している。
再び静的サイト制作に戻る
現在StaticPagesコントローラがどうなっているかというと
app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController
def home
end
def help
end
end
こんな感じ。一般的なRESTアクションには対応していない。
このページはStaticPagesControllerというクラスを定義していることが分かる。このようなクラスはメソッドをまとめる時に便利。また、ApplicationControllerクラスを継承していることもわかる。
見ての通りメソッドは空で、通常のRubyならば何も動作しないが、特定のクラスを継承していることでRails特有の振る舞いをする。具体的には、/static_pages/homeというURLにアクセスすると、StaticPagesコントローラを参照し、homeアクションに記述されているコードを実行する。その後アクションに対応するビューを出力する。今回対応するビューはhome.html.erb
テスト入門
単語
テスト駆動開発(TDD): テストの手法の1つで、最初に「正しいコードがないと失敗するテスト」 を書き、次に本編のコードを書いてそのテストがパスするようにする
テストをいつ行えば良いのか
→まずはメリットのおさらい
1.テストが揃っていれば、機能停止に陥るような回帰バグ(Regression Bug: 以前のバグが再発したり機能の追加/変更に副作用が生じたりすること)を防止できる。
2.テストが揃っていれば、コードを安全にリファクタリング(機能を変更せずにコードを改善)できる。
3.テストコードは、アプリケーションコードから見ればクライアントとして動作するので、アプリケーションの設計やシステムの他の部分とのインターフェイスを決めるときにも役に立つ。
→いつやるか
・アプリケーションのコードよりも明らかにテストコードの方が短くシンプルになる(=簡単に書ける)のであれば、「先に」書く
・動作の仕様がまだ固まりきっていない場合、アプリケーションのコードを先に書き、期待する動作を「後で」書く
・セキュリティが重要な課題またはセキュリティ周りのエラーが発生した場合、テストを「先に」書く
・バグを見つけたら、そのバグを再現するテストを「先に」書き、回帰バグを防ぐ体制を整えてから修正に取りかかる
・すぐにまた変更しそうなコード(HTML構造の細部など)に対するテストは「後で」書く
・リファクタリングするときは「先に」テストを書く。特に、エラーを起こしそうなコードや止まってしまいそうなコードを集中的にテストする
主要なテストはコントローラテスト、モデルテスト、統合テストの3つ。
テストに取り掛かる
手順としてはテストを書く→失敗することを確認→コードを書き換える。
rails g controllerをした際にテストファイルが作成されている。
test/controllers/static_pages_controller_test.rb
require "test_helper"
class StaticPagesControllerTest < ActionDispatch::IntegrationTest
test "should get home" do
get static_pages_home_url
assert_response :success
end
test "should get help" do
get static_pages_help_url
assert_response :success
end
end
それぞれのテストでは、アクションをgetして正常に動作することを確認する。この確認は「アサーション」(assertion)と呼ばれる手法で行う。getはHomeページやHelpページがいわゆる「GETリクエストを受け付ける」普通のWebページであることを示している。その次の「response:success」は実際にはHTTPのステータスコード(ここでは200 OK)を表す。
つまり1つ目のテストは「Homeページのテスト。GETリクエストをhomeアクションに対して送信せよ。そうすれば、リクエストに対するレスポンスは[成功]になるはず」というもの。
続いてaboutページのテストと作成
test/controllers/static_pages_controller_test.rb
require "test_helper"
class StaticPagesControllerTest < ActionDispatch::IntegrationTest
test "should get home" do
get static_pages_home_url
assert_response :success
end
test "should get help" do
get static_pages_help_url
assert_response :success
end
test "should get about" do #←このテストを追加
get static_pages_about_url
assert_response :success
end
end
当然失敗する。なぜ失敗したか。
1.AboutページへのURLが見つからない
config/routes.rb
Rails.application.routes.draw do
get "static_pages/home"
get "static_pages/help"
get "static_pages/about" #←これを追加
root "application#hello"
end
この結果static_pages_about_urlというヘルパーが使えるようになるらしい。
2.StaticPagesコントローラにaboutアクションがない
app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController
def home
end
def help
end
def about #←これを追加
end
end
3.テンプレート(ビュー)がない
touch app/views/static_pages/about.html.erb
ちょっとだけ動的なページの作成
今回はページごとにtitleを変える
| ページ | URL | 基本タイトル | 追加タイトル |
|---|---|---|---|
| Home | /static_pages/home | "Ruby on Rails Tutorial Sample App" | "Home" |
| Help | /static_pages/help | "Ruby on Rails Tutorial Sample App" | "Help" |
| About | /static_pages/about | "Ruby on Rails Tutorial Sample App" | "About" |
タイトルをテストする
今回使うテストメソッド
assert_select: 特定のHTMLタグが存在するかどうかをテストする。この種のアサーションメソッドは「セレクタ」と呼ばれることもある。
test/controllers/static_pages_controller_test.rb
require "test_helper"
class StaticPagesControllerTest < ActionDispatch::IntegrationTest
test "should get home" do
get static_pages_home_url
assert_response :success
assert_select "title", "Home | Ruby on Rails Tutorial Sample App"
#↑追加
end
test "should get help" do
get static_pages_help_url
assert_response :success
assert_select "title", "Help | Ruby on Rails Tutorial Sample App"
#↑追加
end
test "should get about" do
get static_pages_about_url
assert_response :success
assert_select "title", "About | Ruby on Rails Tutorial Sample App"
#↑追加
end
end
一番上のテストを例にすると、
タグ内に、「Home | Ruby on Rails Tutorial Sample App」という文字列があるかどうかをチェックしている。ただ単にhtml内にそれぞれ書き込めばテストはパスできるが、それだとDRY(Don't Repeat Yourself)原則に反する。そこで重複を取り除くテクニックの一つとして、ビューで「ERB」が使える。Railsのprovideというメソッドを使い対ロルをページごとに変更する。
app/views/static_pages/home.html.erb
<% provide(:title, "Home") %> ←追加
<!DOCTYPE html>
<html>
<head>
<title><%= yield(:title) %> | Ruby on Rails Tutorial Sample App</title>
↑追加
</head>
<body>
<h1>Sample App</h1>
<p>
This is the home page for the
<a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a>
sample application.
</p>
</body>
</html>
<% provide(:title, "Home") %>
<title><%= yield(:title) %> | Ruby on Rails Tutorial Sample App</title>
provide→メソッドの引数では、"Home"という文字列と、:titleというラベルを関連づけている。
yield→provideで渡された文字列を挿入。ページごとに"Home"の部分を書き換えれば、テストをパスできる。
さらによく見ると
3つのページで違うのはbodyの中身だけ。リファクタリングできそう。
どうやるかというと、application.html.erbに共通部分を書く。
app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
<head>
<title><%= yield(:title) %> | Ruby on Rails Tutorial Sample App</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta charset="utf-8">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
</head>
<body>
<%= yield %>
</body>
</html>
body内のyieldは、各ページにそれぞれ内容が記述されている。
参考にhomeページを
app/views/static_pages/home.html.erb
<% provide(:title, "Home") %>
<h1>Sample App</h1>
<p>
This is the home page for the
<a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a>
sample application.
</p>
ちなみに
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag ... %>
この部分は、スタイルシートとJSをページ内にインクルードするためのもの。これらはアセットパイプラインの一部であり、クロスサイトスクリプティング攻撃を緩和する、コンテンツセキュリティポリシーを実装するcsp_meta_tagと、クロスサイトリクエストフォージェリー攻撃を緩和するcsrf_meta_tagも含む(????)
最後にルーティングの設定
ルートファイル(開いて最初に見えるファイル)をhomeページに変えておこう。
config/routes.rb
Rails.application.routes.draw do
root "static_pages#home" ←追加
get "static_pages/home"
get "static_pages/help"
get "static_pages/about"
end
おまけ
ページの最後にテストの色を変えて見やすくする方法と、Guardでのテストの自動化の方法が書いてあります。