[Rails] Formオブジェクトに使えるmodule

## 概要
Formオブジェクトなどを実装する場合、`ActiveModel::Model`をincludeしたPOROクラスを実装することがあると思います。
この記事ではその際に、追加のmoduleを使ってできることをまとめてみました。

## 環境
Rails: 7.0.3

## ActiveModel::API
まずはこの`ActiveModel::API`をincludeすることから始まります。
これにより、以下のような機能が利用できます。
- `form_with`や`render`などのAction Viewのヘルパーメソッド
- 値のvalidation

### tips
- 値を上書きしたい場合、対象のattributeは`attr_reader`にしておき、`#{attr_name}=`のsetterを定義します。

```ruby:email_contact.rb
class EmailContact
  include ActiveModel::Model

  # 値を変更したい場合、ここはattr_readerだけにして、setterメソッドを定義する
  attr_accessor :email
  attr_reader :message

  validates :email, :message, presence: true

  # setter
  def message=(value)
    @message = value + 'ありがとうございます。'
  end

  def deliver
    if valid?
      # deliver email
    end
  end
end
```

```erb:new.html
<%= form_with model: @email_contact, local: true do |f| %>
  <%= f.label :email %>
  <%= f.text_field :email %>
  <%= f.label :message %>
  <%= f.text_area :message %>
  <%= f.submit %>
<% end %>
```
```ruby:email_contacts_controller.rb
class EmailContactsController < ApplicationController
  def create
    @email_contact = EmailContact.new(email_contact_params)
    # <@email="sample@example.com", @message="こんにちはありがとうございます。">
    @email_contact.deliver
  end

  def new
    @email_contact = EmailContact.new
  end

  def email_contact_params
    params.require(:email_contact).permit(:email, :message)
  end
end
```


## ActiveModel::Attributes
ActiveModel::Attributesを使うことで、以下のような機能が利用できます。
- 値のキャスト
- 値のデフォルト値設定

### tips
- キャストは自分自身でキャスト方法を定めたtypeを定義することが可能です。(以下の`Creating Custom Types`参照)
https://edgeapi.rubyonrails.org/classes/ActiveRecord/Attributes/ClassMethods.html#method-i-attribute

```ruby:email_contact.rb
class EmailContact
  include ActiveModel::Model
  include ActiveModel::Attributes

  # formに何も入れなかった場合、sample@example.comが入る
  attribute :email, :string, default: 'sample@example.com'
  # formからは文字列のparamsが渡ってくるが、これによりdate型に変換される
  attribute :send_date, :date
  ...
end
```

```erb:new.html
<%= form_with model: @email_contact, local: true do |f| %>
  <%= f.label :email %>
  <%= f.text_field :email %>
  ...
  <%= f.label :send_date %>
  <%= f.date_field :send_date %>
  <%= f.submit %>
<% end %>
```

以下のように、デフォルト値の設定と、キャストが行われます。
また、`attributes`などのメソッドも利用できるようになります。

```ruby:email_contacts_controller.rb
class EmailContactsController < ApplicationController
  def create
    @email_contact = EmailContact.new(email_contact_params)
    # @email.contact.attributes => {"email"=>"sample@example.com", "send_date"=>Sat, 01 Jan 2022}
    @email_contact.deliver
  end
  ...
end
```

## Enumerize
列挙型を扱う際に使いますが、こちらも組み込むことが可能です。
これにより、以下の機能などが利用可能です。
- 値のValidation
- Form側でのoptionsの使用
- i18nの設定

### tips
- enumerizeを定義したattributeは、以下で定義されるため`attributeメソッド`を使う必要はありません。
https://github.com/brainspec/enumerize/blob/master/lib/enumerize/attribute.rb#L90-L121


```ruby
class EmailContact
  include ActiveModel::Model
  include ActiveModel::Attributes
  extend Enumerize

  enumerize :type, in: %w(purchase notice register)
  attribute :email, :string
  validates :name, :email, :message, presence: true
  ...
end
```

以下のように`EmailContact.type.options`でオプション一覧を表示可能。
keyは`type.text`になるので、i18nを使用すれば、その値で表示される。
```erb:new.html
<%= form_with model: @email_contact, local: true do |f| %>
  <%= f.label :email %>
  <%= f.text_field :email %>
  ...
  <%= f.label :type %>
  <%= f.select :type, EmailContact.type.options %> 
  <%= f.submit %>
<% end %>
```

```ruby:email_contacts_controller.rb
class EmailContactsController < ApplicationController
  def create
    @email_contact = EmailContact.new(email_contact_params)
    # @email.contact.attributes => {"type"=>"purchase", "email"=>"sample@example.com"}
    @email_contact.deliver
  end
  ...
end
```