Rails Tutorial 第６版 学習まとめ 第3章

###概要
この記事は私の知識をより確実なものにするためにRailsチュートリアル解説記事を書くことで理解を深め
勉強の一環としています。稀にとんでもない内容や間違えた内容が書いてあるかもしれませんので
ご了承ください。
できればそれとなく教えてくれますと幸いです・・・

出典
[Railsチュートリアル](https://railstutorial.jp/)

###これからやること
TwitterライクなサンプルアプリSample_appの作成を通してRailsアプリの作成方法を総合的に学習していく
本章では新しいプロジェクトSample_appの作成から始める。

第2章まではかなり詳細まで記事を書いたが第3章以降
コードの量がかなり増えてくるのでRailsチュートリアルを読めばできるところは
省略して書くことにします…

###セットアップ

####初期設定
新規でSample_appを作成し、
Gemfileを更新する。今回も本番用のpg gemをインストールしたくないので--without productionは忘れずに。
新規アプリなのでGitリポジトリを初期化する。
READMEもここで書き換えておく。
READMEを書き換えたらとりあえずコミットしておく。
GithubのSample_app用リモートリポジトリも作成しておく。
リモートリポジトリを作成したらプッシュしておく
Cloud9環境の場合はdevelopment.rbを編集し、アプリをローカル起動できるようにしておく。
とりあえず2章と同じようにhello,world表示のコードだけ追加してherokuにもプッシュしておく。

#####演習
1.Githubのリポジトリのトップページのサイト下部にREADMEが自動表示されている。
もちろんファイルを開いて直接覗いてもいい
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617753/06875aa3-964f-d8e6-adbb-91a95aeba5b7.png)
2.hello,world!が表示されているのが確認できる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617753/38116690-9ee2-ac90-3b44-2088b8de302b.png)
ここで豆知識
herokuにデプロイしたアプリのドメイン名は```heroku domains```で確認できる。


####静的ページ
開発の習慣として常にmasterブランチ上で作業するのではなく何かを実装するタイミングで都度トピックブランチを作成
して作業するのがいい習慣と言われている。そうすることで作業の分担がしやすい。作業のくくりが見えやすいなど
様々な利点がある。
癖付けに今回から都度トピックブランチを作って作業する

```
$ git checkout -b static-pages
```

#####静的ページの生成
さっそく静的ページ用のコントローラから作成していく

```
$ rails g controller StaticPages home help 
```
generateは省略形のgでも良い
他にも短縮形があり、そちらを覚えるのをお勧めする。

|完全なコマンド|短縮形|
|:--|:--|
|$ rails server|$ rails s|
|$ rails console|$ rails c|
|$ rails generate|$ rails g|
|$ rails test|$ rails t|
|$ bundle install|$ bundle|

Railsチュートリアル第6版より
https://railstutorial.jp/chapters/static_pages?version=6.0#table-shortcuts

とりあえず今の段階でstatic-pagesブランチをリモートにプッシュしておく
-uオプションを使ってoriginをstatic-pagesのアップストリームに設定すると
以降は```git push```だけで同じプッシュができる。

generateコマンドでコントローラを自動生成するとroutes.rbファイルも自動更新される。
今回の場合generateでhomeアクションとhelpアクションを作成したので

```ruby:routes.rb
Rails.application.routes.draw do
  get 'static_pages/home'
  get 'static_pages/help'
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
  root "application#hello"
end
```
homeアクションとhelpアクションのルーティングルールが自動設定されている。
さっそくローカルサーバーを立ち上げstatic_pagesのhomeページを表示してみる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617753/66f93b51-e367-c444-ce47-2a88bb042dcc.png)

RailsはURLを指定すると(今回は/static_pages/home)ルーターを参照し、
対応するコントローラのアクションを実行する。(StaticPagesコントローラのhomeアクション)
そのあとにそのアクションに対応するビューを出力する。
静的ページのコントローラでは都度内容が変わることはないので(ページの内容が同じ)アクションは空になっている。

#####演習
1.

```
$ rails g controller Foo bar baz
```
2.destroyも短縮形dが使える。

```
$ rails d controller Foo
```

####静的なページの調整
現在の状態だと作成されたhomeページ、helpページはHTMLで完結しており、完全に静的なページ
つまりRailsの知識がなくても編集できるページになっているという状態。
ここで第2章のようにユーザーのデータを取得したり投稿のデータを取得したりしてその内容に応じて
表示内容を変える(動的)ような動作をするとRailsの知識(ERB)が必要になる。

