Module: MagicEnum::ClassMethods

Defined in:
lib/magic_enum/class_methods.rb

Instance Method Summary collapse

Instance Method Details

#define_enum(name, opts = {}) ⇒ Object

Method used to define ENUM attributes in your model. Examples:

Statuses = {
  :draft => 0,
  :published => 1,
  :approved => 2
}
define_enum :status

Before using define_enum, you should define constant with ENUM options. Constant name would be pluralized enum attribute name. Additional constant named like YourEnumInverted would be created automatically and would contain inverted hash.

Please note: nil and 0 are not the same values!

You could specify additional options:

  • :default - value which will be used when current state of ENUM attribute is invalid or wrong value received. If it has not been specified, min value of the ENUM would be used.

  • :raise_on_invalid - if true an exception would be raised on invalid enum value received. If it is false, default value would be used instead of wrong one.

  • :simple_accessors - if true, additional methods for each ENUM value would be defined in form value?. Methods returns true when ENUM attribute has corresponding value.

  • :enum - string with name of the ENUM hash.

Look the following example:

Statuses = {
  :unknown => 0,
  :draft => 1,
  :published => 2,
  :approved => 3
}
define_enum :status, :default => 1, :raise_on_invalid => true, :simple_accessors => true

This example is identical to:

Statuses = {
  :unknown => 0,
  :draft => 1,
  :published => 2,
  :approved => 3
}
StatusesInverted = Statuses.invert

def status
  return StatusesInverted[self[:status].to_i] || StatusesInverted[1]
end

def status=(value)
  raise ArgumentError, "Invalid value \"#{value}\" for :status attribute of the #{self.class} model" if Statuses[value].nil?
  self[:status] = Statuses[value]
end

def unknown?
  status == :unknown
end

def draft?
  status == :draft
end

def published?
  status == :published
end

def approved?
  status == :approved
end


77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/magic_enum/class_methods.rb', line 77

def define_enum(name, opts = {})
  default_opts = {  :raise_on_invalid => false,
                    :simple_accessors => false,
                    :named_scopes => false,
                    :scope_extensions => false
                    }
  opts = default_opts.merge(opts)
  name = name.to_sym

  # bug in Rails 1.2.2
  opts[:enum] = name.to_s.pluralize.classify.pluralize unless opts[:enum]
  enum = opts[:enum]
  enum_inverted = "#{enum}Inverted"

  opts[:default] = const_get(enum).values.sort do |a, b|
    if a.nil? and b.nil?
      0
    elsif a.nil?
      -1
    elsif b.nil?
      1
    else
      a <=> b
    end
  end.first unless opts[:default]

  const_set(enum_inverted, const_get(enum).invert)

  define_method name do
    self.class.const_get(enum_inverted)[self[name]] || self.class.const_get(enum_inverted)[opts[:default]]
  end

  define_method "#{name}_name" do
    send(name).to_s
  end

  define_method "#{name}=" do |value|
    value = value.to_sym if value.is_a?(String)
    raise ArgumentError, "Invalid value \"#{value}\" for :#{name} attribute of the #{self.class} model" if opts[:raise_on_invalid] and self.class.const_get(enum)[value].nil?
    if value.is_a?(Integer)
      self[name] = value
    else
      self[name] = self.class.const_get(enum)[value] || opts[:default]
    end
  end

  if opts[:simple_accessors]
    const_get(enum).keys.each do |key|
      define_method "#{key}?" do
        send(name) == key
      end
    end
  end

  # Create named scopes for each enum value
  if opts[:named_scopes]
    const_get(enum).keys.each do |key|
      named_scope key.to_s.pluralize.to_sym, :conditions => ["#{name} = ?", const_get(enum)[key]] do
        opts[:scope_extensions].each do |ext_name, ext_block|
          define_method ext_name, ext_block
        end if opts[:scope_extensions] and opts[:scope_extensions].is_a?(Hash)
      end
    end
    named_scope "of_#{name}".to_sym, lambda { |t| { :conditions => ["#{name} = ?", const_get(enum)[t]] } } do
      opts[:scope_extensions].each do |ext_name, ext_block|
        define_method ext_name, ext_block
      end if opts[:scope_extensions] and opts[:scope_extensions].is_a?(Hash)
    end
  end

end