Rails6.1ハンズオン(5)～6.1の機能を触る

# はじめに

Rails6.1ハンズオン(4)の続きです。

ようやくここまできました。今回が最終回です。
コードは[Github]([https://github.com/hirorocky/Rails6.1_hands_on](https://github.com/hirorocky/Rails6.1_hands_on))にあげています。章ごとにコミットしてますので、参考にしていただければ幸いです。

# やること

個人的に気になった（面白そうor仕事で使いそう）機能をピックアップして、現在のRails_6.1_hands_onプロジェクトに追加してみます。

- Delegated Types
- Strict Loading
- ActiveModel::Errorオブジェクト

参考：

[Ruby on Rails 6.1の主要な新機能・変更点 - Qiita](https://qiita.com/ryohashimoto/items/7caf905389ccb5d7d8fb)

## 6-1. Delegated Types

### ※単一テーブル継承(STI)とは？

予約語「type(String)」カラムを用いて、単一のテーブルに複数のモデルを定義する

例えば今回の掲示板のCommentモデルについて、

Commentの種類をこれまで実装した「テキスト形式」と新たに追加する「emoji形式」（LINEのスタンプみたいな感じ）に分けるとしたら、以下のようなレコードになる。（具体的な実装は省略）

|author|type|text|emoji|
|:----|:----|:----|:----|
|taro|text|（ActionTextへの参照）|NULL|
|jiro|emoji|NULL|😎|


STIだと一つのテーブルで似たようなモデルを管理できるので、実装が楽？になるし、テーブルが無尽蔵に増えなくて管理も楽そう。（仕事で使ったことないのでなんともですが...）

しかし、ものによってはNULLの穴ぼこだらけのテーブルになって、たくさんのレコードを扱うようなシステムだとパフォーマンスに影響が出そう。。。

そこで実装されたのが今回の「Delegated Types」だと解釈しました！

STIのメリットである、代表のテーブルを用いたカラムの共通化をしつつ、モデル毎の微妙な違いは別途テーブルに分けて管理する形になります。（NULLの穴ぼこは無くなります！）

上の例だと、
authorカラムは共通なので、代表としてCommentテーブルの中にauthorカラムを残し、
「違い」である、textカラムとemojiカラムはそれぞれTextテーブルとEmojiテーブルに分けて管理します。

Delegate Typeに則った最終的なテーブル図はこうなります

＜図＞
![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70400/037a01ef-04dd-d3fd-8b7f-84b30f615862.png)



### 6-1-1. モデル作成

すごく適当なマイグレーションファイルです。。。

```ruby
def change
    create_table :texts do |t|
      t.timestamps
    end

    create_table :emojis do |t|
      t.integer :emoji_type, null: false, default: 0
    end

    add_column :comments, :content_type, :string, null: false
    add_column :comments, :content_id, :bigint, null: false
end
```

```ruby
class Comment < ApplicationRecord
  belongs_to :community
  delegated_type :content, types: %w[Text Emoji]
end

class Text < ApplicationRecord
  has_one :comment, as: :content, touch: true
  has_rich_text :text_content
  delegate :author_name, to: :comment
end

class Emoji < ApplicationRecord
  has_one :comment, as: :content, touch: true
  enum emoji_type: %i[😀 😂 😎]
  delegate :author_name, to: :comment
end
```

### 6-1-2. コントローラー・ビュー

一部を略して書きます、詳しくはgithubをご覧ください。

app/controllers/text_comments_controller.rb

```ruby
class TextCommentsController < ApplicationController
  before_action :set_community
  
  def new
    @text = Text.new.build_comment
  end

  def create
    # FIXME: バリデーションエラーを無視している
    @community.comments
              .create(content: Text.new(text_content: text_params[:text_content]),
                      author_name: text_params[:author_name])
    redirect_to community_path(@community)
  end

  private

  def set_community
    @community = Community.find(params[:community_id])
  end

  def text_params
    params.require(:comment).permit(:text_content, :author_name)
  end
end
```

ポイントは親のCommentにcontentという属性で子供のTextやEmojiを渡しているところだと思います。

![Untitled 1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70400/8afdabd4-6e99-a8f2-3973-d976a3f6c58d.png)
action_textとただのenumを使い分けられるようになりました。

## 6-2. Strict Loading

```ruby
class Community < ApplicationRecord
  has_many :comments, strict_loading: true
end
```

こんな風につけておくだけで、N+1問題が発生しているときに例外を出すようになります。例外になるので、bulletより強制力がありますね。。。（今回は意味なかったです）

## 6-3. ActiveModel::Errorオブジェクト

バリデーションエラーが起きたときにモデルのインスタンスに付与されていたエラー情報がただのハッシュではなく、クラスのインスタンスになりました。

app/views/shared/_error_messages.html.haml

```haml
- if object.errors.present?
  .card.mb-2.border-danger
    .card-header.bg-danger.text-white
      エラーがあります
    .card-body
      %ul
        - object.errors.each do |e|
          %li= e.full_message
```

エラー文の呼び出し方が変わって、whereとかも使えるようになって、どこかで使えそうですね。。。

# さいごに

このシリーズはこれで以上ですが、今回触らなかったもの（特に並列データベースとかは、大規模な開発で使いそう）もいつか触れたいと思いました。（小並感）