Rails: Using normalizes without copying code

Posted . Visible to the public.

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)
Maximilian Berger
Last edit
Maximilian Berger
License
Source code in this card is licensed under the MIT License.
Posted by Maximilian Berger to makandra dev (2024-11-15 14:14)