[Rails]enum
はじめに
enumをよく使いますが、enumに対しての理解度がまだ低いと感じたので復習を兼ねてまとめてみました。
tl;dr
1. enum
2. データ型
3. 定義
4. オプション
5. Rails7のenum構文
6. 日本語対応
7. enumと非同期処理
enumとは
enumはenumerated type
の略称で、列挙型のテータタイプを持ちます。
列挙型とは、プログラミング言語やデータベース管理システムなどにおけるデータ型の一つで、複数の異なる定数を一つの集合として定義するもの。 多くの言語では
enum
の略号で示される。
Railsのenumは、特定の属性に対して許容される値のリストを定義するための便利な方法です。
値に対して名前が付けられるため、コードの可読性が向上します。
enumのソースコード:
定義
integer
のデータ型を定義します。
これで、整数型(integer)のカラム(status)を持つProjectモデルを定義できました。
rails g Project status:integer
default: 0
:初期値を0
にします。
null:false
:レコードにnullの値を保存されることを許可しない
t.integer "status", default: 0, null: false
statusのenumは5つの異なる状態を持ちます。
それぞれの状態は0、1、2、3、4の整数値にマップされます。
class Project < ApplicationRecord
enum status: { planned: 0, started: 1, completed: 2, archived: 3, other: 4 }
end
デフォルト値を設定することができます。
class Project < ApplicationRecord
enum status: { planned: 0, started: 1, completed: 2, archived: 3, other: 4 }, _default: 0
end
オプション
_prefix:
enum
を定義する際に、各enum値のプレフィックスを自動的に追加するオプションです。status
がcompleted
の場合、status_completed
といった形式でメソッドが生成されます。
class Project < ApplicationRecord
enum status: { planned: 0, started: 1, completed: 2, archived: 3, other: 4 }, _prefix: true
end
irb(main):010:0> Project.status_completed
Project Load (0.3ms) SELECT "projects".* FROM "projects" WHERE "projects"."status" = $1 [["status", 2]]
=>
_suffix:
enum
を定義する際に、各enum値のサフィックスを自動的に追加するオプションです。status
がplanning
の場合、planned_status
といった形式でメソッドが生成されます。
class Article < ApplicationRecord
enum status: { planned: 0, started: 1, completed: 2, archived: 3, other: 4 }, _suffix: true
end
irb(main):011:0> Project.planned_status
Project Load (0.2ms) SELECT "projects".* FROM "projects" WHERE "projects"."status" = $1 [["status", 0]]
=>
scope:
enumを使用すると、enumの値ごとに自動的にスコープが生成されます。スコープを明示的に定義する必要はありません。enumの値は、そのままスコープとして利用できます。
# BAD
scope :completed, -> { where(status: 2) }
# GOOD
Project.completed
_scopes: false
オプションを入れてスコープを作成させないこともできます。
class Project < ApplicationRecord
enum status: { planned: 0, started: 1, completed: 2, archived: 3, other: 4 }, _scopes: false
end
enumの定義によって使用できるカスタムメソッド
scope以外、こちらのカスタムメソッドも利用可能になります。
class Conversation < ActiveRecord::Base
enum :status, [ :active, :archived ]
end
# 更新
# conversation.update! status: 0
conversation.active!
conversation.active? # => true
conversation.status # => "active"
# conversation.update! status: 1
conversation.archived!
conversation.archived? # => true
conversation.status # => "archived"
# conversation.status = 1
conversation.status = "archived"
# nil
conversation.status = nil
conversation.status.nil? # => true
conversation.status # => nil
# not_ メソッド
Conversation.active
Conversation.not_active
Conversation.archived
Conversation.not_archived
# カラム検索
Conversation.where(status: [:active, :archived])
Conversation.where.not(status: :active)
Rails7のenum構文
オプションのアンダースコアをなしに変更されました。
従来の構文 | rails7以後 |
---|---|
_default |
default |
_prefix |
prefix |
_suffix |
suffix |
_scopes |
scopes |
日本語対応
enum の日本語対応を行う前のstatusハッシュです。
irb(main):005:0> Projec.statuses
=> {"planned"=>0, "started"=>1, "completed"=>2, "archived"=>3, "other"=>4}
irb(main):006:0> Project.statuses.keys
=> ["planned", "started", "completed", "archived", "other"]
irb(main):00:0> Project.statuses.values
=> [0, 1, 2, 3, 4]
フロント側では日本語を表示させたいので日本語の訳文を追加します。
ja:
enums:
project:
status:
planned: 未着手
started: 着手中
completed: 完了
archived: 保留
other: その他
日本語対応の設定を追加する
railsアプリのデフォルトの言語を日本語にします。
また、翻訳ファイルの読み込みの記述を追加します。
class Application < Rails::Application
# ymlファイルを読み込む
config.i18n.load_path += Dir[Rails.root.join("config/locales/**/*.{rb,yml}").to_s]
config.i18n.default_locale = :ja
end
map
メソッドを使ってstatusの配列を取得します。
I18n
ヘルパメソッドを使うと対応した日本語を取得することができます。
irb(main):002:0> Project.statuses.keys.map{|status|[I18n.t("enums.project.status.#{status}"), Project.statuses[status]]}
=> [["未着手", 0], ["着手中", 1], ["完了", 2], ["保留", 3], ["その他", 4]]
irb(main):002:0> Project.statuses.keys.map { |status| [status, I18n.t("enums.project.status.#{status}"
)] }
=> [["planned", "未着手"], ["started", "着手中"], ["completed", "完了"], ["archived", "保留"], ["other", "その他"]]
セレクトボックスの形で選択できるようにします。
irb(main):004:0> p = Project.last
irb(main):005:0> p.status
=> "started"
irb(main):006:0> I18n.t("enums.project.status.#{p.status}")
=> "着手中"
irb(main):007:0> p.status_before_type_cast
=> 1
DBに保存されている整数値を取得するにはbefore_type_cast
メソッドがあります。
enumと非同期処理
最後ではenumを非同期に更新する方法を見ていきます。
セレクトボックスを使用して非同期にプロジェクトの状態を変更できるようにしていきます。
<%= form_with model: project do |form|%>
<%= form.select :status, Project.statuses.keys.map { |status|
[I18n.t("enums.project.status.#{status}"), status] },
onhange:"this.form.requestSubmit()" %>
<% end %>
onchange
は、フォーム内の要素の内容が変更された時に発火させるイベントハンドラーです。
this.form.requestSubmit()
は、フォーム内の現在のデータを使用してフォームを送信するために使用されます。
this.form
は、this
要素が所属しているフォーム要素を参照します。
requestSubmit()
メソッドは、フォームを送信するためのメソッドです。通常、フォームを送信するには、フォーム内のボタンをクリックするか、JavaScriptでこのメソッドを呼び出します。このメソッドを使用すると、フォームが通常の方法で送信されるのと同じように、フォームのデータがサーバーに送信されます。
turboでの非同期処理を追加する
フォームの送信後に非同期処理をさせたいのでupdate.turbo_stream.erb
を作成し、フラッシュメッセージを表示させます。
<%= turbo_stream.after "project_phase" do %>
<span id="message">更新しました!</span>
<% end %>
<%= turbo_stream_action_tag :remove_later, targer: "message", after: "2000" %>
turboのカスタムアクションを作成します。application.js
に読み込みます。
import { StreamActions } from "@hotwired/turbo-rails"
StreamActions.remove_later = function() {
setTimeout(()=>{
this.targetElements.forEach((element) => element.remove())
}, this.getAttribute("after"))
}
終わりに
enumを定義することで、国際化に簡単に対応でき、スコープやカスタムメソッドを使えるようになるところ便利だと感じました。しっかり理解した上で使っていきたいです。
Discussion