Validations

Role of Validations

Validation is used to validate the coherence of data in a context. It can be as simple as validating if a field is present or it can be more complex and use other data and logic.

Validation is tied to a context: A model can be valid at the creation and then invalid at update. Or it can be valid if an admin creates it and invalid if a regular user creates it.

Validation can also be triggered at the collection level. One model can't be created or updated if there is a conflict with existing data.

Location

  • app/predicates/

Usage

Validation can be used:

  • Inside the model itself (validation is tied to a model)
  • Inside a Service (when creating new models)
  • Inside a controller to display error messages to the user

Naming

The naming should be explicit regarding the model.

  • UserIsValid
  • AdminIsValid
  • OfferIsValidForCompany

Structure

  • All validations classes are Predicates and should inherit from Predicate::Base.
  • They are initialized with a single model in the initialize method.

Code

module Accounts
  # Is the user valid? Is it ok to be saved in the database?
  class UserIsValid < Predicate::Base

    def initialize(user)
      @user = user
    end

    validates_presence_of :first_name, :last_name
    validates_presence_of :email
    validates_format_of :email, with: User::EMAIL_FORMAT, allow_blank: true, if: :email_changed?
    validate :unique_key_is_unique, if: -> { @user.email_changed? || @user.school_id_changed? }

    # Password validations
    validates_presence_of :password, if: -> { @user.new_record? }
    validates_confirmation_of :password, unless: 'password.blank?'
    validate :password_is_secure
    validate :content_locales, presence: true

    # Mandatory fields for external users
    validates_presence_of(
      :phone_number,
      :address,
      :zip_code,
      :city,
      :country,
      :position,
      if: :require_contact_information?
    )

    validate :promotion_date_is_valid, if: :promotion_date_changed?

    private

    delegate :first_name, :last_name, :email, :password, :password_confirmation, :email_changed?,
      :school_id_changed?, :promotion_date_changed?, :phone_number, :address,
      :zip_code, :city, :country, :position, :content_locales, to: :@user

    def require_contact_information?
      @user.extern_user?
    end

    def password_is_secure
      return if password.nil?

      password_security = CheckPasswordSecurity.new(password, @user.role)
      errors.add(:password, password_security.error_message) unless password_security.valid?
    end

    def promotion_date_is_valid
      return if @user.promotion_date.blank?
      return if @user.promotion_date.to_s =~ /\A[1-9][0-9]{3}\Z/
      errors.add(:promotion_date, I18n.t('invalid_year_format'))
    end

    def unique_key_is_unique
      errors.add(:email, :is_already_taken) if User.other_user_with_same_unique_key_exists?(@user)
    end

  end
end

results matching ""

    No results matching ""