[Rails]ActiveModel::Serializerに引数を与えて任意のバリデーションチェックを行う
概要
ActiveModel::Serializer に引数を与えて任意のバリデーションチェックを行う方法を以下に示します。
基本的な ActiveModel::Serializer の使い方に関してはこの記事では触れません。
コード
以下のような User モデルを題材とします。
email, name はログインユーザー以外には見せたくないので、 masked_email, masked_name として空文字を返すようにしています。
app/models/user.rb
class class User < ActiveRecord::Base
def masked_email
''
end
def masked_name
''
end
end
CREATE SEQUENCE IF NOT EXISTS users_id_seq;
CREATE TABLE "users" (
"id" int8 NOT NULL DEFAULT nextval('users_id_seq'::regclass),
"email" varchar,
"name" varchar,
"profile_image_url" varchar,
"screen_name" varchar,
"created_at" timestamp NOT NULL,
"updated_at" timestamp NOT NULL,
PRIMARY KEY ("id")
);
Serializer は以下のように ActiveModel::Validations を include してバリデーションを定義しています。
引数は initialize でインスタンス変数にセットして、バリデーション invalid であれば、 raise_validation_error によって ActiveRecord::RecordInvalid を raise しています。
app/serializers/user_serializer.rb
class UserSerializer < ActiveModel::Serializer
include ActiveModel::Validations
attributes(
:id,
:email,
:name,
:profile_image_url,
:screen_name,
:created_at,
:updated_at
)
validates :is_mask, boolean: true
def initialize(object, options = {})
super
@options = options
@is_mask = options[:is_mask]
raise_validation_error if invalid?
end
def name
return object.masked_name if is_mask
object.name
end
def email
return object.masked_email if is_mask
object.email
end
private
attr_reader :is_mask
end
引数、 is_mask が渡されている場合、 email, name をマスクして返すようにしています。
バリデーションに関して、 boolean を判定するバリデーションは以下のように実装しました。
これにより validates :is_mask, boolean: true
で Boolean かどうか判定できるようになります。
nil を許容する場合は validates :is_mask, boolean: true, allow_nil: true
とします。
app/validators/boolean_validator.rb
class BooleanValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
return if [FalseClass, TrueClass].include?(value.class)
record.errors.add(attribute, ' はBoolean型で入力してください')
end
end
使い方
1件の場合
serialized_user = ActiveModel::SerializableResource.new(
User.first,
serializer: UserSerializer,
is_mask: true
)
複数件の場合
serialized_users = ActiveModelSerializers::SerializableResource.new(
User.limit(3),
each_serializer: UserSerializer,
is_mask: true
)
型をキャストするパターン
引数を受け取って、バリデーション前に型をキャストするなら、以下のように initialize で行います。
def initialize(object, options = {})
super
@options = options
@is_mask = ActiveRecord::Type::Boolean.new.cast(options[:is_mask])
raise_validation_error if invalid?
end
デフォルト引数をセットするパターン
引数が渡されなかった場合のデフォルト引数をセットするなら、以下のように initialize で行います。
def initialize(object, options = {})
super
@options = options
@is_mask = options[:is_mask].nil? ? true : options[:is_mask]
raise_validation_error if invalid?
end
まとめ
以上、 ActiveModel::Serializer に引数を与えて任意のバリデーションチェックを行う方法でした。
バリデーションチェックを行うことで型や値のチェックができてアプリケーションがより堅牢に・表現力豊かになりますし、 ActiveModel::Validations を用いるとバリデーションを見慣れたスタイルでスマートに記述することができるので、積極的に使っていきたいです。
Discussion