Enumerize 
Enumerated attributes with I18n and ActiveRecord/Mongoid/MongoMapper/Sequel support
Table of Contents
- Installation
- Supported Versions
- Usage
- Database support
- I18n Support
- Boolean Helper Methods
- Optimzations and Tips
- Forms
- Testing
- Contributing
Installation
Add this line to your application’s Gemfile:
gem 'enumerize'
And then execute:
$ bundle
Or install it yourself as:
$ gem install enumerize
Supported Versions
- Ruby 2.7+
- Rails 5.2+
Usage
Basic:
```ruby class User extend Enumerize
enumerize :role, in: [:user, :admin] end ```
Note that enumerized values are just identifiers so if you want to use multi-word, etc. values then you should use I18n feature.
Database support
ActiveRecord
```ruby 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 end ```
:warning: By default, enumerize adds inclusion validation to the model. You can skip validations by passing skip_validations option. :warning:
```ruby 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 end ```
Mongoid
```ruby class User include Mongoid::Document extend Enumerize
field :role enumerize :role, in: [:user, :admin], default: :user end ```
MongoMapper
```ruby class User include MongoMapper::Document extend Enumerize
key :role enumerize :role, in: [:user, :admin], default: :user end ```
I18n Support
ruby
en:
enumerize:
user:
status:
student: "Student"
employed: "Employed"
retired: "Retiree"
or if you use status attribute across several models you can use defaults scope:
ruby
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.
```ruby 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:
ruby
class User
extend Enumerize
extend ActiveModel::Naming
end
I18n Helper Methods
*_text / .text
Attribute’s I18n text value:
ruby
@user.status_text # or @user.status.text
values
List of possible values for an enumerized attribute:
ruby
User.status.values # or User.enumerized_attributes[:status].values
# => ['student', 'employed', 'retired']
I18n text values
List of possible I18n text values for an enumerized attribute:
ruby
User.status.values.collect(&:text)
# => ['Student', 'Employed', 'Retiree']
Form example
Use it with forms (it supports :only and :except options):
erb
<%= form_for @user do |f| %>
<%= f.select :status, User.status.options %>
<% end %>
Boolean Helper Methods
Basic
ruby
user.status = :student
user.status.student? #=> true
user.status.retired? #=> false
Predicate Methods
```ruby class 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? # => false ```
:warning: If enumerize is used with Mongoid, it’s not recommended to use "writer" as a field value since writer? is defined by Mongoid. See more. :warning:
Predicate Prefixes
```ruby class User extend Enumerize
enumerize :status, in: %w(student employed retired), predicates: { prefix: true } end
user = User.new user.status = ‘student’ user.status_student? # => true ```
Use :only and :except options to specify what values create predicate methods for.
Optimzations and Tips
Extendable Module
To make some attributes shared across different classes it’s possible to define them in a separate module and then include it into classes:
```ruby module RoleEnumerations extend Enumerize
enumerize :roles, in: %w[user admin] end
class Buyer include RoleEnumerations end
class Seller include RoleEnumerations end ```
Customizing Enumerize Value
It’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:
```ruby class User < ActiveRecord::Base extend Enumerize
enumerize :role, in: => 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 #=> 2 ```
ActiveRecord scopes:
Basic
```ruby class 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’) ```
Shallow Scopes
Adds named scopes to the class directly.
```ruby 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 ```
:warning: It is not possible to define a scope when using the :multiple option. :warning:
Array-like Attributes
Array-like attributes with plain ruby objects:
```ruby class User extend Enumerize
enumerize :interests, in: [:music, :sports], multiple: true end
user = User.new user.interests « :music user.interests « :sports ```
and with ActiveRecord:
```ruby class User < ActiveRecord::Base extend Enumerize
serialize :interests, Array enumerize :interests, in: [:music, :sports], multiple: true end ```
get an array of all text values:
ruby
@user.interests.texts # shortcut for @user.interests.map(&:text)
Also, the reader method can be overridden, referencing the enumerized attribute value using super:
ruby
def status
if current_user.admin?
"Super #{super}"
else
super
end
end
Forms
SimpleForm
If you are using SimpleForm gem you don’t need to specify input type (:select by default) and collection:
erb
<%= simple_form_for @user do |f| %>
<%= f.input :status %>
<% end %>
and if you want it as radio buttons:
erb
<%= 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.
Formtastic
If you are using Formtastic gem you also don’t need to specify input type (:select by default) and collection:
erb
<%= semantic_form_for @user do |f| %>
<%= f.input :status %>
<% end %>
and if you want it as radio buttons:
erb
<%= semantic_form_for @user do |f| %>
<%= f.input :status, :as => :radio %>
<% end %>
Testing
RSpec
Also you can use builtin RSpec matcher:
```ruby 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) } end ```
Qualifiers
in
Use in to test usage of the :in option.
```ruby class User extend Enumerize
enumerize :status, in: [:student, :employed, :retired] end
describe User do it { should enumerize(:status).in(:student, :employed, :retired) } end ```
You can test enumerized attribute value using custom values with the in
qualifier.
```ruby class User extend Enumerize
enumerize :role, in: { user: 0, admin: 1 } end
describe User do it { should enumerize(:role).in(user: 0, admin: 1) } end ```
with_default
Use with_default to test usage of the :default option.
```ruby 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) } end ```
with_i18n_scope
Use with_i18n_scope to test usage of the :i18n_scope option.
```ruby 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’) } end ```
with_predicates
Use with_predicates to test usage of the :predicates option.
```ruby 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) } end ```
You can text prefixed predicates with the with_predicates qualifiers.
```ruby 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) } end ```
with_scope
Use with_scope to test usage of the :scope option.
```ruby 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) } end ```
You can test a custom scope with the with_scope qualifiers.
```ruby 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) } end ```
with_multiple
Use with_multiple to test usage of the :multiple option.
```ruby 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) } end ```
Minitest with Shoulda
You can use the RSpec matcher with shoulda in your tests by adding two lines in your test_helper.rb inside class ActiveSupport::TestCase definition:
```ruby class ActiveSupport::TestCase ActiveRecord::Migration.check_pending!
require ‘enumerize/integrations/rspec’ extend Enumerize::Integrations::RSpec
… end ```
Other Integrations
Enumerize integrates with the following automatically:
Contributing
- 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