lib/exnum/active_record/exnum.rb

Summary

Maintainability
A
0 mins
Test Coverage
module ActiveRecord
  module Exnum

    def self.prepended(base)
      base.singleton_class.class_eval { self.prepend(ClassMethods) }
    end

    module ClassMethods
      def extended(base)
        base.class_attribute(:defined_params, instance_writer: false)
        base.defined_params = {}
        super(base)
      end
    end

    def inherited(base)
      base.defined_params = defined_params.deep_dup
      super
    end

    #--------------------------------------------------------------------------

    def exnum(definitions)
      enum_definitions = definitions.each_with_object({}) do |(name, values), ret|
        ret[name] = extract_enums(values)
      end
      enum(**enum_definitions)

      pram_definitions = definitions.each_with_object({}) do |(name, values), ret|
        next if %i[_prefix _suffix].include?(name)

        ret[name] = extract_params(values)
        self.send(name.to_s.pluralize).each do |k, v|
          ret[name][k.to_sym] ||= {}
          ret[name][k.to_sym][:val] = v
        end
      end

      enum_i18n(pram_definitions)
      enum_param(pram_definitions)
      enum_for_select(pram_definitions)
    end

    #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    private
    def extract_enums(values)
      return values unless values.kind_of?(Hash)

      values.each_with_object({}) do |(field, value), ret|
        ret[field.to_sym] = (value.kind_of?(Hash) ? value[:val] : value)
      end
    end

    def extract_params(values)
      return {} unless values.kind_of?(Hash)

      target_values = values.select{|_field, value| value.kind_of?(Hash)}
      target_values.each_with_object({}) do |(field, value), ret|
        ret[field.to_sym] = value
      end
    end

    #--------------------------------------------------------------------------

    def enum_i18n(definitions)
      definitions.each do |name, values|
        enum_i18n_class_method(name, values)
        enum_i18n_instance_method(name)
      end
    end

    # define class method which returns i18n string of each satus
    def enum_i18n_class_method(name, values)
      klass = self
      method_name = "#{name.to_s.pluralize}_i18n"
      detect_enum_conflict!(name, method_name, true)
      klass.singleton_class.send(:define_method, method_name) do |&block|
        i18n_hash = ActiveSupport::HashWithIndifferentAccess.new
        values.each_with_object(i18n_hash) do |(enum_name, value), ret|
          next if block.present? && !block.call(value)
          ret[enum_name] = i18n_string(klass, name, enum_name)
        end
      end
    end

    # define instance method which returns current i18n string of the instance
    def enum_i18n_instance_method(name)
      klass = self
      method_name = "#{name}_i18n"
      detect_enum_conflict!(name, method_name, false)
      klass.send(:define_method, method_name) do
        status = self.send(name)
        status.nil? ? nil : klass.send(:i18n_string, klass, name, status)
      end
    end

    def i18n_string(klass, name, enum_name)
      I18n.t("#{klass.i18n_scope}.enum.#{klass.model_name.i18n_key}.#{name}.#{enum_name}")
    end

    #--------------------------------------------------------------------------

    def enum_param(definitions)
      klass = self
      klass.defined_params.merge!(definitions)

      definitions.each do |name, values|
        param_names = values.values.map(&:keys).flatten.uniq
        param_names.each do |param_name|
          enum_param_class_method(name, values, param_name)
          enum_param_instance_method(name, values, param_name)
        end
      end
    end

    # define class methods which returns the parameter's values of each status
    def enum_param_class_method(name, values, param_name)
      klass = self
      method_name = "#{name}_#{param_name.to_s.pluralize}"
      detect_enum_conflict!(name, method_name, true)
      klass.singleton_class.send(:define_method, method_name) do |&block|
        param_hash = ActiveSupport::HashWithIndifferentAccess.new
        values.each_with_object(param_hash) do |(enum_name, params), ret|
          next if block.present? && !block.call(params)
          ret[enum_name] = params[param_name]
        end
      end
    end

    # define instance methods which returns the parameter's current value of the instance
    def enum_param_instance_method(name, values, param_name)
      method_name = "#{name}_#{param_name}"
      detect_enum_conflict!(name, method_name, false)
      define_method(method_name) do
        status = self.send(name)
        return if status.nil?
        defined_params[name][status.to_sym][param_name]
      end
    end

    #--------------------------------------------------------------------------

    def enum_for_select(definitions)
      definitions.each do |name, values|
        param_names = values.values.map(&:keys).flatten.uniq
        param_names.each do |param_name|
          enum_for_select_class_method(name, values, param_name)
        end
      end
    end

    # define class methods which returns the array for select box
    def enum_for_select_class_method(name, values, param_name)
      klass = self
      method_name = "#{name.to_s.pluralize}_for_select"
      detect_enum_conflict!(name, method_name, true)
      klass.singleton_class.send(:define_method, method_name) do |&block|
        i18n_hash = klass.send("#{name.to_s.pluralize}_i18n", &block)
        i18n_hash.to_a.map(&:reverse)
      end
    end

  end
end