joel/enum_for

View on GitHub
lib/enum_for.rb

Summary

Maintainability
A
1 hr
Test Coverage
require 'enum_for/version'

require 'active_support/hash_with_indifferent_access'
require 'active_support/core_ext/string'
require 'active_support/core_ext/hash'
require 'active_support/inflector/inflections'

# Public: Module that extends class in order to define enum methods using
# `enum_for` class method.
module EnumFor
  class Error < StandardError; end

  # Public: Defines few methods useful for handling integer attributes which
  # have to be used by enums. There is ActiveRecord enum type, but it shouldn't
  # be exposed in API as it doesn't handle validations well (it raises exception
  # when wrong enum is sent, while we would like to validate enum)
  #
  # This method doesn't prevent enum names to be reserved ruby words so please
  # use with caution.
  #
  # Methods generated by `enum_for` defined as:
  #
  # enum_for color: { red: 0, green: 1 }
  #
  # When enum is set to 1:
  #
  # object.color
  # => 'green'
  #
  # object.red?
  # => false
  #
  # object.green?
  # => true
  #
  # object.class.colors
  # => { red: 0, green: 1 }
  #
  # object.color_to_enum
  # => 1
  #
  # It also adds validation:
  #
  # validates :color, presence: true, inclusion: colors.keys
  #
  # @return [NilClass]
  def enum_for(hash)
    enum_name = hash.keys.first.to_s
    mapping   = hash.values.first.with_indifferent_access

    define_singleton_method(enum_name.pluralize) do
      mapping
    end

    define_method(enum_name) do
      return nil if self[enum_name].blank?

      mapping.key(self[enum_name]).to_s
    end

    define_method("#{enum_name}=") do |value|
      begin
        self[enum_name] = Integer(value)
      rescue ArgumentError, TypeError
        self[enum_name] = mapping[value.to_s.downcase]
      end
    end

    mapping.each do |name, _|
      define_method "#{name}?" do
        public_send(enum_name).to_s == name
      end
    end

    mapping.each do |name, value|
      define_method "#{name}!" do
        public_send("#{enum_name}=", value)
        save!
      end
    end

    define_method("#{enum_name}_to_enum") do
      self[enum_name]
    end

    nil
  end
end