Rails 7.1 added the normalizes
method which can be used to normalize user input.
It let's you define the fields you want to normalize and how to normalize them.
class Movie < ApplicationRecord
normalizes :title, with: -> { _1.strip }
end
If you wanted to apply the same normalization to a field in another model you could just repeat yourself and copy the same lambda.
class Actor < ApplicationRecord
normalizes :name, with: -> { _1.strip }
end
If however you would prefer to reuse code then one option is to replace the lambda with a class. Which could look like this:
class Movie < ApplicationRecord
normalizes :title, with: Normalizers::StripNormalizer
end
The with
keyword accepts any callable object that takes the attribute’s value as its only argument. To comply with this requirement we can create a class defining a class method call
that accepts one argument.
module Normalizers
class StripNormalizer
def self.call(value)
value.to_s.strip
end
end
end
You can also define class methods to share common normalizers between multiple classes:
# e.g. application_record.rb
def self.strips(*names)
normalizes(*names, with: Normalizers::StripNormalizer)
end
Enforcing consistency
In order to enforce a consistent interface we could also define a base class for all normalizers.
module Normalizers
class BaseNormalizer
def self.call(value)
raise AbstractMethodError
end
end
end
Folder structure
If you would like your normalizers to exist in a folder normalizers
at the same level such as validators
or models
(app/normalizers
) you can use the following initializer.
# config/initializers/normalizers.rb
module Normalizers; end
Rails.autoloaders.main.push_dir("#{Rails.root}/app/normalizers", namespace: Normalizers)