wbotelhos/normalizy

View on GitHub
lib/normalizy/extensions.rb

Summary

Maintainability
A
3 hrs
Test Coverage
# frozen_string_literal: true

module Normalizy
  module Extension
    extend ActiveSupport::Concern

    included do
      private

      def extract_filter(rule, rule_options, attribute, filters: Normalizy.config.filters)
        options = rule_options.merge(attribute: attribute, object: self)

        return [filters[rule] || rule, options] unless rule.is_a?(Hash)

        filter  = filters[rule.keys.first] || rule
        options = (rule.values.first || {}).merge(options)

        [filter, options]
      end

      def extract_rule(rule)
        if rule.is_a?(Hash)
          [rule.keys.first, rule.values.first]
        else
          [rule, {}]
        end
      end

      def extract_value(value, filter, options, block)
        if filter.respond_to?(:call)
          if filter.method(:call).arity == -2
            filter.call value, options, &block
          else
            filter.call value, &block
          end
        elsif respond_to?(filter)
          if method(filter).arity == -2
            send filter, value, options, &block
          else
            send filter, value, &block
          end
        elsif value.respond_to?(filter)
          value.send filter, &block
        else
          value
        end
      end

      def normalizy!(attribute:, block:, options:, rules:, value:)
        rules ||= Normalizy.config.default_filters

        return if rules.blank? && block.blank?

        result = value

        [rules].flatten.compact.each do |rule|
          rule_name, rule_options = extract_rule(rule)

          unalias_for(rule_name).each do |unaliased_rule|
            filter, filter_options = extract_filter(unaliased_rule, rule_options, attribute)
            result                 = extract_value(result, filter, filter_options, block)
          end
        end

        result
      end

      def unalias_for(rule, aliases: Normalizy.config.normalizy_aliases)
        [aliases.key?(rule) ? aliases[rule] : rule].flatten.compact
      end
    end

    module ClassMethods
      attr_accessor :normalizy_rules

      def normalizy(*args, &block)
        options = args.extract_options!
        rules   = options[:with]

        self.normalizy_rules ||= {}

        args.each do |field|
          normalizy_rules[field] ||= []
          normalizy_rules[field] << { block: block, options: options.except(:with), rules: rules }
        end

        prepend Module.new {
          args.each do |attribute|
            define_method :"#{attribute}=" do |value|
              result = normalizy!(
                attribute: attribute,
                block:     block,
                options:   options.except(:with),
                rules:     rules,
                value:     value
              )

              if rules.is_a?(Hash) && rules.dig(:slug, :to).present?
                write_attribute rules.dig(:slug, :to), result

                super value
              else
                super result
              end
            end
          end
        }
      end
    end
  end
end