require 'active_support'
require 'active_support/concern'
require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/string/inflections'
require 'enumerations/configuration'
require 'enumerations/version'
require 'enumerations/base'
require 'enumerations/reflection'
require 'enumerations/errors'
module Enumerations
extend ActiveSupport::Concern
included do
class_attribute :_enumerations
self._enumerations = []
end
module ClassMethods
# Create an enumeration for the symbol name.
# Options include foreign_key attribute and class_name
#
# Example:
#
# class User < ActiveRecord::Base
# enumeration :role
# end
#
# user.role => #
#
# user.role = Role.staff
#
def enumeration(name, options = {})
reflection = Reflection.new(name, options)
add_enumeration(reflection)
end
# Output all the enumerations that this model has defined
# Returns an array of Reflection objects for all the
# enumerations in the class.
#
# Example:
#
# User.reflect_on_all_enumerations => # [
# #,
# #
# ]
#
def reflect_on_all_enumerations
_enumerations
end
def fetch_foreign_key_values(reflection, *symbols)
symbols.flatten.map do |symbol|
enumeration_value = reflection.enumerator_class.find(symbol)
enumeration_value &&
enumeration_value.send(reflection.enumerator_class.primary_key || :symbol)
end
end
private
def add_enumeration(reflection)
define_getter_method(reflection)
define_setter_method(reflection)
define_bang_methods(reflection)
define_scopes_for_each_enumeration_value(reflection)
define_enumeration_scope(reflection)
self._enumerations += [reflection]
end
# Getter for belongs_to
#
# Example:
#
# user.role = Role.admin
# user.role => #
#
def define_getter_method(reflection)
define_method(reflection.name) do
reflection.enumerator_class.find(self[reflection.foreign_key])
end
end
# Setter for belongs_to
#
# Example:
#
# user.role = Role.admin
#
def define_setter_method(reflection)
define_method("#{reflection.name}=") do |other|
enumeration_value = reflection.enumerator_class.find(other)
raise Enumerations::InvalidValueError if other.present? && enumeration_value.nil?
self[reflection.foreign_key] =
enumeration_value &&
enumeration_value.send(reflection.enumerator_class.primary_key || :symbol)
end
end
# Add bang methods for setting all enumeration values.
# All methods are prefixed with enumeration name.
#
# Example:
#
# user.role_admin!
# user.role => #
#
def define_bang_methods(reflection)
reflection.enumerator_class.all.each do |enumeration|
define_method("#{reflection.name}_#{enumeration.to_sym}!") do
send("#{reflection.name}=", enumeration)
end
end
end
# Scopes for enumerated ActiveRecord model.
# Format of scope name is with_#{enumeration_name}_#{enumeration_value_name}.
#
# Example:
#
# User.with_role_admin => <#ActiveRecord::Relation []>
# User.with_role_editor => <#ActiveRecord::Relation []>
#
def define_scopes_for_each_enumeration_value(reflection)
reflection.enumerator_class.all.each do |enumeration|
foreign_key = enumeration.send(reflection.enumerator_class.primary_key || :symbol)
scope("with_#{reflection.name}_#{enumeration.symbol}",
-> { where(reflection.foreign_key => foreign_key) })
scope("without_#{reflection.name}_#{enumeration.symbol}",
-> { where.not(reflection.foreign_key => foreign_key) })
end
end
# Scope for enumerated ActiveRecord model.
# Format of scope name is with_#{enumeration_name}(*enumerations).
#
# Example:
#
# User.with_role(:admin) => <#ActiveRecord::Relation []>
# User.with_role(:admin, Role.editor) => <#ActiveRecord::Relation []>
#
def define_enumeration_scope(reflection)
scope("with_#{reflection.name}",
lambda do |*symbols|
where(reflection.foreign_key => fetch_foreign_key_values(reflection, symbols))
end)
scope("without_#{reflection.name}",
lambda do |*symbols|
where.not(reflection.foreign_key => fetch_foreign_key_values(reflection, symbols))
end)
end
end
end
# Extend ActiveRecord with Enumeration capabilites
ActiveSupport.on_load(:active_record) do
include Enumerations
end