Ruby on Rails6 実践ガイド cp13~cp15 【メモ】

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

## 概要
この記事は[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)
[Ruby on Rails6 実践ガイド cp7~cp9 【メモ】](https://qiita.com/nizi24/items/60e7fb846a25830478b9)
[Ruby on Rails6 実践ガイド cp10~cp12 【メモ】](https://qiita.com/nizi24/items/e430cadb57a26a7cff76)

# Chapter 13 モデル間の関連付け

### リソースがネストされているかどうかで処理を切り替える

```ruby:routes.rb
resources :staff_members do
  resources :staff_evnets, only: [ :index ]
end
resources :staff_events, only: [ :index ]
```

上のようなルーティングが設定されている時、

```ruby:staff_events_controller.rb
def index
  if params[:staff_member_id]
    @staff_member = StaffMember.find(params[:staff_member_id])
    @events = @staff_member.events.order(occurred_at: :desc)
  else
    @events = StaffEvent.order(occurred_at: :desc)
  end
end
```
URLパスにstaff_member_idが含まれるかどうかで処理を切り替えることができます。特定の職員のイベントの場合とすべてのイベントの場合のどちらも同じアクションで処理することができます。

---
### render collection

```haml
= render partial: "event", collection: @events
```
テンプレート内でrenderメソッドのcollectionオプションに配列を指定して呼び出すと、配列の要素の数だけ部分テンプレートが繰り返し埋め込まれます。

詳しく調べて記事を書きました。
[Viewでの繰り返しをコレクションをレンダリングすることで実装する【Rails】](https://qiita.com/nizi24/items/c2d609c709bbafdfc5c9)

---
### N+1問題

クエリの回数を減らせるにもかかわらず、取得したいオブジェクトの個数に1を加えた回数のクエリが発行されてしまうことを<strong>N+1問題</strong>といいます。

#### includes

```ruby
@events = @events.includes(:member)
```
includesメソッドに対して関連付けの名前を与えると、関連付けられたモデルオブジェクトを一括して取得するクエリが発行されるようになります。
その結果、発行されるクエリの回数が減り、多くの場合でパフォーマンスの向上につながります。

# Chapter 14 値の正規化とバリデーション

### 値の正規化

#### 正規化のためのメソッド

```ruby:models/concern/string_normalizer.rb
require "nkf"

def normalize_as_text(text)
  NKF.nkf("-W -w -Z1", text).strip if text
end

def normalize_as_furigana(text)
  NKF.nkf("-W -w -Z1 --katakana", text).strip if text
end
```

NKFモジュールは日本語特有の変換機能を提供します。nkfメソッドは第一引数にフラグ文字列、第二引数に変換対象の文字列をとり、変換後の文字列を返します。

| フラグ    |意味|
|:----------|:---------------------------------|
| -W        |入力の文字コードがUTF-8であることを指定|
| -w        |UTF-8で出力する|
| -Z1       |全角の英数字、記号、全角スペースを半角に変換する|
| --katakana|ひらがなをカタカナに変換する|

[フラグ文字列一覧](https://ruby-doc.org/stdlib-2.1.1/libdoc/nkf/rdoc/NKF.html)

フラグ文字列は連結して書くことができます。

```ruby
NKF.nkf("-WwZ1", text)
```

stripメソッドは文字列の先頭と末尾にある空白文字列を除去します。

#### before_vallidation

モデルオブジェクトに対してバリデーション、保存、削除などの操作が行われる前後に実行される処理を<strong>コールバック</strong>または<strong>フック</strong>といいます。
before_validationはバリデーションの直前に実行されます。

```ruby
before_validation do
  self.family_name = normalize_as_name(family_name)
  self.family_name_kana = normalize_as_furigana(family_name_kana)
end
```
上の例では、バリデーションが実行される前に名前とフリガナに対して正規化を行っています。

---
### Gem data-validaor

data-validatorはdataタイプのバリデーションを提供します。

|  オプション        |チェックする内容|
|:------------------|:-------------------------------------------|
| after             |指定された日付より後。ただし、指定された日付は含まない。|
| before            |指定された日付よりも前。ただし、指定された日付は含まない。|
| after_or_equal_to |指定された日付よりも後。ただし、指定された日付を含む。|
| before_or_equal_to|指定された日付よりも前。ただし、指定された日付を含む。|
| allow_blank       |trueをしてすれば空欄が許可される|

```ruby
before: -> (obj) { 1.year.from_now.to_date }
```
dateタイプのバリデーションでは、Procオブジェクトを指定すると動的に基準となる日付を指定することができます。

```ruby
before: 1.year.from_now.to_date
```

上のように書いても動きますが、beforeキーの値が起動時を基準にして固定されてしまうので、予想外の結果を引き起こします。

---
### validate

```ruby
validate do
  unless Staff::Authenticator.new(object).authenticate(current_password)
    errors.add(:current_password, :wrong)
  end
end
``` 

validateは、組み込みバリデーション以外の方法でバリデーションを実装するときに利用します。

# Chapter 15 プレゼンター

### プレゼンターとは

あるオブジェクトに関連するHTMLコードを生成する役割を担うクラスを<strong>プレゼンター</strong>と呼びます。
Railsの公式用語ではなく、Railsコミュニティの中で生まれた概念です。

---
### モデルプレゼンター

対象がモデルオブジェクトの場合のプレゼンターです。

```ruby:app/presenters/model_presenter.rb
class ModelPresenter
  attr_reader :object, :view_context

  def initialize(object, view_context)
    @object = object
    @view_context = view_context
  end
end
```

ModelPresenterクラスを継承したStaffMemberPresenterクラスを定義します。

```ruby:staff_member_presenter.rb
class StaffMemberPresenter < ModelPresenter

  # 職員の停止フラグのon/offを表現する記号を返す
  def suspended_mark
    object.suspended? ?
      view_context.raw("&#x2611;") :
      view_context.raw("&#x2610;")
  end
end
```

<strong>使用例</strong>

```ruby:staff_members/index.html.haml
- @staff_members.each do |m|
  - p = StaffMemberPresenter.new(m, self)
  - p.suspended
```

StaffMemberPresenterのインスタンスを作成しています。第一引数にStaffMemberオブジェクト、第二引数にselfを指定しています。
ビューテンプレート内でselfによって参照されるオブジェクトを<strong>ビューコンテキスト</strong>と呼びます。このオブジェクトはRailsで定義されたすべてのへルパーメソッドを自分のメソッドとしてもっています。

---
### 委譲

委譲を用いるとコードを簡潔に書くことができます。

```ruby:model_presenter.rb
  delegate :raw, to: :view_context
```

```ruby:staff_member_presenter.rb
  delegate :suspended?, to: :object
```

delegateメソッドは引数に指定された名前のインスタンスメソッドを定義し、toオプションに指定されたメソッドが返すオブジェクトに委ねます。

要するにメソッドを呼び出すときにレシーバーを省略できるようになります。
delegateを定義した結果、下のように簡潔に書くことができるようになりました。

```ruby:staff_member_presenter.rb
  def suspended_mark
    suspended? ? raw("&#x2611;") : raw("&#x2610;")
  end
```

---

HtmlBuilderモジュールとフォームプレゼンターについては省略します。

---

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

[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)