SimpleEnum
Unobtrusive enum-like fields for ActiveRecord and Ruby, brings enums functionality to ActiveRecord and Mongoid models (built for Rails 4+).
Since version 2.0, simple_enum is no longer compatible with Rails 3.x or Ruby 1.8, use version 1.6 instead: https://github.com/lwe/simple_enum/tree/legacy-1.x
Note: a recent search on github for enum
turned out, that there are many,
many similar solutions. In fact starting with Rails 4.1, there's ActiveRecord::Enum
which provides some of the functionality, but is IMHO pretty limited and too
strict in the defaults it provides.
ActiveRecord Quick start
Add this to a model:
class User < ActiveRecord::Base
as_enum :gender, female: 1, male: 0
end
Then create the required gender_cd
column using migrations:
class AddGenderColumnToUser < ActiveRecord::Migration
def self.up
add_column :users, :gender_cd, :integer
end
def self.down
remove_column :users, :gender_cd
end
end
Mongoid Quick start
Due to the dependency on ActiveModel 4.x, the Mongoid integration is only available for mongoid 4.0.0 (which is at beta1 at the moment). If you intend to use simple_enum with another version of mongoid, use version 1.6 instead.
Load mongoid support in the Gemfile
:
gem 'simple_enum', '~> 2.3.0' , require: 'simple_enum/mongoid'
Add this to a model:
class User
include Mongoid::Document
include SimpleEnum::Mongoid
as_enum :gender, female: 1, male: 0
end
The primary difference between AR and mongoid is, that additionaly a field is
added to mongoid automatically, the field can be customized by setting field:
option, or disabled by setting field: false
.
Working with enums
Now it's possible to pull some neat tricks on the new column, yet the original
db column (gender_cd
) is still intact and not touched by anything.
jane = User.new
jane.gender = :female
jane.female? # => true
jane.male? # => false
jane.gender # => :female
jane.gender_cd # => 1
Easily switch to another value using the bang methods, this does not save the record, only switch the value.
joe = User.new
joe.male! # => :male
joe.gender # => :male
joe.gender_cd # => 0
Accessing actual enum values is possible at the class level:
User.genders # => #<SimpleEnum::Enum:0x0....>
User.genders[:male] # => 0
User.genders.values_at(:male, :female) # => [0, 1] (since 2.1.0)
User.females # => #<ActiveRecord::Relation:0x0.....> (WHERE gender_cd = 1)
By default, scope names are generated as pluralized forms of the defined enum values e.g.
class Booking < ActiveRecord::Base
as_enum :status, %i{active cancelled pending}
end
would generate the following:
Booking.actives # => #<ActiveRecord::Relation:0x0.....> (WHERE status_cd = 1)
Booking.cancelleds # => #<ActiveRecord::Relation:0x0.....> (WHERE status_cd = 2)
Booking.pendings # => #<ActiveRecord::Relation:0x0.....> (WHERE status_cd = 3)
By setting pluralize_scopes: false
will not generate pluralized versions of scopes e.g.
class Booking < ActiveRecord::Base
as_enum :status, %i{active cancelled pending}, pluralize_scopes: false
end
would generate the following:
Booking.active # => #<ActiveRecord::Relation:0x0.....> (WHERE status_cd = 1)
Booking.cancelled # => #<ActiveRecord::Relation:0x0.....> (WHERE status_cd = 2)
Booking.pending # => #<ActiveRecord::Relation:0x0.....> (WHERE status_cd = 3)
Wait, there's more!
-
Too tired of always adding the integer values? Try:
class User < ActiveRecord::Base as_enum :status, %i{deleted active disabled} # translates to: { deleted: 0, active: 1, disabled: 2 } end
Disclaimer: if you ever decide to reorder this array, beware that any previous mapping is lost. So it's recommended to create mappings (that might change) using hashes instead of arrays. For stuff like gender it might be probably perfectly fine to use arrays though.
-
You can store as string values instead of integer values if your database column has the type
string
ortext
:class User < ActiveRecord::Base as_enum :status, [:deleted, :active, :disabled], map: :string end User.create!(status: :active) #=> #<User id: 1, status_cd: "active">
-
Want to use
SimpleEnum
in an ActiveModel, or other class, just add:class MyModel extend SimpleEnum::Attribute attr_accessor :gender_cd as_enum :gender, [:male, :female] end
-
Maybe you've columns named differently than the proposed
{column}_cd
naming scheme, feel free to use any column name by providing an option:class User < ActiveRecord::Base as_enum :gender, [:male, :female], source: :sex end
Starting with 2.0 it's possible to use the same source name as column name.
-
By default ActiveRecord dirty methods are generated:
user = User.male.first user.gender = :female user.gender_was # => :male
-
Need to provide custom options for the mongoid field, or skip the automatically generated field?
# skip field generation field :gender_cd # <- create field manually (!) as_enum :gender, [:male, :female], field: false # custom field options (directly passed to Mongoid::Document#field) as_enum :gender, [:male, :female], field: { :type => Integer, :default => 1 }
-
To validate enum values simply make use of a
validates :gender, presence: true
validation. If an invalid value is assigned, the gender is set tonil
by default. -
If the shortcut methods (like
female?
,female!
orUser.male
) conflict with something in your class, it's possible to define a prefix:class User < ActiveRecord::Base as_enum :gender, %w{male female}, prefix: true end jane = User.new gender: :female jane.gender_female? # => true User.gender_females # => <ActiveRecord::Relation...WHERE gender_cd = 1.>
The
:prefix
option not only takes a boolean value as an argument, but instead can also be supplied a custom prefix, so withprefix: 'foo'
all shortcut methods would look like:foo_<symbol>
-
To define which methods are generated it's possible to set
with:
option, by defaultwith:
is set to[:attribute, :dirty, :scope]
.-
:attribute
- generates themale?
andmale!
accessor methods -
:dirty
- adds thegender_was
andgender_changed?
dirty methods -
:scope
- adds the class level scopes, if thescope
method is present
-
-
By default the value is set to
nil
when the user sets an invalid value, this behavior can be changed by setting theaccessor:
option. At the moment there are three different behaviors:-
:default
- which sets the value simply tonil
-
:whiny
- raises an ArgumentError when trying to set an invalid value -
:ignore
- keeps the existing value
class User < ActiveRecord::Base as_enum :gender, %w{male female}, accessor: :whiny end User.new(gender: "dunno") # => raises ArgumentError
See
lib/simple_enum/accessors/*
for more. -
-
To define any option globally, e.g. never generating dirty methods, create an initializer and add:
# See lib/simple_enum.rb for other options SimpleEnum.with = [:attribute, :scope]
View Helpers
Require translated enum values? See SimpleEnum::ViewHelpers for more details and functions. Disclaimer: these methods are release candidate quality so expect them to change in future versions of SimpleEnum.
-
Translate the current value in a view:
translate_enum user, :gender # => "Frau" # assuming :de and translations exist te user, :gender # translate_enum is also aliased to te
Provide translations in the i18n yaml file like:
de: enums: gender: female: 'Frau' male: 'Mann'
-
Build a select tag with a translated dropdown and symbol as value:
select :user, :gender, enum_option_pairs(User, :gender)
-
...and one with the index as value:
select :user, :gender_cd, enum_option_pairs(User, :gender, true)
Extensions
simple_enum
provides hooks to extend its functionality, starting with 2.3.0
the following extensions can be used:
- Multi-select enum support for SimpleEnum: simple_enum-multiple
- Persistence values, i.e. store values in the DB: simple_enum-persistence
- Scopes, scopes helper for ActiveRecord enum attributes: simple_enum-scopes
Best practices
Do not use values named after existing, or well known method names, like new
, create
etc.
# BAD, conflicts with Rails ActiveRecord Methods (!)
as_enum :handle, [:new, :create, :update]
# GOOD, prefixes all methods
as_enum :handle, [:new, :create, :update], prefix: true
Searching for certain values by using the finder methods:
User.females # => returns an ActiveRecord::Relation
Contributors
- @dmitry - bugfixes and other improvements
- @tarsolya - implemented all the ruby 1.9 and rails 3 goodness!
- @dbalatero - rails 2.3.5 bugfix & validator fixes
-
@johnthethird - feature for
_for_select
to return the values - @sinsiliux - ruby 1.9 fixes and removed AR dependency
- @sled - mongoid support
-
@abrom -
find_by_...
method - @mhuggins - translations fixes
- @patbenatar - for helping move towards 2.0 (scopes et all)
- @abacha - translation helpers, README fixes
- @bbtfr - for support, ideas and pushing extensions
- and all others: https://github.com/lwe/simple_enum/graphs/contributors thanks
License & Copyright
Copyright (c) 2011-2015 by Lukas Westermann, Licensed under MIT License (see LICENSE file)