###テストから始める
####最初のテスト
aboutページを追加する前にaboutページに対するテストを先行で書いてみる。
generateコマンドでhomeとhelpページに対するテストが自動生成されているので
まずはテストを実行して問題ないことを現時点で確認しておく

```
$ rails t

...

Finished in 6.708000s, 0.2982 runs/s, 0.2982 assertions/s.
2 runs, 2 assertions, 0 failures, 0 errors, 0 skips
```

####Red
テスト駆動開発(TDD)のサイクルは
失敗するテスト(未実装の機能に対するテスト等)を書く→失敗(RED)
実際のアプリケーションコードを書いて先に書いたテストをパスさせる→成功(GREEN)
リファクタリング(REFACTOR)の3ステップ

これを踏まえてまだ作っていないAboutページのテストを書いてみる

```ruby:static_pages_controller_test.rb
require 'test_helper'

class StaticPagesControllerTest < ActionDispatch::IntegrationTest
  
  ...
  
  test "should get about" do
    get static_pages_about_url
    assert_response :success
  end
end
```
このコードではstatic_pagesのaboutページにGETリクエストを送って
レスポンスコードが200 OK(success)になるという内容をテストしている。

もちろんまだAboutページを何も実装していないので期待通りエラーをはく

####Green
先ほどのエラーメッセージをみてみる

```
Error:
StaticPagesControllerTest#test_should_get_about:
NameError: undefined local variable or method `static_pages_about_url' for #<StaticPagesControllerTest:0x000055b65cfc03b0>
    test/controllers/static_pages_controller_test.rb:15:in `block in <class:StaticPagesControllerTest>'
```

詳しく読み解くと
StaticPagesControllerTestのshould_get_aboutで
static_pages_about_urlなんてメソッドは定義されていないよ(AboutのURLがないよ)
と怒られている。

これはルーティングを設定していないから

さっそくルーティングを追加してみる。
routes.rbに
```  get 'static_pages/about'```を追加する。
ルーティングを追加することでstatic_pages_about_urlというヘルパーが有効になる。

もういちどテストを流すと今度は

```
Error:
StaticPagesControllerTest#test_should_get_about:
AbstractController::ActionNotFound: The action 'about' could not be found for StaticPagesController
    test/controllers/static_pages_controller_test.rb:15:in `block in <class:StaticPagesControllerTest>'
```

これもシンプルなエラーで
aboutアクションが見つからないよと怒られている。
ルーティングを追加して、static_pagesコントローラのaboutアクションを参照するようにした結果である。

さっそくAboutアクションを追加してみる

```
  def about 
  end
```
これをstatic_pagesコントローラの中に書くだけ
アクションの中身はhomeやhelpと同じくまだ完全に静的ページなので空でOK

これでテストを流すと今度は

```
Error:
StaticPagesControllerTest#test_should_get_about:
ActionController::MissingExactTemplate: StaticPagesController#about is missing a template for request formats: text/html
    test/controllers/static_pages_controller_test.rb:15:in `block in <class:StaticPagesControllerTest>'
```

これはaboutテンプレート(ビュー)が見つからないよと怒られている。

これもビューを追加して対応する。
app/views/static_pages内にabout.html.erbを追加する。

この状態でテストは成功(GREEN)する

```
Finished in 0.976764s, 3.0714 runs/s, 3.0714 assertions/s.
3 runs, 3 assertions, 0 failures, 0 errors, 0 skips
``` 


###少しだけ動的なページ
タイトルを動的に変更する。
勉強用にレイアウトを無効にしておく

```
$ mv app/views/layouts/application.html.erb layout_file
```
このコマンドでapplication.html.erbというレイアウトファイルの名前を一時的に変更し移動することで無効にしている。

先にタイトルのテストを書いておく
タイトルはページごとに変わればいいのでこのようなコードになる。

```ruby
  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
```

titleタグを選択しその中身が一致することを確かめている。
もちろん未実装のためテストはRED

まずはhome,help,aboutページのビューを書き換える。

それぞれのページにタイトルを設定したのでテストはGREENになる。

```
Finished in 0.062407s, 48.0717 runs/s, 96.1433 assertions/s.
3 runs, 6 assertions, 0 failures, 0 errors, 0 skips
```

####演習
1.ダブルクォーテーションで囲った部分(文字列)の中に#{}で変数を囲むとその変数を展開できる。
今回は変数の中にRuby on Rails Tutorial Sample Appという文字列を代入しているのでその文字列が展開される。
共通な部分は変数で代用して変更に柔軟に対応できるようにするのはどのプログラム言語でも変わらない基本事項。

