Sanitize your attributes through your form object

October 17, 2017 | Jean Anquetil | 2-minute read

At Drivy, we use the Virtus gem to build form objects in our codebase. This lets us:

  • Keep our business logic out of the Controller and Views
  • Deal with unpersisted attributes
  • Add specific validations instead of adding them directly in the model
  • Display custom data validations errors directly in the form
  • Use features from ActiveModel::Model by including it

Sometimes, we have to sanitize user input: format the data, remove whitespaces and so on. Here is a convenient way to handle it with Virtus.

Using #coerce

Let’s imagine that we want to remove all the whitespaces from a VAT number recorded as a string. This is a pretty simple use case, but concepts will apply to more complex situation as well.

User input

First we have to define a custom attribute object for the attribute we want to sanitize. It has to inherit from Virtus::Attribute in order to use the coerce method. Then, in this method, we just have to define the reformatting we want to perform.

class SanitizedVatNumber < Virtus::Attribute
  def coerce(value)
    value.respond_to?(:to_s) ? value.to_s.gsub(/\s+/, '') : value
  end
end

Next, in your Virtus form object, we specify the vat_number attribute - the one we want to update - as a SanitizedVatNumber:

class CompanyForm
  attribute :vat_number, SanitizedVatNumber

  def initialize(...)

  end
end

And there we have it! The vat_number will be sanitized once the form is submitted.

Testing it with Rspec

It is also easy to add basic tests on this custom Virtus attribute, for instance by using Rspec:

describe SanitizedVatNumber do
  let(:object) { described_class.build(described_class) }
  subject { object.coerce(value) }


  context 'when vat_number is nil' do
    let(:value)  { nil }

    it { is_expected.to eq('') }
  end

  context 'when vat_number has white spaces' do
    let(:value)  { 'EN XX 999 999 999' }

    it { is_expected.to eq('ENXX999999999')}
  end
end

Conclusion

You avoid giving too much responsibility to your form object, which would be the risk of sanitizing attributes directly inside the form. Plus, Virtus custom coercion can be reused across multiple forms, and lends itself well to be easily unit tested.

View openings 👍  Like this post? Join Drivy's engineering team!