# frozen_string_literal: true require 'human_enum/version' require 'active_support' require 'active_support/concern' require 'active_support/core_ext/array' require 'active_support/core_ext/module/deprecation' require 'active_model/naming' require 'active_model/translation' require 'active_record/type' require 'active_record/enum' # # About Human Enum # Human Enum allows you to easily translate `enum` values in [ActiveRecord] # models # [ActiveRecord]: https://api.rubyonrails.org/v5.2.3/classes/ActiveRecord/Enum.html module HumanEnum class Error < StandardError; end extend ActiveSupport::Concern class << self def deprecator = @deprecator ||= ActiveSupport::Deprecation.new('2.0', 'HumanEnum') end included do # DEPRECATED: This method is deprecated and will be removed in future versions. # # Returns the human-readable value of the specified enum. # # @param enum_name [Symbol] The name of the enum. # @return [String] The human-readable value of the enum. # @deprecated Use `human_` instead. # :nocov: def human_enum_value(enum_name, enum_value) _translate_enum(enum_name, enum_value) end # :nocov: deprecate(human_enum_value: 'use `human_` instead', deprecator: HumanEnum.deprecator) private def _translate_enum(enum_name, enum_value = send(enum_name)) self.class.send(:_translate_enum, enum_name, enum_value) end end class_methods do # rubocop:disable Metrics/BlockLength def enum(...) super(...).tap { humanize_enums } end # DEPRECATED: This method is deprecated and will be removed in future versions. # # Translates the given enum value into a human-readable string. # # @param enum_name [Symbol] The name of the enum. # @param enum_value [Symbol] The value of the enum. # @return [String] The human-readable string representation of the enum value. # @deprecated Use `human_` instead. # :nocov: def human_enum_value(enum_name, enum_value) _translate_enum(enum_name, enum_value) end # :nocov: deprecate(human_enum_value: 'use `human_` instead', deprecator: HumanEnum.deprecator) # Goes through all enums defined in the model and creates the necessary methods, if they don't exist yet. def humanize_enums defined_enums.each do |enum_name, values| _define_instance_method(enum_name) _define_class_method(enum_name, values) end end private def _translate_enum(enum_name, enum_value) attributes_scope = "#{i18n_scope}.attributes" enum_key = [enum_name.to_s.pluralize, enum_value].compact.join('.') defaults = lookup_ancestors.map do |klass| :"#{attributes_scope}.#{klass.model_name.i18n_key}.#{enum_key}" end defaults << :"attributes.#{enum_key}" defaults << enum_value.to_s.humanize I18n.t defaults.shift, default: defaults end def _define_instance_method(enum_name) return if method_defined?(:"human_#{enum_name}") define_method(:"human_#{enum_name}") do _translate_enum(enum_name, send(enum_name)) end end def _define_class_method(enum_name, values) collection_name = enum_name.to_s.pluralize return if respond_to?(:"human_#{collection_name}") self.class.send :define_method, "human_#{collection_name}" do values.to_h { |key, _| [key.to_sym, _translate_enum(enum_name, key)] } end end end end