fnando/normalize_attributes

View on GitHub
lib/normalize_attributes/callbacks.rb

Summary

Maintainability
A
0 mins
Test Coverage
# frozen_string_literal: true

module NormalizeAttributes
  def self.retrieve_value(record, attribute, raw)
    before_type_cast_method = "#{attribute}_before_type_cast"

    if raw && record.respond_to?(before_type_cast_method)
      record.send(before_type_cast_method)
    else
      record.send(attribute)
    end
  end

  def self.retrieve_normalizers(normalizers, value)
    return normalizers unless normalizers.empty?

    case value
    when String
      [:squish]
    when Array
      [:compact]
    else
      []
    end
  end

  def self.apply_normalizers(record, attribute, normalizers, options)
    value = NormalizeAttributes.retrieve_value(record, attribute, options[:raw])
    normalizers = NormalizeAttributes.retrieve_normalizers(normalizers, value)

    normalizers.each do |normalizer|
      if normalizer.respond_to?(:call)
        value = normalizer.call(value)
      elsif value.respond_to?(normalizer)
        value = value.send(normalizer)
      elsif record.respond_to?(normalizer, true)
        value = record.send(normalizer, value)
      else
        raise "Unknown normalizer #{normalizer.inspect}"
      end
    end

    record.public_send("#{attribute}=", value)
  end

  module Callbacks
    def self.included(base)
      base.instance_eval do
        extend ClassMethods
        include InstanceMethods
        before_validation :normalize_attributes

        class << self
          attr_accessor :normalize_options
        end
      end
    end

    module ClassMethods
      def normalize(*args, &block)
        self.normalize_options ||= {}
        options = args.extract_options!

        args.each do |attr_name|
          attr_name = attr_name.to_sym

          normalize_options.tap do |o|
            o[attr_name] ||= []
            o[attr_name] << [
              [block, options[:with]].flatten.compact,
              options.except(:with)
            ]
          end
        end
      end

      alias normalize_attributes normalize
      alias normalize_attribute normalize
      alias normalize_attr normalize
      alias normalize_attrs normalize
    end

    module InstanceMethods
      private def normalize_attributes
        return unless self.class.normalize_options

        self.class.normalize_options.each do |attribute, items|
          items.each do |item|
            NormalizeAttributes.apply_normalizers(self, attribute, *item.dup)
          end
        end
      end
    end
  end
end