Exploring ActiveRecord::Base::normalizes in Rails 7.1

Arish  KhanArish Khan
2 min read

Occasionally, there’s a need to standardize data before it’s stored in a database. For instance, you might want to convert email addresses to lowercase, eliminate leading and trailing spaces, and more. With the introduction of Rails 7.1, the new ActiveRecord::Base::normalizes API comes into play. This feature empowers you to normalize attribute values to a uniform format before they’re persisted in the database. By doing so, you enhance the integrity and consistency of your data and simplify the process of querying records based on attribute values

Prior to Rails 7.1, attribute normalization could be achieved by utilizing the before_save callback.

class User < ApplicationRecord
  before_save :normalize_email

  private

  def normalize_email
    self.email = email.strip.downcase if email.present?
  end
end

Rails 7.1 onwards

class User < ActiveRecord::Base
  normalizes :email, with: -> email { email.downcase.strip }
end

By default, the normalization will not be applied to nil values. This behavior can be modified using the apply_to_nil option, which is set to false by default.

class User < ActiveRecord::Base
  normalizes :email, with: -> email { email&.downcase&.titleize || 'example@yourdomain.com' }, apply_to_nil: true
end

You can also utilize the normalizes method to apply normalization to multiple attributes simultaneously.

class User < ActiveRecord::Base
  normalizes :email, :first_name, :last_name, :title, with: -> attribute { attribute.strip }
end

How can you apply normalization to pre-existing records?

Normalization takes effect when an attribute is assigned or updated. This implies that if a record was saved before the normalization rule was established, the attribute won’t be normalized until it receives a new value. To address this, you can explicitly perform migration through Normalization#normalize_attribute.

class User < ActiveRecord::Base
  normalizes :email, with: ->(email) { email&.downcase&.strip }
end

User.find_each do |user|
  # user.email # => "\n\nexample@DOMAIN.COM\n"
  user.normalize_attribute(:email)
  # user.email # => "example@domain.com"
  user.save
end

Note: The normalization can be applied multiple times and is designed to be idempotent. This means that applying the normalization repeatedly will yield the same result as applying it just once.

Please review this pull request for more details.

2
Subscribe to my newsletter

Read articles from Arish Khan directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Arish  Khan
Arish Khan

Ruby On Rails/React.JS Developer