概要
原著者の許諾を得て翻訳・公開いたします。
- 英語記事: Sanitize your attributes through your form object
- 原文公開日: 2017/10/17
- 著者: Jean Anquetil
- サイト: drivy.engineering -- 自動車の所有という概念の変革を目指すスタートアップ企業です。
Form Objectパターンについては「肥大化したActiveRecordモデルをリファクタリングする7つの方法(翻訳)」もご覧ください。
Rails: Form ObjectとVirtusを使って属性をサニタイズする(翻訳)
私たちはDrivyのコードベースで、Form ObjectのビルドにVirtus gemを使っています。これによって以下のメリットを得られました。
- コントローラやビューにビジネスロジックを含めないようにする
- 永続しない属性を取り扱える
- 特定のバリデーションを、モデルに直接追加せずに追加できる
- カスタムのデータバリデーションをフォームに直接表示できる
include
することで機能をActiveModel::Model
から使える
ユーザー入力のサニタイズ、データの整形、スペースの除去といった作業が必要になったときに、Virtusを使って行える便利な方法をご紹介します。
#coerce
を使う
文字列として記録されているVAT番号からスペースをすべて除去したいとします。シンプルなユースケースですが、このコンセプトはもっと複雑な状況にも適用できます。
まず、サニタイズ対象となる属性のカスタム属性オブジェクトを定義する必要があります。そのオブジェクトで#coerce
メソッドを使うためにVirtus::Attribute
の継承も必要です。次に、実行したい整形処理をメソッドに定義します。
class SanitizedVatNumber < Virtus::Attribute
def coerce(value)
value.respond_to?(:to_s) ? value.to_s.gsub(/\s+/, '') : value
end
end
次はVirtusのForm Objectでvat_number
属性(これが更新対象です)をSanitizedVatNumber
として指定します。
class CompanyForm
attribute :vat_number, SanitizedVatNumber
def initialize(...)
end
end
以上でおしまいです。フォームが送信されるとvat_number
がサニタイズされます。
RSpecでテストする
カスタムのVirtus属性に基本的なテストを追加するのも簡単です。RSpecを使う場合は次のようにします。
describe SanitizedVatNumber do
let(:object) { described_class.build(described_class) }
subject { object.coerce(value) }
context 'vat_numberがnilの場合' do
let(:value) { nil }
it { is_expected.to eq('') }
end
context 'vat_numberがスペースを含む場合' do
let(:value) { 'EN XX 999 999 999' }
it { is_expected.to eq('ENXX999999999')}
end
end
まとめ
Form Objectの責務の増加(属性をフォーム内で直接サニタイズするリスクにつながる)を避けることができます。また、Virtusのカスタム#coerce
は複数のフォームで再利用でき、単体テストも非常に行いやすくなります。
本記事が気に入った方はぜひDrivyエンジニアリングチームの募集要項をご覧ください。