現場で使える Ruby on Rails 5速習実践ガイド を読んで学んだこと

Rails速習で学んだことをおおまかに書き留めました。

https://amzn.asia/d/17kehBx

# 前提
RubyやRailsでは万物がオブジェクトとして認識する　とクラスとインスタンスオブジェクトが紹介されているので抜粋・加筆する
## RubyやRailsでは万物がオブジェクトとして認識する

オブジェクトという概念は簡単にいうと「モノ」のようなものである。私の現実世界でもモノは溢れている。食材・家・人間・動物という物体から、時間・愛・感情のような抽象的な概念もモノとして捉えることができる。
Ruby/Railsでも同じようにオブジェクトが溢れている。Ruby/Railsを書くというのはありふれたオブジェクトを取捨選択し、レゴブロックさながら新たな形を生み出すようなものである。

## クラスとインスタンスオブジェクト

Rubyのようなオブジェクト指向言語では一般的に、「オブジェクトXのクラスがYであるとき、XはYのインスタンスオブジェクトである」と捉える。例えば`text`はStringクラスのインスタンスオブジェクトであり、`1`はIntegerクラスのインスタンスオブジェクトである。
オブジェクトにどんな仕事をさせることができるのはそのオブジェクトが、どのようなクラスのオブジェクトであるかによる。例えば先述した`text`はStringクラスのオブジェクトなので、文字列の文字数をオブジェクトに尋ねることができる。

```ruby
"text".length
=> 4
```

# 基本的なRails Appの作成方法

ローカルで構築するケース

1. アプリケーションの雛形を作成する。
まず、rails newコマンドでアプリケーションの基本的なディレクトリ・ファイル一式を用意する。`rails new [app name] {option}`。例えばmessengerというアプリケーションを作成するとする。
DBは主にPostgreSQLやMySQLなどスケーラブルなRDBMSが採用されることが多い。指定する場合はこのようなコマンドになる。 `rails new messenger -d postgresql`
2. DBを作成する。
messengerのwork directoryに移動しDBを作成しておく。 `bin/rails db:create`
3. サーバが起動するか確認する。
DBが作成できればサーバを起動できる。次のコマンドでサーバを起動することができる。`bin/rails s`

Dockerで構築するケース

1. 必要なファイルを用意する
基本的に必要なファイルは次のファイルである。【Dockerfile・compose.yml・Gemfile・Gemfile.lock・entrypoint.sh】
・entrypoint.shはコンテナが正常終了しなかった場合に、railsのserver.pidが残りエラーの原因となるので削除するために配置している。
・Gemfile.lockは空の状態で用意する。
※　今回用いた教材(2018年初版)の環境に合わせているため現在(2023年)の環境構築とは若干異なる。
    
    ```docker
    FROM ruby:2.5.1
    # 本来であればapt-get update -qq && apt-get install -y postgresql-client build-essential nodejsでビルドできると思われるが 
    # 今回の教材環境だと古すぎたためかエラーが発生したため、Debianのアーカイブリポジトリを追加し、Node.jsのセットアップスクリプトを実行してる。
    RUN echo "deb http://archive.debian.org/debian/ stretch main" > /etc/apt/sources.list \
        && echo "deb http://archive.debian.org/debian-security stretch/updates main" >> /etc/apt/sources.list \
        && curl -sL https://deb.nodesource.com/setup_14.x | bash - \
        && apt-get update -qq && apt-get install -y postgresql-client build-essential nodejs
    
    RUN mkdir /myapp
    WORKDIR /myapp
    COPY Gemfile /myapp/Gemfile
    COPY Gemfile.lock /myapp/Gemfile.lock
    RUN bundle install
    
    COPY entrypoint.sh /usr/bin/
    RUN chmod +x /usr/bin/entrypoint.sh
    ENTRYPOINT ["entrypoint.sh"]
    EXPOSE 3000
    
    COPY . /myapp
    
    CMD ["rails", "server", "-b", "0.0.0.0"]
    ```
    
    ```yaml
    version: '3'
    services:
      web:
        build:
          context: .
          dockerfile: Dockerfile
        command: /bin/sh -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
        volumes:
          - .:/myapp
        ports:
          - 3000:3000
        depends_on:
          - db
        tty: true
        stdin_open: true
    
      db:
        platform: linux/x86_64
        image: postgres:10
        volumes:
          - db-volume:/var/lib/postgresql/data
        environment:
          POSTGRES_USER: postgresql
          POSTGRES_PASSWORD: password
          POSTGRES_HOST_AUTH_METHOD: md5
    
    volumes:
      db-volume:
    ```
    
    ```bash
    #!/bin/bash
    set -e
    
    # Rails に対応したファイル server.pid が存在しているかもしれないので削除する。
    rm -f /myapp/tmp/pids/server.pid
    
    # コンテナーのプロセスを実行する。（Dockerfile 内の CMD に設定されているもの。）
    exec "$@"
    ```
    
    ```markdown
    source 'https://rubygems.org'
    gem 'rails', '5.0.0.1'
    ```
    
