Method: SimpleEnum::ClassMethods#as_enum
- Defined in:
- lib/simple_enum.rb
#as_enum(enum_cd, values, options = {}) ⇒ Object
Provides ability to create simple enumerations based on hashes or arrays, backed by integer columns (but not limited to integer columns).
Columns are supposed to be suffixed by _cd
, if not, use :column => 'the_column_name'
, so some example migrations:
add_column :users, :gender_cd, :integer
add_column :users, :status, :integer # and a custom column...
and then in your model:
class User < ActiveRecord::Base
as_enum :gender, [:male, :female]
end
# or use a hash:
class User < ActiveRecord::Base
as_enum :status, { :active => 1, :inactive => 0, :archived => 2, :deleted => 3 }, :column => 'status'
end
Now it’s possible to access the enumeration and the internally stored value like:
john_doe = User.new
john_doe.gender # => nil
john_doe.gender = :male
john_doe.gender # => :male
john_doe.gender_cd # => 0
And to make life a tad easier: a few shortcut methods to work with the enumeration are also created.
john_doe.male? # => true
john_doe.female? # => false
john_doe.female! # => :female (set's gender to :female => gender_cd = 1)
john_doe.male? # => false
Sometimes it’s required to access the db-backed values, like e.g. in a query:
User.genders # => { :male => 0, :female => 1}, values hash
User.genders(:male) # => 0, value access (via hash)
User.female # => 1, direct access
User.find :all, :conditions => { :gender_cd => User.female } # => [...], list with all women
To access the key/value assocations in a helper like the select helper or similar use:
<%= select(:user, :gender, User.genders.keys)
The generated shortcut methods (like male?
or female!
etc.) can also be prefixed using the :prefix
option. If the value is true
, the shortcut methods are prefixed with the name of the enumeration.
class User < ActiveRecord::Base
as_enum :gender, [:male, :female], :prefix => true
end
jane_doe = User.new
jane_doe.gender = :female # this is still as-is
jane_doe.gender_cd # => 1, and so it this
jane_doe.gender_female? # => true (instead of jane_doe.female?)
It is also possible to supply a custom prefix.
class Item < ActiveRecord::Base
as_enum :status, [:inactive, :active, :deleted], :prefix => :state
end
item = Item.new(:status => :active)
item.state_inactive? # => false
Item.state_deleted # => 2
Item.status(:deleted) # => 2, same as above...
To disable the generation of the shortcut methods for all enumeration values, add :slim => true
to the options.
class Address < ActiveRecord::Base
as_enum :canton, {:aargau => 'ag', ..., :wallis => 'vs', :zug => 'zg', :zurich => 'zh'}, :slim => true
end
home = Address.new(:canton => :zurich, :street => 'Bahnhofstrasse 1', ...)
home.canton # => :zurich
home.canton_cd # => 'zh'
home.aargau! # throws NoMethodError: undefined method `aargau!'
Address.aargau # throws NoMethodError: undefined method `aargau`
This is especially useful if there are (too) many enumeration values, or these shortcut methods are not required.
Configuration options:
-
:column
- Specifies a custom column name, instead of the default suffixed_cd
column -
:prefix
- Define a prefix, which is prefixed to the shortcut methods (e.g.<symbol>!
and<symbol>?
), if it’s set totrue
the enumeration name is used as a prefix, else a custom prefix (symbol or string) (default isnil
=> no prefix) -
:slim
- If set totrue
no shortcut methods for all enumeration values are being generated, if set to:class
only class-level shortcut methods are disabled (default isnil
=> they are generated) -
:upcase
- If set totrue
theKlass.foos
is namedKlass.FOOS
, why? To better suite some coding-styles (default isfalse
=> downcase) -
:whiny
- Boolean value which if set totrue
will throw anArgumentError
if an invalid value is passed to the setter (e.g. a value for which no enumeration exists). if set tofalse
no exception is thrown and the internal value is set tonil
(default istrue
)
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'lib/simple_enum.rb', line 153 def as_enum(enum_cd, values, = {}) = SimpleEnum..merge({ :column => "#{enum_cd}_cd" }).merge() .assert_valid_keys(:column, :whiny, :prefix, :slim, :upcase) = (class << self; self; end) # convert array to hash... values = SimpleEnum::EnumHash.new(values) values_inverted = values.invert # store info away write_inheritable_attribute(:enum_definitions, {}) if enum_definitions.nil? enum_definitions[enum_cd] = enum_definitions[[:column]] = { :name => enum_cd, :column => [:column], :options => } # generate getter define_method("#{enum_cd}") do id = read_attribute [:column] values_inverted[id] end # generate setter define_method("#{enum_cd}=") do |new_value| v = new_value.blank? ? nil : values[new_value.to_sym] raise(ArgumentError, "Invalid enumeration value: #{new_value}") if ([:whiny] and v.nil? and !new_value.blank?) write_attribute [:column], v end # allow access to defined values hash, e.g. in a select helper or finder method. attr_name = enum_cd.to_s.pluralize enum_attr = :"#{attr_name.downcase}_enum_hash" write_inheritable_attribute(enum_attr, values) class_eval(<<-RUBY, __FILE__, __LINE__ + 1) def self.#{attr_name}(*args) return read_inheritable_attribute(#{enum_attr.inspect}) if args.first.nil? return read_inheritable_attribute(#{enum_attr.inspect})[args.first] if args.size == 1 args.inject([]) { |ary, sym| ary << read_inheritable_attribute(#{enum_attr.inspect})[sym]; ary } end def self.#{attr_name}_for_select(attr = :key, &block) self.#{attr_name}.map do |k,v| [block_given? ? yield(k,v) : self.human_enum_name(#{attr_name.inspect}, k), attr == :value ? v : k] end end RUBY # only create if :slim is not defined if [:slim] != true # create both, boolean operations and *bang* operations for each # enum "value" prefix = [:prefix] && "#{[:prefix] == true ? enum_cd : [:prefix]}_" values.each do |k,code| sym = k.to_enum_sym define_method("#{prefix}#{sym}?") do code == read_attribute([:column]) end define_method("#{prefix}#{sym}!") do write_attribute [:column], code sym end # allow class access to each value unless [:slim] === :class .send(:define_method, "#{prefix}#{sym}", Proc.new { |*args| args.first ? k : code }) end end end end |