Enumerated attributes with I18n and ActiveRecord/Mongoid/MongoMapper/Sequel support
- Installation
- Supported Versions
- Usage
- Database support
- I18n Support
- Boolean Helper Methods
- Optimzations and Tips
- Forms
- Testing
- Contributing
Add this line to your application's Gemfile:
gem 'enumerize' And then execute:
$ bundle Or install it yourself as:
$ gem install enumerize - Ruby 3.1+
- Rails 7.0+
Basic:
class User extend Enumerize enumerize :role, in: [:user, :admin] endNote that enumerized values are just identifiers so if you want to use multi-word, etc. values then you should use I18n feature.
class CreateUsers < ActiveRecord::Migration def change create_table :users do |t| t.string :status t.string :role t.timestamps end end end class User < ActiveRecord::Base extend Enumerize enumerize :status, in: [:student, :employed, :retired], default: lambda { |user| StatusIdentifier.status_for_age(user.age).to_sym } enumerize :role, in: [:user, :admin], default: :user endenumerize adds inclusion validation to the model. You can skip validations by passing skip_validations option.
class User < ActiveRecord::Base extend Enumerize enumerize :status, in: [:student, :employed, :retired], skip_validations: lambda { |user| user.new_record? } enumerize :role, in: [:user, :admin], skip_validations: true endclass User include Mongoid::Document extend Enumerize field :role enumerize :role, in: [:user, :admin], default: :user endclass User include MongoMapper::Document extend Enumerize key :role enumerize :role, in: [:user, :admin], default: :user enden: enumerize: user: status: student: "Student" employed: "Employed" retired: "Retiree"or if you use status attribute across several models you can use defaults scope:
en: enumerize: defaults: status: student: "Student" employed: "Employed" retired: "Retiree"You can also pass i18n_scope option to specify scope (or array of scopes) storing the translations.
class Person extend Enumerize extend ActiveModel::Naming enumerize :status, in: %w[student employed retired], i18n_scope: "status" enumerize :roles, in: %w[user admin], i18n_scope: ["user.roles", "roles"] enumerize :color, in: %w[green blue], i18n_scope: proc { |value| "color" } end # localization file en: status: student: "Student" employed: "Employed" retired: "Retiree" user: roles: user: "User" roles: admin: "Admin"Note that if you want to use I18n feature with plain Ruby object don't forget to extend it with ActiveModel::Naming:
class User extend Enumerize extend ActiveModel::Naming endAttribute's I18n text value:
@user.status_text # or @user.status.textList of possible values for an enumerized attribute:
User.status.values # or User.enumerized_attributes[:status].values # => ['student', 'employed', 'retired']List of possible I18n text values for an enumerized attribute:
User.status.values.collect(&:text) # => ['Student', 'Employed', 'Retiree']Use it with forms (it supports :only and :except options):
<%= form_for @user do |f| %> <%= f.select :status, User.status.options %> <% end %>user.status = :student user.status.student? #=> true user.status.retired? #=> falseclass User extend Enumerize enumerize :status, in: %w(student employed retired), predicates: true end user = User.new user.student? # => false user.employed? # => false user.status = :student user.student? # => true user.employed? # => falseenumerize is used with Mongoid, it's not recommended to use "writer" as a field value since writer? is defined by Mongoid. See more.
class User extend Enumerize enumerize :status, in: %w(student employed retired), predicates: { prefix: true } end user = User.new user.status = 'student' user.status_student? # => trueUse :only and :except options to specify what values create predicate methods for.
To make some attributes shared across different classes it's possible to define them in a separate module and then include it into classes:
module RoleEnumerations extend Enumerize enumerize :roles, in: %w[user admin] end class Buyer include RoleEnumerations end class Seller include RoleEnumerations endIt's also possible to store enumerized attribute value using custom values (e.g. integers). You can pass a hash as :in option to achieve this:
class User < ActiveRecord::Base extend Enumerize enumerize :role, in: { user: 1, admin: 2 } end user = User.new user.role = :user user.role #=> 'user' user.role_value #=> 1 User.role.find_value(:user).value #=> 1 User.role.find_value(:admin).value #=> 2class User < ActiveRecord::Base extend Enumerize enumerize :role, in: [:user, :admin], scope: true enumerize :status, in: { student: 1, employed: 2, retired: 3 }, scope: :having_status end User.with_role(:admin) # SELECT "users".* FROM "users" WHERE "users"."role" IN ('admin') User.without_role(:admin) # SELECT "users".* FROM "users" WHERE "users"."role" NOT IN ('admin') User.having_status(:employed).with_role(:user, :admin) # SELECT "users".* FROM "users" WHERE "users"."status" IN (2) AND "users"."role" IN ('user', 'admin')Adds named scopes to the class directly.
class User < ActiveRecord::Base extend Enumerize enumerize :status, in: [:student, :employed, :retired], scope: :shallow enumerize :role, in: { user: 1, admin: 2 }, scope: :shallow end User.student # SELECT "users".* FROM "users" WHERE "users"."status" = 'student' User.admin # SELECT "users".* FROM "users" WHERE "users"."role" = 2:multiple option.
Array-like attributes with plain ruby objects:
class User extend Enumerize enumerize :interests, in: [:music, :sports], multiple: true end user = User.new user.interests << :music user.interests << :sportsand with ActiveRecord:
class User < ActiveRecord::Base extend Enumerize serialize :interests, Array enumerize :interests, in: [:music, :sports], multiple: true endget an array of all text values:
@user.interests.texts # shortcut for @user.interests.map(&:text)Also, the reader method can be overridden, referencing the enumerized attribute value using super:
def status if current_user.admin? "Super #{super}" else super end endIf you are using SimpleForm gem you don't need to specify input type (:select by default) and collection:
<%= simple_form_for @user do |f| %> <%= f.input :status %> <% end %>and if you want it as radio buttons:
<%= simple_form_for @user do |f| %> <%= f.input :status, as: :radio_buttons %> <% end %>Please note that Enumerize overwrites the I18n keys of SimpleForm collections. The enumerized keys are used instead of the SimpleForm ones for inputs concerning enumerized attributes. If you don't want this just pass :collection option to the input call.
If you are using Formtastic gem you also don't need to specify input type (:select by default) and collection:
<%= semantic_form_for @user do |f| %> <%= f.input :status %> <% end %>and if you want it as radio buttons:
<%= semantic_form_for @user do |f| %> <%= f.input :status, as: :radio %> <% end %>Also you can use builtin RSpec matcher:
class User extend Enumerize enumerize :status, in: [:student, :employed, :retired] end describe User do it { should enumerize(:status) } # or with RSpec 3 expect syntax it { is_expected.to enumerize(:status) } endUse in to test usage of the :in option.
class User extend Enumerize enumerize :status, in: [:student, :employed, :retired] end describe User do it { should enumerize(:status).in(:student, :employed, :retired) } endYou can test enumerized attribute value using custom values with the in qualifier.
class User extend Enumerize enumerize :role, in: { user: 0, admin: 1 } end describe User do it { should enumerize(:role).in(user: 0, admin: 1) } endUse with_default to test usage of the :default option.
class User extend Enumerize enumerize :role, in: [:user, :admin], default: :user end describe User do it { should enumerize(:user).in(:user, :admin).with_default(:user) } endUse with_i18n_scope to test usage of the :i18n_scope option.
class User extend Enumerize enumerize :status, in: [:student, :employed, :retired], i18n_scope: 'status' end describe User do it { should enumerize(:status).in(:student, :employed, :retired).with_i18n_scope('status') } endUse with_predicates to test usage of the :predicates option.
class User extend Enumerize enumerize :status, in: [:student, :employed, :retired], predicates: true end describe User do it { should enumerize(:status).in(:student, :employed, :retired).with_predicates(true) } endYou can text prefixed predicates with the with_predicates qualifiers.
class User extend Enumerize enumerize :status, in: [:student, :employed, :retired], predicates: { prefix: true } end describe User do it { should enumerize(:status).in(:student, :employed, :retired).with_predicates(prefix: true) } endUse with_scope to test usage of the :scope option.
class User extend Enumerize enumerize :status, in: [:student, :employed, :retired], scope: true end describe User do it { should enumerize(:status).in(:student, :employed, :retired).with_scope(true) } endYou can test a custom scope with the with_scope qualifiers.
class User extend Enumerize enumerize :status, in: [:student, :employed], scope: :employable end describe User do it { should enumerize(:status).in(:student, :employed, :retired).with_scope(scope: :employable) } endUse with_multiple to test usage of the :multiple option.
class User extend Enumerize enumerize :status, in: [:student, :employed, :retired], multiple: true end describe User do it { should enumerize(:status).in(:student, :employed, :retired).with_multiple(true) } endYou can use the RSpec matcher with shoulda in your tests by adding two lines in your test_helper.rb inside class ActiveSupport::TestCase definition:
class ActiveSupport::TestCase ActiveRecord::Migration.check_pending! require 'enumerize/integrations/rspec' extend Enumerize::Integrations::RSpec ... endEnumerize integrates with the following automatically:
- Fork it
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Added some feature') - Push to the branch (
git push origin my-new-feature) - Create new Pull Request