2. Railsの雛形を作成する。
次のコマンドを入力し、railsの雛形を作成する。`docker-compose run web rails new . —force —d postgresql`
これにより新たなGemfileが作成されるため、イメージを再ビルドする必要がある。次のコマンドを実行する。`docker-compose build`
3. config/database.ymlを編集する
postgresql環境に合わせてファイルを以下のように編集する。
    
    ```yaml
    default: &default
      adapter: postgresql
      encoding: unicode
      # For details on connection pooling, see Rails configuration guide
      # http://guides.rubyonrails.org/configuring.html#database-pooling
      host: db
      # 実際のセキュリティ面では環境変数から取得するのがベストです。
      username: postgresql
      password: password
      pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
    ```
    
4. DBを作成する。
config/database.ymlの変更後、`docker-compose up`を実行。その後、`docker-compose run web rails db:create`を実行。localhost:3000で画面表示が問題ないかを確認する。

# MVCとは

MVCとはRailsが採用しているアーキテクチャの一種である。
アプリケーション固有のデータや処理の扱いの部分(Model)、UIに関わる部分、すなわちHTMLなどのHTTPレスポンスの中身を実際に組み立てる部分(View)、モデルやビューを統合的に制御する部分、ユーザが操作するクライアントからのリクエストを受け、必要に応じてモデルを利用したり、ビューを構築したり、適切な出力を作成する部分(Controller)の３つに役割を分けることにより管理のしやすさを向上させるのが目的である。

