At Drivy, we use the Virtus gem to build form objects in our codebase. This lets us:
ActiveModel::Model
by including itSometimes, we have to sanitize user input: format the data, remove whitespaces and so on. Here is a convenient way to handle it with Virtus.
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.
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.
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
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.