SimpleDelegator を使ってモデルに Warnings を追加してみる

1 min読了の目安(約1300字TECH技術記事

はじめに

先日、データの登録フォームで入力した値に対して警告メッセージを表示する、という実装が必要になりました。

  • 入力した内容が条件に合致していない場合は、警告メッセージを表示する

  • 入力した内容が条件に合致していない場合でも、データの登録処理は可能にする

    • バリデーションエラーとは分けて管理したい

実装方法はいろいろ考えられると思いますが、SimpleDelegator を使った方法を検討してみたので、メモとして。

前提

$ ruby -v
ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-darwin19]
$ rails -v
Rails 6.1.0

SimpleDelegator

class SimpleDelegator

「委譲」を行うためのクラスです。委譲先に指定されたオブジェクトへメソッドの実行を委譲します。

使い方

require 'delegate'

class Foo < SimpleDelegator
  def dance
    pp "🕺"
  end
end

class Bar
  def walk
    pp "🚶"
  end
end

foo = Foo.new(Bar.new)
foo.dance 
=> "🕺"
foo.walk
=> "🚶"

SimpleDelegator で Warning を実装する

例えば User オブジェクトの Warning を取得する場合の実装です。

class UserWarnings < SimpleDelegator
  include ActiveModel::Validations

  validate :validates_name

  NG_NAMES = %w[dummy ng_word]
  
  def initialize(record)
    super
    valid?
  end

  def warnings; errors; end

  def validates_name   
    errors.add(:base, '別の名前を設定してね') if NG_NAMES.include?(name)
  end
end
class User < ApplicationRecord
  def warnings
    @warnings ||= UserWarnings.new(self).warnings
  end
end
> user = User.new(name: 'dummy')
> user.warnings.full_messages
=> ["別の名前を設定してね"]

Warning の実体は ActiveModel::Errors なので、full_messages などのメソッドも使えます。