```ruby
  def setup
    @base_title = "Ruby on Rails Tutorial Sample App"
  end
  test "should get home" do
    get static_pages_home_url
    assert_response :success
    assert_select "title", "Home | #{@base_title}"
  end

  test "should get help" do
    get static_pages_help_url
    assert_response :success
    assert_select "title", "Help | #{@base_title}"
  end
  
  test "should get about" do
    get static_pages_about_url
    assert_response :success
    assert_select "title", "About | #{@base_title}"
  end
```

####レイアウトと埋め込みRuby
現段階だとhome,help,aboutのどのページもHTML構造の大半(headなどの基本タグ)が重複している。
タイトルもRuby on Rails Tutorial Sample Appは共通。
こういった重複を取り除いてDRYすることが大切。

provideメソッドはビューで
```provide(:シンボル,"文字列")```といった指定をすることで
yieldでシンボルに結びつけた文字列を呼び出せる。
ビューでRubyのコードが使えるのは
ビューが埋め込みRuby[ERB(Embedded RuBy)]という形式のファイルでHTML内にRubyを埋め込めるようになっているから。
<% %>で囲むと中の文を実行するだけ
<%= %>で囲むと中の文を実行して結果がテンプレートに挿入されるという違いがある。

この仕組みを使ってタイトル部分を書き換えてみた結果がこちら

```erb
<% provide(:title,"Help") %>
<!DOCTYPE html>
<html>
  <head>
    <title><%= yield(:title) %> | Ruby on Rails Tutorial Sample App</title>
  </head>
```
もちろんテストも通る。

このままだと共通な部分が残ってしまうのでさっそくリファクタリングしてみることにする。
まずは先ほど無効化したレイアウトファイルを有効化する

```
mv layout_file app/views/layouts/application.html.erb
```

そしてhome,help,aboutにそれぞれ個別に入れているtitleタグを丸ごとレイアウトファイルのタイトルタグと入れ替える。

```erb:application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title><%= yield(:title)%> | Ruby on Rails Tutorial Sample 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>
```

yieldの部分には各ページのビューの中身がそのまま代入される。
つまり各ページを表示しようとしたときに表示されているのは
各ページのビューを読み込んだapplication.html.erbである。

あとはhtmlタグなどの余計なものが各ページのビューに残ってしまっていて、このままだとapplication.html.erbと
重複してしまうので不要な部分を削除し、各ページのビューはコンテンツだけにする。

```erb:help.html.erb
<% provide(:title,"Help") %>
<h1>Help</h1>
<p>
  Get help on the Ruby on Rails Tutorial at the
  <a href="https://railstutorial.jp/help">Rails Tutorial help page</a>.
  To get help on this sample app, see the
  <a href="https://railstutorial.jp/#ebook"><em>Ruby on Rails Tutorial</em>
  book</a>.
</p>
```

#####演習
1.先にContactのテストを書いておく(TDD)

```rb:static_pages_controller_test.rb
 
  test "should get contact" do
    get static_pages_contact_url
    assert_response :success
    assert_select "title", "Contact | #{@base_title}"
  end
```

あとはroutes.rbにcontactのルーティングを追加し、
static_pages_controllerにcontactアクションを追加し、
contactビューを作成すれば良い。

```
Finished in 1.044651s, 3.8290 runs/s, 7.6581 assertions/s.
4 runs, 8 assertions, 0 failures, 0 errors, 0 skips
```
テストもパスする。

####ルーティングの設定
とりあえずデプロイするためにhello,worldを表示するhelloアクションをルートURLに設定していたが
今回homeページを作ったのでそちらに移す
```  root 'static_pages/home'```

#####演習
1.

```rb:static_pages_controller_test.rb
  test "should get root" do
    get root_url
    assert_response :success
  end
```

2.Rubyは行頭に#をつけることでその行をコメントアウトできる
ちなみにWindowsはCTRL+/でその行をコメントアウトできる。
rootURLをコメントアウトする。```  # root 'static_pages#home'```

```
Error:
StaticPagesControllerTest#test_should_get_root:
NameError: undefined local variable or method `root_url' for #<StaticPagesControllerTest:0x000055d85cb31200>
    test/controllers/static_pages_controller_test.rb:10:in `block in <class:StaticPagesControllerTest>'
```
root_urlメソッドが定義されていないとエラーが出た。

rootURLを設定したことでroot_urlヘルパーが使えるようになっていることがわかる。

####本章のまとめ
3章の作業が一通り終わったので
コミットしておく
デプロイするときにはテストを走らせるとデプロイ後にバグ発見なんて言うめんどくさいことにならないのでいい。

[←前の章へ](https://qiita.com/take_webengineer/items/86da084f32e502ff4393)


[次の章へ→]()