Ruby on Rails6 実践ガイド cp7~cp9 【メモ】

## はじめに
この記事の筆者はプログラミングを学習し始めたばかりの初心者です。間違いがあれば指摘していただけると幸いです。

## 概要
この記事は[Ruby on Rails6 実践ガイド](https://book.impress.co.jp/books/1118101134)を読んで学んだことを自分用のメモとして記録したものです。抜粋してピックアップするので読みづらいと思われます。すいません。
この本には、続編の[機能拡張編](https://book.impress.co.jp/books/1118101135)もあり、記事を書いている段階で二冊とも学習を終えています。復習もかねて記事を書いていくつもりです。
18のチャプターに分かれているので、見出しごとに区切っていきます。

前の記事
[Ruby on Rails6 実践ガイド cp4~cp6 【メモ】](https://qiita.com/nizi24/items/aca9a745ccda08081eeb)

## Chapter7 ユーザー認証(1)

##### コマンドで生成したファイルを削除する

```
$ bin/rails destroy model XXX
```
XXXの部分に生成したファイルの名前を入力します。モデル以外でも使用できます。

##### インデックス作成時の工夫

```ruby
add_index :staff_members, "LOWER(email)", unique: true
```

第二引数にSQLの関数を用いると文字列全体を式として評価した値がインデックス作成で用いられるようになります。

##### テーブル名と属性名のカスタマイズ

本書では使用されていませんでしたが、モデル名とテーブル名の関連付けは自由に変更できます。

例、StaffMemberモデルとstaffテーブルを結びつける場合

```ruby:staff_member.rb
class StaffMember < ActiveRecord::Base
  self.table_name = "staff"
end
```
(staffは集合名詞なので例が悪いかもしれません。)

カラム名とは異なる名前の属性を用いることもできます。

```ruby
alias_attribute :family_name, :last_name
```

第一引数が別名で、第二引数がそれに対応するカラムの名前です。

## Chapter8 ユーザー認証(2)

### フォームオブジェクト

>フォームオブジェクトを用いると、データベースと無関係なフォームやデータベーステーブルと1対1に結びついていないフォームをform_withメソッドで生成できます。

例、ログインフォーム

```ruby:app/forms/staff/login_form.rb
class Staff::LoginForm
  include ActiveModel::Model

  attr_accessor :email, :password
end
```
フォームオブジェクトは
* ActiveRecord::Baseを継承しない
* ActiveModel::Modelをincludeする
という特徴が必要です。

##### コントローラー

```ruby:sessions_controller.rb
  def new
    @form = Staff::LoginForm.new
  end
```

##### ビューテンプレート

※一部抜粋

```ruby:new.html.haml
= form_with model: @form, url: :staff_session do |f|
  = f.label :email, "メールアドレス"
  = f.email_field :email

  = f.label :password, "パスワード"
  = f.password_field :password

  = f.submit "ログイン"
```

#### プレフィックス

パラメータの角括弧の前にある部分をプレフィックスと呼びます。
例、staff_login_form[password]の場合、staff_login_formがプレフィックス

角括弧の内側にある部分(上の例だとpassword)をフィールド名と呼びます。

>プレフィックスはform_withメソッドのmodelオプションに指定したオブジェクトのクラス名から導き出されます。

form_withのscopeオプションを用いれば、プレフィックスを変更できます。

```ruby
= form_with model: @form, url: :staff_session, scope: "form" do |f|
  #省略
```
上記の例だとプレフィックスはformになります。

---
### サービスオブジェクト

サービスオブジェクトはファットコントローラーやファットモデルを避けるためにRailsコミュニティの中で生まれた概念です。コントローラーやモデルに記述するのはあまりふさわしくない、サービスに必要なロジックを移行することができます。

例、ユーザー認証のためのサービスオブジェクト

```ruby:app/services/staff/authenticator.rb
class Staff::Authenticator
  def initialize(staff_member)
    @staff_member = staff_member
  end

  def authenticator(raw_password)
    #ロジックは省略
  end
end
```

## Chapter9 ルーティング

#### ルーティングに名前を与える

asオプションを用いると、ルーティングに名前を与えることができます。urlパスを指定するときに使う名前です。

```ruby:routes.rb
  get "login" => "sessions#new", as: :login_form
  post "login" => "sessions#create", as: :authentication
```
---
### namespaceメソッドのパスを変更する

```ruby:routes.rb
  namespace :staff, path: "" do
    root #top#index
  end
```

namespaceメソッドにpathオプションを与えると、名前空間のURLパスが変化します。
上の場合では、トップページのURLパスが/staffから/に変わります。

---
### ルーティングの分類

操作が特定のレコードを対象に行われる(URLパスに主キーのパラメータが含まれる)アクションに対するルーティングを<strong>メンバールーティング</strong>と呼びます。
逆にURLパスのパターンにパラメータ:idを持たないルーティングを<strong>コレクションルーティング</strong>と呼びます。

7つの基本アクション(resourcesで設定されるルーティング)の中では、

* メンバールーティング
  * show
  * edit
  * update
  * destroy

* コレクションルーティング
  * index
  * create
 
に分類されます。newはどちらにも属しません。

---
### 単数リソース

resourceメソッドは単数リソース基本アクションを設定します。indexはありません。id属性の値がセッションオブジェクトから取得できる時などに使用します。

```ruby:routes.rb
  resource :account
```

コントローラー名は複数形(上記の例だとaccounts)になるので注意が必要です。

---
### 独自の設定を追加する

※ファイル名の「baukis2」は開発中のアプリケーションの名前です。

```ruby:config/initializers/baukis2.rb
Rails.application.configure do
  config.baukis2 = {
    staff: { host: "baukis2.example.com", path: "" },
    admin: { host: "baukis2.example.com", path: "admin" },
    customer: { host: "example.com", path: "mypage" }
  }
end
```

>configは、Rails::Application::Configurationクラスのインスタンスを返すメソッドです。
このオブジェクトには設定項目を自由に追加することができます。

Railsアプリケーションの中では次の式でこのハッシュにアクセスできます。

```ruby
Rails.application.config.baukis2
```

---
### 制約

制約を用いると、ホスト名やポート番号、リクエスト元のIPアドレスなどによってルーティングを変更することができます。

```ruby:routes.rb
config = Rails.application.config.baukis2

constraints host: config[:staff][:host] do
  namespace :staff, path: config[:staff][:path] do
    root "top#index"
  end
end
```

上記の例だとbaukis2.example.comというホスト名でアクセスされた場合にのみ、職員トップページが表示されます。

---
### ルーティングのテスト

#### route_to

```ruby
describe "ルーティング" do
  example "職員トップページ" do
    config = Rails.application.config.baukis2
    url = "http://#{config[:staff][:host]}/#{config[:staff][:path]}"
    expect(get: url).to route_to(
      host: config[:staff][:host],
      controller: "staff/top",
      action: "index"
    )
  end
end
```

route_toはルーティングテスト用のマッチャーです。引数にurlにアクセスした時のparamsオブジェクトにセットされるであろう内容を指定します。

#### be_routable

```ruby
  example "存在しないパスならroutableではない" do
    config = Rails.application.config.baukis2
    expect(get: http://#{config[:staff][:host]}/xyz).to_not be_routable
  end
```

be_routableは与えられたHTTPメソッドとURLの組み合わせが何らかのアクションと結び付けられているかどうかを調べます。

## 続き
続きの記事のURLも順次追加していきます。

[Ruby on Rails6 実践ガイド cp10~cp12 【メモ】](https://qiita.com/nizi24/items/e430cadb57a26a7cff76)
[Ruby on Rails6 実践ガイド cp13~cp15 【メモ】](https://qiita.com/nizi24/items/241e766e7c2afc98292b)
[Ruby on Rails6 実践ガイド cp16~cp18 【メモ】](https://qiita.com/nizi24/items/e83749826f8d70223311)
[Ruby on Rails6 実践ガイド[機能拡張編] cp3~cp6 【メモ】](https://qiita.com/nizi24/items/36da7b89d561c19af30f)
[Ruby on Rails6 実践ガイド[機能拡張編] cp7~cp9 【メモ】](https://qiita.com/nizi24/items/232e4f05f9d416e0a08c)
[Ruby on Rails6 実践ガイド [機能拡張編] cp10~cp12 【メモ】](https://qiita.com/nizi24/items/2ec4ed513fce22a89222)

## 引用元
※マークダウンの引用を用いている部分は以下の書籍から引用しています。
[Ruby on Rails6 実践ガイド](https://book.impress.co.jp/books/1118101134)