![https://i.gyazo.com/925002bc2393c7dc3697ceee596f7a9e.png](https://i.gyazo.com/925002bc2393c7dc3697ceee596f7a9e.png)

コントローラではURIやHTTPメソッドを元に、「リクエストを処理する担当箇所」をまず特定する。この特定はconfig/routes.rbによって行われる。

# モデルについて

Railsのモデルは主に２つの要素から構成される。

1. モデルに対応するRubyのクラス
2. モデルに対応するDBのテーブル

クラス名とDBのテーブル名には次のような命名規則が存在する。

- テーブル名はモデルのクラス名を複数形にしたもの。
- モデルのクラス名はキャメルケースで記述。テーブル名はスネークケースで記述。

### 作成手順

モデルを作成する際はRailsの用意しているジェネレートコマンド(gコマンド)を利用すると良い。
※勘違いしやすいが、テーブル名を複数形にするのであってgコマンドのモデル名等は単数形である。
`bin/rails g model [ model name ] { column name:type }  { column name:type } 〔 option 〕`
例：`bin/rails g model Task name:string content:text`

gコマンドによって`db/migrate/`配下にマイグレーションファイル、`app/models/`配下にモデルクラスのrbファイル、`test/`配下にテスト用ファイルが複数作成される。

作成後はマイグレーションでDBにテーブルを追加する。

## カラム/属性

Railsではテーブル設計する際にID,created_at,updated_atを自動的に用意をする。つまり、作成するアプリケーションに必要な属性のみを考えれば良いのである。
RailsではカラムにデフォルトでNULLが許容されている。カラムにNOT NULL制約を指定したい場合はマイグレーションファイルで指定する必要がある。尚NOT NULL制約では空文字を許容するため、許容しない場合はモデルにpresence:trueを記述する必要がある。


https://teratail.com/questions/59911

https://qiita.com/itengineer0415/items/a9d2a5657de5b52157c9

## マイグレーション

DBにテーブルを追加する際は、マイグレーションという仕組みを用いる。マイグレーションによってスキーマを変更したり、戻したりすることが可能になる。
例えば先ほどのgコマンドでマイグレーションファイルを作成するとこのようになる。

```ruby
class CreateTasks < ActiveRecord::Migration[5.2]
  def change
    create_table :tasks do |t|
      t.string :name
      t.text :content

      t.timestamps
    end
  end
end
```

カラムにNOT NULL制約を指定する場合は次のように記述する。`null:false`

```ruby
class CreateTasks < ActiveRecord::Migration[5.2]
  def change
    create_table :tasks do |t|
     # t.string :name
			t.string :name,null:false
      t.text :content

      t.timestamps
    end
  end
end
```

適用するには次のコマンドを実行する。`bin/rails db:migrate`

マイグレーションファイルを作成した際は、ロールバックも行えるか検証する必要がある。ロールバックが行えないと、万が一の場合以前のバージョンに戻すことが困難である。検証するには次のコマンドを実行する。`bin/rails db:migrate:redo`

余談ではあるが、現在のテーブル状態を視覚的に確認したい場合はschema.rbを確認すると良い。

```ruby
# 省略・・・
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 2023_08_18_051502) do

  # These are extensions that must be enabled in order to support this database
  enable_extension "plpgsql"

  create_table "tasks", force: :cascade do |t|
    t.string "name"
    t.text "content"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

end
```

# コントローラについて

コントローラを作成する際は、作成前に必要なアクションを考えておくとスムーズである。RailsでWebアプリケーションを開発するおいて基本的にはRESTfulに則ったCRUD(Create,Read,Update,Delete)を持たせることになると思う。先ほどのTaskモデルのアクションを設計すると次のようになる。
※ RailsにおいてDelete機能のアクション名はdeleteではなくdestroyである。

| HTTPメソッド | アクション名 | 機能 |
| --- | --- | --- |
| GET | index | 全てのタスクを表示する |
| GET | show | 特定のタスクを表示する |
| GET | new | タスク作成用のフォームを表示する |
| POST | create | タスクを作成する |
| GET | edit | タスク編集用のフォームを表示する |
| PATCH/PUT | update | タスクを更新する |
| DELETE | destroy | タスクを削除する |

アクションの全容を確認したらgコマンドでコントローラを作成する。コントローラを作成する際にアクションを指定すると同名のビューファイルも作成してくれる。（そのため先にアクションを確認したのだ。）
次のコマンドを実行する。今回必要なビューファイルは一覧(index)・詳細(show)・新規作成画面(new)・編集画面(edit)である。create,destroyはバック側の処理であってビューファイルは必要ないので指定しない。`bin/rails g controller tasks index show new edit`

ここでなぜモデルを作成する際は**Task**と単数系であったのに、今回は複数形であることに疑問を持つ人はいないであろうか。
これは、コントローラーがデータベース内の複数のレコードを操作する可能性があるからである。コントローラーはアプリケーションの機能を表現し、特定のリソースを操作するための処理を保持する。なのでRailsではコントローラー名は複数形にするのが慣例である。
実行後にcontrollers/配下にこのようなファイルが生成される

```ruby
class TasksController < ApplicationController
  def index
  end

  def show
  end

  def new
  end

  def edit
  end
end
```

# ルーティングについて

`bin/rails g controller tasks index show new edit`を実行した際、config/routes.rbにもアクションに対応したルーティング設定を行う。

```ruby
Rails.application.routes.draw do
  get 'tasks/index'
  get 'tasks/show'
  get 'tasks/new'
  get 'tasks/edit'
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
```

しかし、これだけだとdestroyなど指定しなかったものはルーティング設定されていない。このまま加筆しても良いのだが、Railsでは`resources :controller`とすることでRESTfulなルーティングを設定してくれる。
ついでにルートパスをデフォルト画面ではなく一覧(index)が表示されるようにしよう。
次のように変更する。

```ruby
Rails.application.routes.draw do
  # get 'tasks/index'
  # get 'tasks/show'
  # get 'tasks/new'
  # get 'tasks/edit'
	root to: 'tasks#index'
  resources :tasks
end
```

カスタムアクションが必要な場合や、RESTfulなアクション以外のルートを設定する場合には、手動でルーティングを追加することも可能だが今回は割愛する。

コントローラを名前空間でグルーピングすることもできる。よく使われる名前空間といえば管理者である`Admin`であろう。`/controller/admin`というサブフォルダを作成したのち、routes.rbにて`namespace`メソッドを用いることにより名前空間を定義し、コントローラをグループ化する。

```ruby
namespace :admin do
  resources :tasks
end
```

次のようなルーティングが作成される。

| Path | Controller#Action | Routing Helper |
| --- | --- | --- |
| /admin/tasks | admin/tasks#index | admin_tasks_path |
| /admin/tasks/:id | admin/tasks#show | admin_task_path(:id) |
| /admin/tasks/new | admin/tasks#new | new_admin_task_path |
| /admin/tasks | admin/tasks#create | admin_tasks_path |
| /admin/tasks/:id/edit | admin/tasks#edit | edit_admin_task_path(:id) |
| /admin/tasks/:id | admin/tasks#update | admin_task_path(:id) |
| /admin/tasks/:id | admin/tasks#destroy | admin_task_path(:id) |

上記のように名前空間を使用することで、「一般・管理者」が存在する場合かつ同じタスクという用語が用いられる際に、コントローラやビュー内で名前やコードが衝突するのを防ぐことができる。詳しくはRailsガイドを参照 。

https://railsguides.jp/routing.html

# Helper methodとは

ヘルパーメソッドとはRailsで提供される便利なメソッドの集合である。これらのメソッドを用いることで、開発者がより簡単にコードを記述することができ、セキュリティ面も担保される。

- リンクヘルパーメソッド
ルートヘルパーメソッドはURLへのパスを生成するメソッドである。用いることでURLを直接指定することなくリンクなどを作成することができる。`bin/rails routes`コマンドやサーバ上で確認することができる。
- フォームヘルパーメソッド
フォームヘルパーメソッドは、簡単にフォームが作成できる上にクロスサイトリクエストフォージェリ（CSRF）対策などセキュリティにも優れたフォームを提供してくれる。最も基本的なフォームヘルパーは`form_with`である。
    
    ```reason
    <%= form_with do |form|  %>
      Form contents
    <% end %>
    ```
    
    このフォームは次のようなHTMLを生成する
    
    ```html
    <form accept-charset="UTF-8" action="/" method="post">
      <input name="authenticity_token" type="hidden" value="J7CBxfHalt49OSHp27hblqK20c9PgwJ108nDHX/8Cts=" />
      Form contents
    </form>
    ```
    
    詳しくはRailsガイドを参照。
    
https://railsguides.jp/action_view_helpers.html
    
https://railsguides.jp/form_helpers.html
    

# Strong Parametersとは

コントローラでリクエストパラメータを受け取る際に、想定通りのパラメータかどうかをホワイトリスト方式でチェックする機能がStrong Parametersである。例えばこのようなコードがあるとする。

```ruby
task = Task.new(params[:task])
```

このコードだとパラメータに意図しない属性や悪意のある実行コードが紛れ込んだ場合そのまま実行してしまう恐れがある。
これを防ぐには、パラメータのどの属性を許可してどの属性を弾くかを制御する必要がある。
例えばこれまでのtasksテーブルではnameとcontent以外は不必要であるため次にように記述する。

```ruby
def create
	@task = Task.new(set_params)
end

private

def set_params
	params.require(:task).permit(:name,:content)
end
```

viewファイルからStrong Paramsが参照できたら元もこもないのでprivate配下に記述する。

# モデル検証について

Railsでは独自に検証用のヘルパーを用意している。これを利用することでDBの定義でデータの範囲をあまり制限せずとも、モデルで制約をかけることでそこそこの正しい状態を保つことができる。
Railsにおけるモデル検証は基本的に「モデルオブジェクト（レコード）をDBに登録・更新する前に検証を行い、エラーが発生すればロールバックするという仕組みである。
主に利用するヘルパーは以下のようなものがある。

| 内容 | 説明 | Helper |
| --- | --- | --- |
| presence | 指定された属性が空または空白でないことを検証する | validates :attribute, presence: true |
| numericality | 指定された属性が数値であるかどうかを検証する | validates :attribute, numericality: true |
| length | 指定された属性の長さを検証する | validates :attribute, length: { minimum: 2 } |
| inclusion | 指定された属性がリストに含まれているかどうかを検証する | validates :attribute, inclusion: { in: ['A', 'B', 'C'] } |
| exclusion | 指定された属性がリストに含まれていないことを検証する | validates :attribute, exclusion: { in: %w(admin superuser) } |
| format | 指定された属性が正規表現にマッチするかどうかを検証する | validates :attribute, format: { with: /\\A[a-zA-Z]+\\z/, message: "only allows letters" } |
| uniqueness | 指定された属性が一意であるかどうかを検証する。 | validates :attribute, uniqueness: true |
| confirmation | 指定された属性が確認用の入力と一致しているかどうかを検証する | validates :attribute, confirmation: true |

これらのエラーメッセージは`errors.full_messages`で取得できる。
**※ モデル検証をしたからとしって、DBで制約を指定しなかった場合、想定外が発生した際に痛い目を見ることになるので最低限はDBでの制約も必要である。**

# redirect_toとrenderについて

redirect_toやrenderは主にコントローラ層で活用するメソッドである。主にcreate,updateアクションで多用するであろう。

```ruby
def create
	@task = Task.new(strong_params)
	if @task.save!
		redirect_to tasks_path
	else
		render :new
	end
end
```

一見するとどちらも画面遷移を促すものだがどういった違いがあるかを理解しておく必要がある。

- redirect_toについて
redirect_toは処理後に一覧画面や詳細画面に遷移する際、つまり別のURLにリダイレクトするために用いられる。特徴として前回の状態を保持せず新しいリクエストを発生させる(**データの永続性と一貫性)**。例えば主に次のようなユースケースがある。
    - ユーザーがログインまたはログアウトした後に、別のページにリダイレクトするため。
    - フォームの送信後、成功メッセージを表示するために別のページにリダイレクトするため。
- renderについて
renderは同じアクションないで別のビューを表示するために使用される。redirect_toと違い同アクション内で状態を保持したまま異なるビューを表示させる。例えば主に次のようなユースケースがある。
    - フォームのバリテーションエラーが発生した際、元のフォームページに戻りフラッシュメッセージを表示するため。エラーメッセージとともに、入力フォームを持ったまま、ユーザーに再度入力の機会を与えることができる。
    - 特定のアクションが他のアクションの一部である場合に、同じアクション内で異なるビューを表示するため。

saveが成功した際にredirect_toを使う理由がいまいちパッとしない方もいるであろう。
例えばECサイトの購入処理でsaveがtrueの際にrenderメソッドを利用するとしよう。
先述したが、renderの特徴として状態（つまりデータ）を保持するので、ブラウザのリフレッシュなどが発生した場合に、同じデータが再度送信される可能性がある。これは二重決済などの原因となる。
詳しくは **POST-Redirect-GET パターン(PRG)** を確認するとわかりやすい。

https://zenn.dev/imah/articles/3d186a6462ecc8

# Railsコマンドを実行する際のbin/railsについて

railsコマンドを実行する際に、`rails db:create`ではなく、`bin/rails db:create`とする。これはアプリケーションのルートディレクトリ直下のbinディレクトリにあるrailsというスクリプトを呼び出している。このスクリプトを利用すると、`bundle exec rails`として実行した際と同様に、Gemfile通りのgemを利用できる環境上でrailsコマンドを実行できる。なのでDocker環境上でコマンドを利用する際も`docker-compose exec web bundle exec rails …`としているのである。

### 頻用するRailsコマンドについて

rails new以外のbin/railsで実行するコマンドは全て、作成中のRails Appのroot dirで実行すること。

| $rails new [app name] {option} | 実行すると、指定した名前で新しいRails appが作成される。この場合latest versionが選択される。 |
| --- | --- |
| $rails 5.1.6 new [app name] | _n.n.n_で指定したversionで作成できる |
| $rails new [app name] -d postgresql | -d optionで使用するDBを指定できる。デフォルトではSQlite3が使用される。一般的に通常はよりスケーラブルなPosggreSQLやMySQLが使用される。 |
| $bin/rails s -p 3000 | 3000 portでrails serverを起動する |
| $bin/rails c | Rails appの環境上でコンソールを起動する。 |
| $bin/rails c -s | -s　option(—sandbox)でDBのデータを変更することなく動きを確認できる。 |
| $bin/rails db | DBのコンソールを起動する |
| $bin/rails routes | Routingを確認できる。Rails appのどのコントローラで、どのアクションが設定されているかを確認できる |
| $bin/rails g [対象の雛形] {引数} | モデルやコントローラ、マイグレーションファイルなどの雛形を作成する。対象の雛形は複数形にする。
例: bin/rails g controller Books index new create |
| $bin/rails d [対象] {引数} | 対象のパーツとそれに紐づくファイルを削除する。 |
| $bin/rails db:create | Rails appで使うDBを作成する。 |
| $bin/rails db:migrate | マイグレーションを実行する。 |
| $bin/rails db:migrate:redo | ロールバックと再マイグレーションを行う。 |

# おわりに
Railsを扱うにおいて最低限知っておいた方がいい機能などをまとめました。本当はセッション管理・ログイン機能の作成方法なども書きたかったのですが。
気になった方は本書をご購入ください。ちょっとふるいですが、Railsを扱うことにおいてはわかりやすく書かれています。