Rails: Form ObjectとVirtusを使って属性をサニタイズする(翻訳)

概要 原著者の許諾を得て翻訳・公開いたします。 英語記事: 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エンジニアリングチームの募集要項をご覧ください。 関連記事 肥大化したActiveRecordモデルをリファクタリングする7つの方法(翻訳) Railsで重要なパターンpart 1: Service Object(翻訳) Railsで重要なパターンpart 2: Query Object(翻訳)