Better Rails Enumerations
Ruby
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
lib
test
.gitignore
Gemfile
Rakefile
Readme.md
enumerations.gemspec

Readme.md

Enumerations

Gem Version Code Climate Build Status Test Coverage

Rails plugin for enumerations in ActiveRecord models.

Installation

Inside your Gemfile add the following:

gem 'enumerations'

Usage

Defining enumerations

Create a model for your enumerations:

class Status < Enumerations::Base
  values draft:           { name: 'Draft' },
         review_pending:  { name: 'Review pending' },
         published:       { name: 'Published' }
end

Or you can use value method for defining your enumerations:

class Status < Enumerations::Base
  value :draft,           name: 'Draft'
  value :review_pending,  name: 'Review pending'
  value :published,       name: 'Published'
end

Include enumerations for integer fields in other models:

class Post < ActiveRecord::Base
  enumeration :status

  validates :status, presence: true
end

You can pass attributes to specify which enumeration and which column to use:

class Post < ActiveRecord::Base
  enumeration :status,
              foreign_key: :post_status,   # specifies which column to use
              class_name: Post::Status     # specifies the class of the enumerator

  validates :post_status, presence: true
end

Attribute foreign_key you can pass as a String or a Symbol. Attribute class_name can be set as a String, a Symbol or a String.

Setting enumeration value to objects

Set enumerations:

@post = Post.first
@post.status = Status.draft
@post.save

Or you can set enumerations by symbol:

@post.status = Status.find(:draft)

Also, you can set enumeration value like this:

@post.status_draft!

When you include enumerations into your model, you'll get methods for setting each enumeration value. Each method name is consists from enumeration name and enumeration value name with ! at the end. Examples:

class Post < ActiveRecord::Base
  enumeration :status
end

@post.status_draft!
class User < ActiveRecord::Base
  enumeration :role
end

@user.role_admin!
class User < ActiveRecord::Base
  enumeration :type, class_name: Role
end

@user.type_editor!

Finder methods

Find enumerations by id:

@post.status = Status.find(2)                 # => Review pending
@post.save

Other finding methods:

# Find by key as a Symbol
Status.find(:review_pending)                  # => Review pending

# Find by key as a String
Status.find('draft')                          # => Draft

# Find by multiple attributes
Status.find_by(name: 'None', visible: true)   # => None

Compare enumerations:

@post.status == :published                    # => true
@post.status == 'published'                   # => true
@post.status == Status.published              # => true
@post.status.published?                       # => true

Get all enumerations:

Status.all

Filtering methods

Enumerations can be filtered with where method, similar to ActiveRecord::QueryMethods#where.

Role.where(admin: true)                       # => [Role.admin, Role.editor]
Role.where(admin: true, active: true)         # => [Role.admin]

Scopes on model

With enumerations, you'll get scope for each enumeration value in the following format:

with_#{enumeration_name}_#{enumeration_value_name}

Examples:

class Post < ActiveRecord::Base
  enumeration :status
end

Post.with_status_draft                        # => <#ActiveRecord::Relation []>
Post.with_status_review_pending               # => <#ActiveRecord::Relation []>
class Post < ActiveRecord::Base
  enumeration :my_status, class_name: Status
end

Post.with_my_status_draft                      # => <#ActiveRecord::Relation []>
Post.with_my_status_review_pending             # => <#ActiveRecord::Relation []>

Each scope returns all records with specified enumeration value.

Forms usage

Use in forms:

%p
  = f.label :status
  %br
  = f.collection_select :status, Status.all, :symbol, :name

Advanced Usage

Except name you can specify any other attributes to your enumerations:

class Status < Enumerations::Base
  value :draft,           id: 1, name: 'Draft'
  value :review_pending,  id: 2, name: 'Review pending', description: 'Some description...'
  value :published,       id: 3, name: 'Published', published: true
end

Every enumeration has id, name, description and published methods. If you call method that is not in attribute list for enumeration, it will return nil.

Status.review_pending.description              # => 'Some description...'
Status.draft.description                       # => nil

Translations

Enumerations uses power of I18n API to enable you to create a locale file for enumerations like this:

---
en:
  enumerations:
    status:
      draft:
        name: Draft
        description: Article draft...
        ...
    role:
      admin:
        name: Administrator

You can separate enumerations locales into a separate *.yml files. Then you should add locale file paths to I18n load path:

# config/initializers/locale.rb

# Where the I18n library should search for translation files (e.g.):
I18n.load_path += Dir[Rails.root.join('config', 'locales', 'enumerations', '*.yml')]

Configuration

Basically no configuration is needed.

Enumerations has two configuration options. You can customize primary key and foreign key suffix. Just add initializer file to config/initializers/enumerations.rb.

Example of configuration:

# config/initializers/enumerations.rb

Enumerations.configure do |config|
  config.primary_key        = :id
  config.foreign_key_suffix = :id
end

By default, primary_key and foreign_key_suffix are set to nil.

By default model enumeration value is saved to column with same name as enumeration. If you set enumeration as:

enumeration :status

then model should have statuscolumn (as String type). If you want save an ID to this column, you can set foreign_key_suffix to id. Then model should have status_id column.

If you set primary_key then you need provide this attribute for all enumerations values. Also, value from primary_key attribute will be stored to model as enumeration value.

For example:

# with default configuration

post = Post.new
post.status = Status.draft      # => post.status = 'draft'

# with configured primary_key and foreign_key_suffix:

Enumerations.configure do |config|
  config.primary_key        = :id
  config.foreign_key_suffix = :id
end

class Status < Enumerations::Base
  value :draft,           id: 1, name: 'Draft'
  value :review_pending,  id: 2, name: 'Review pending',
  value :published,       id: 3, name: 'Published', published: true
end

post = Post.new
post.status = Status.draft      # => post.status_id = 1

Database Enumerations

By default, enumeration values are saved to database as String. If you want, you can define Enum type in database:

CREATE TYPE status AS ENUM ('draft', 'review_pending', 'published');

Then you'll have enumeration as type in database and you can use it in database migrations:

add_column :posts, :status, :status, index: true

With configuration option primary_key, you can store any type you want (e.g. Integer).

Also, for performance reasons, you should add indices to enumeration column.

Here you can find more informations about ENUM types.

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

Credits

Enumerations is maintained and sponsored by Infinum

Copyright © 2016 Infinum Ltd.

License

The gem is available as open source under the terms of the MIT License.