require 'mongoid/enum/version' require 'mongoid/enum/validators/multiple_validator' module Mongoid # Mongoid Enum module Enum extend ActiveSupport::Concern # # Class Methods # # class Model # include Mongoid::Enum # # enum :status, in: %i( waiting approved dismissed ) # module ClassMethods # # Main class method # def enum(field_name, values, options = {}) options = default_options.merge(options) set_values_constant field_name, values create_field field_name, options, values create_i18n_helper field_name, options create_values_helper field_name, options create_validations field_name, values, options define_value_scopes_and_accessors field_name, values, options if options[:multiple] define_array_field_writer field_name else define_field_writer field_name end end private def default_options { multiple: false, required: true, validate: true } end def set_values_constant(name, values) const_name = name.to_s.upcase const_set const_name, values end def create_field(field_name, options, values) type = options[:multiple] && Array || Symbol default = \ if options.key?(:default) options[:default] else options[:multiple] ? [] : values.first end field field_name, type: type, default: default end def define_array_field_writer(field_name) define_method("#{field_name}=") do |vals| write_attribute(field_name, Array(vals).compact.map(&:to_sym)) end end def define_field_writer(field_name) define_method("#{field_name}=") do |val| val = nil if val.respond_to?(:empty?) && val.empty? write_attribute(field_name, val) end end def create_validations(field_name, values, options) return unless options[:validate] if options[:multiple] validates field_name, :'mongoid/enum/validators/multiple' => { in: values.map(&:to_sym), allow_blank: !options[:required] } else validates field_name, inclusion: { in: values }, allow_blank: !options[:required] end end def create_i18n_helper(field_name, options) return if options[:i18n].is_a?(FalseClass) if options[:multiple] define_method("#{field_name}_i18n") do return if self[field_name].blank? self[field_name].map do |k| I18n.translate("mongoid.enums.#{model_name.to_s.underscore}."\ "#{field_name}.#{k}") end end else define_method("#{field_name}_i18n") do return if self[field_name].blank? I18n.translate("mongoid.enums.#{model_name.to_s.underscore}."\ "#{field_name}.#{self[field_name]}") end end end def create_values_helper(field_name, options) return if options[:i18n].is_a?(FalseClass) define_singleton_method("#{field_name}_values") do I18n.translate("mongoid.enums.#{model_name.to_s.underscore}."\ "#{field_name}").map { |k, v| [v, k] } end end def define_value_scopes_and_accessors(field_name, values, options) values.each do |value| unless options[:scope].is_a?(FalseClass) scope value, -> { where(field_name => value) } end next if options[:accessor].is_a?(FalseClass) if options[:multiple] define_array_accessor(field_name, value) else define_string_accessor(field_name, value) end end end def define_array_accessor(field_name, value) class_eval "def #{value}?() self.#{field_name}.include?(:#{value}) end" class_eval "def #{value}!() update_attributes! :#{field_name} => (self.#{field_name} || []) + [:#{value}] end" end def define_string_accessor(field_name, value) class_eval "def #{value}?() self.#{field_name} == :#{value} end" define_method("#{value}!") do update_attributes! :"#{field_name}" => :"#{value}" end end end end end