├── .github
└── workflows
│ └── test.yml
├── .gitignore
├── .rubocop.yml
├── CHANGELOG.md
├── Gemfile
├── LICENSE
├── MIGRATE.md
├── README.md
├── Rakefile
├── enumerations.gemspec
├── lib
├── enumerations.rb
└── enumerations
│ ├── base.rb
│ ├── configuration.rb
│ ├── errors.rb
│ ├── finder_methods.rb
│ ├── reflection.rb
│ ├── value.rb
│ └── version.rb
└── test
├── base_test.rb
├── configuration
├── configuration.rb
├── custom_configuration_test.rb
├── enumerations_test.rb
└── finder_test.rb
├── configuration_test.rb
├── enumerations_test.rb
├── finder_test.rb
├── helpers
├── database_helper.rb
├── locale_helper.rb
└── test_helper.rb
├── locales
└── hr.yml
├── reflection_test.rb
├── translation_test.rb
└── value_test.rb
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Test suite
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 |
9 | jobs:
10 | test:
11 | name: Ruby ${{ matrix.ruby }}
12 | runs-on: ubuntu-latest
13 | strategy:
14 | matrix:
15 | ruby: [2.6, 2.7, '3.0', 3.1]
16 |
17 | steps:
18 | - uses: actions/checkout@v2
19 | - uses: ruby/setup-ruby@v1
20 | with:
21 | ruby-version: ${{ matrix.ruby }}
22 | bundler-cache: true
23 | - name: Run tests
24 | run: bundle exec rake test
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.gem
3 | Gemfile.lock
4 | .todo
5 | coverage
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
1 | LineLength:
2 | Max: 100
3 |
4 | Documentation:
5 | Enabled: False
6 |
7 | Style/SymbolArray:
8 | Enabled: False
9 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | ## [v2.6.0](https://github.com/infinum/enumerations/tree/v2.6.0) (2024-10-22)
4 | [Full Changelog](https://github.com/infinum/enumerations/compare/v2.5.4...v2.6.0)
5 |
6 | **Implemented enhancements:**
7 |
8 | - Add ability to specify foreign key suffix on enum class.
9 |
10 | ## [v2.5.4](https://github.com/infinum/enumerations/tree/v2.5.4) (2023-01-09)
11 | [Full Changelog](https://github.com/infinum/enumerations/compare/v2.5.3...v2.5.4)
12 |
13 | **Implemented enhancements:**
14 |
15 | - Add MIT license file.
16 |
17 | ## [v2.5.3](https://github.com/infinum/enumerations/tree/v2.5.3) (2022-02-02)
18 | [Full Changelog](https://github.com/infinum/enumerations/compare/v2.5.2...v2.5.3)
19 |
20 | **Fixed bugs:**
21 |
22 | - Prevent `method_missing` instantiates new instance of `Enumerations::Base`; returns `String` instead
23 |
24 | **Merged pull requests:**
25 |
26 | - Prevent method_missing to instantiates new Enumerations::Base [\#58](https://github.com/infinum/enumerations/pull/58) ([PetarCurkovic](https://github.com/PetarCurkovic))
27 |
28 | ## [v2.5.2](https://github.com/infinum/enumerations/tree/v2.5.2) (2022-01-27)
29 | [Full Changelog](https://github.com/infinum/enumerations/compare/v2.5.1...v2.5.2)
30 |
31 | **Implemented enhancements:**
32 |
33 | - Adds Ruby 3.1 to test suite. Changes the runner to ubuntu-latest.
34 |
35 | **Closed issues:**
36 |
37 | - Ruby 3.0, `respond_to? :name` always returns true [\#55](https://github.com/infinum/enumerations/issues/55)
38 |
39 | **Merged pull requests:**
40 |
41 | - Initialize `ActiveSupport::Multibyte::Chars` with a String [\#56](https://github.com/infinum/enumerations/pull/56) ([lovro-bikic](https://github.com/lovro-bikic))
42 | - Test against Ruby 3.1 [\#57](https://github.com/infinum/enumerations/pull/57) ([lovro-bikic](https://github.com/lovro-bikic))
43 |
44 | ## [v2.5.1](https://github.com/infinum/enumerations/tree/v2.5.1) (2021-05-24)
45 | [Full Changelog](https://github.com/infinum/enumerations/compare/v2.5.0...v2.5.1)
46 |
47 | **Implemented enhancements:**
48 |
49 | - Add support for Ruby 3 [\#52](https://github.com/infinum/enumerations/issues/52)
50 |
51 | ## [v2.5.0](https://github.com/infinum/enumerations/tree/v2.5.0) (2021-03-03)
52 | [Full Changelog](https://github.com/infinum/enumerations/compare/v2.4.0...v2.5.0)
53 |
54 | **Implemented enhancements:**
55 |
56 | - Add `raise_invalid_value_error` configuration option to disable raising errors on invalid values
57 |
58 | ## [v2.4.0](https://github.com/infinum/enumerations/tree/v2.4.0) (2019-02-07)
59 | [Full Changelog](https://github.com/infinum/enumerations/compare/v2.3.3...v2.4.0)
60 |
61 | **Implemented enhancements:**
62 |
63 | - Add `translate_attributes` configuration option to disable translation of attributes
64 |
65 | ## [v2.3.3](https://github.com/infinum/enumerations/tree/v2.3.1) (2019-19-09)
66 | [Full Changelog](https://github.com/infinum/enumerations/compare/v2.3.2...v2.3.3)
67 |
68 | ## [v2.3.2](https://github.com/infinum/enumerations/tree/v2.3.1) (2019-03-27)
69 | [Full Changelog](https://github.com/infinum/enumerations/compare/v2.3.1...v2.3.2)
70 |
71 | ## [v2.3.1](https://github.com/infinum/enumerations/tree/v2.3.1) (2017-09-08)
72 | [Full Changelog](https://github.com/infinum/enumerations/compare/v2.3.0...v2.3.1)
73 |
74 | ## [v2.3.0](https://github.com/infinum/enumerations/tree/v2.3.0) (2017-09-08)
75 | [Full Changelog](https://github.com/infinum/enumerations/compare/v2.2.3...v2.3.0)
76 |
77 | **Implemented enhancements:**
78 |
79 | - Setting attribute to nil if enumeration value is unknown - surprising? [\#35](https://github.com/infinum/enumerations/issues/35)
80 | - Add `without` methods [\#31](https://github.com/infinum/enumerations/issues/31)
81 | - Add methods with '?' [\#19](https://github.com/infinum/enumerations/issues/19)
82 | - Add with\_enumeration\(enumeration\) scope [\#18](https://github.com/infinum/enumerations/issues/18)
83 |
84 | **Closed issues:**
85 |
86 | - Not assigning the enumeration when tweaking the primary and foreign key [\#32](https://github.com/infinum/enumerations/issues/32)
87 | - Undocumented feature - can't set attribute if it's not in the enumeration [\#30](https://github.com/infinum/enumerations/issues/30)
88 |
89 | **Merged pull requests:**
90 |
91 | - Feature/raising error on invalid enumeration value [\#38](https://github.com/infinum/enumerations/pull/38) ([PetarCurkovic](https://github.com/PetarCurkovic))
92 | - Bugfix/config options [\#37](https://github.com/infinum/enumerations/pull/37) ([PetarCurkovic](https://github.com/PetarCurkovic))
93 | - Feature/predicate methods for attributes [\#36](https://github.com/infinum/enumerations/pull/36) ([PetarCurkovic](https://github.com/PetarCurkovic))
94 | - Feature/without scopes [\#34](https://github.com/infinum/enumerations/pull/34) ([PetarCurkovic](https://github.com/PetarCurkovic))
95 | - Feature/with enumeration scope [\#33](https://github.com/infinum/enumerations/pull/33) ([PetarCurkovic](https://github.com/PetarCurkovic))
96 |
97 | ## [v2.2.3](https://github.com/infinum/enumerations/tree/v2.2.3) (2017-03-17)
98 | [Full Changelog](https://github.com/infinum/enumerations/compare/v2.2.2...v2.2.3)
99 |
100 | ## [v2.2.2](https://github.com/infinum/enumerations/tree/v2.2.2) (2017-03-01)
101 | [Full Changelog](https://github.com/infinum/enumerations/compare/v2.2.1...v2.2.2)
102 |
103 | **Implemented enhancements:**
104 |
105 | - Add id and quoted\_id methods [\#20](https://github.com/infinum/enumerations/issues/20)
106 |
107 | **Fixed bugs:**
108 |
109 | - Error when assiging nil [\#21](https://github.com/infinum/enumerations/issues/21)
110 |
111 | **Closed issues:**
112 |
113 | - upgrade to new codeclimate reporter [\#25](https://github.com/infinum/enumerations/issues/25)
114 | - Giving a invalid enum name raises an error [\#23](https://github.com/infinum/enumerations/issues/23)
115 | - Make second argument to Enumerations.value optional [\#22](https://github.com/infinum/enumerations/issues/22)
116 |
117 | **Merged pull requests:**
118 |
119 | - Make second argument to Enumerations.value optional [\#28](https://github.com/infinum/enumerations/pull/28) ([domagojnakic](https://github.com/domagojnakic))
120 | - Fix giving invalid enum name raises error [\#27](https://github.com/infinum/enumerations/pull/27) ([domagojnakic](https://github.com/domagojnakic))
121 | - Replaced CodeClimate::TestReporter with SimpleCov [\#26](https://github.com/infinum/enumerations/pull/26) ([PetarCurkovic](https://github.com/PetarCurkovic))
122 | - Add migration readme [\#24](https://github.com/infinum/enumerations/pull/24) ([Narayanan170](https://github.com/Narayanan170))
123 |
124 | ## [v2.2.1](https://github.com/infinum/enumerations/tree/v2.2.1) (2016-09-20)
125 | [Full Changelog](https://github.com/infinum/enumerations/compare/v2.2.0...v2.2.1)
126 |
127 | **Closed issues:**
128 |
129 | - Add option to store foreign\_key as string [\#14](https://github.com/infinum/enumerations/issues/14)
130 |
131 | **Merged pull requests:**
132 |
133 | - Bugfix/empty initializer [\#17](https://github.com/infinum/enumerations/pull/17) ([PetarCurkovic](https://github.com/PetarCurkovic))
134 |
135 | ## [v2.2.0](https://github.com/infinum/enumerations/tree/v2.2.0) (2016-09-19)
136 | [Full Changelog](https://github.com/infinum/enumerations/compare/v2.1.0...v2.2.0)
137 |
138 | **Merged pull requests:**
139 |
140 | - Feature/remove [\#16](https://github.com/infinum/enumerations/pull/16) ([PetarCurkovic](https://github.com/PetarCurkovic))
141 |
142 | ## [v2.1.0](https://github.com/infinum/enumerations/tree/v2.1.0) (2016-08-19)
143 | [Full Changelog](https://github.com/infinum/enumerations/compare/v2.0.0...v2.1.0)
144 |
145 | **Closed issues:**
146 |
147 | - Moved all back to Base [\#9](https://github.com/infinum/enumerations/issues/9)
148 | - I18n.locale [\#6](https://github.com/infinum/enumerations/issues/6)
149 | - Case statement [\#3](https://github.com/infinum/enumerations/issues/3)
150 |
151 | **Merged pull requests:**
152 |
153 | - Feature/where filtering [\#13](https://github.com/infinum/enumerations/pull/13) ([PetarCurkovic](https://github.com/PetarCurkovic))
154 | - Feature/localized attributes [\#12](https://github.com/infinum/enumerations/pull/12) ([PetarCurkovic](https://github.com/PetarCurkovic))
155 |
156 | ## [v2.0.0](https://github.com/infinum/enumerations/tree/v2.0.0) (2016-08-17)
157 | [Full Changelog](https://github.com/infinum/enumerations/compare/v1.3.0...v2.0.0)
158 |
159 | **Merged pull requests:**
160 |
161 | - Update version to 2.0.0 [\#11](https://github.com/infinum/enumerations/pull/11) ([PetarCurkovic](https://github.com/PetarCurkovic))
162 | - Refactor [\#10](https://github.com/infinum/enumerations/pull/10) ([PetarCurkovic](https://github.com/PetarCurkovic))
163 |
164 | ## [v1.3.0](https://github.com/infinum/enumerations/tree/v1.3.0) (2016-08-11)
165 | [Full Changelog](https://github.com/infinum/enumerations/compare/v1.1.1...v1.3.0)
166 |
167 | ## [v1.1.1](https://github.com/infinum/enumerations/tree/v1.1.1) (2016-06-07)
168 | [Full Changelog](https://github.com/infinum/enumerations/compare/v1.1.0...v1.1.1)
169 |
170 | **Merged pull requests:**
171 |
172 | - Bugfix/value-id [\#8](https://github.com/infinum/enumerations/pull/8) ([nikajukic](https://github.com/nikajukic))
173 | - Document configuration options [\#7](https://github.com/infinum/enumerations/pull/7) ([stankec](https://github.com/stankec))
174 | - Refactoring all around [\#5](https://github.com/infinum/enumerations/pull/5) ([janvarljen](https://github.com/janvarljen))
175 | - Added value method and support for custom attributes [\#4](https://github.com/infinum/enumerations/pull/4) ([PetarCurkovic](https://github.com/PetarCurkovic))
176 | - Some refactoring and updated README [\#2](https://github.com/infinum/enumerations/pull/2) ([PetarCurkovic](https://github.com/PetarCurkovic))
177 |
178 | ## [v1.1.0](https://github.com/infinum/enumerations/tree/v1.1.0) (2012-08-19)
179 | **Merged pull requests:**
180 |
181 | - Gemspec. [\#1](https://github.com/infinum/enumerations/pull/1) ([neektza](https://github.com/neektza))
182 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | gemspec
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Infinum
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/MIGRATE.md:
--------------------------------------------------------------------------------
1 | How to migrate from 1.x.x
2 | =========================
3 |
4 | Changes in the namespace
5 | ========================
6 |
7 | First off, start by renaming your namespaces, what used to be
8 |
9 | ```ruby
10 | class MyClass < Enumeration::Base
11 | ```
12 |
13 | will now be
14 |
15 | ```ruby
16 | class MyClass < Enumerations::Base
17 | ```
18 |
19 | Configuration
20 | =============
21 |
22 | Since v2 has a breaking change of default values in the database columns, you have 2 options:
23 |
24 | 1. Create an initializer that you will configure to have the old naming conventions
25 | 2. Create data migrations and convert your data to be alligned with the new Enumerations
26 |
27 | First option might be tempting, but you need to weigh in the advantages of the new enumerations.
28 | With the second option, you can get enumerations in the database instead of ID's, which may prove quite usefull.
29 |
30 | First way
31 | =========
32 |
33 | You need to create the following initializer
34 |
35 | ```ruby
36 | # config/initializers/enumerations.rb
37 |
38 | Enumerations.configure do |config|
39 | config.primary_key = :id
40 | config.foreign_key_suffix = :id
41 | end
42 | ```
43 |
44 | And voilà! You can carry on with work knowing your enumerations are up to date.
45 |
46 | Second way
47 | ==========
48 |
49 | You can use a gem for the migration, or just write raw SQL.
50 |
51 | Your migrations will look something like this:
52 |
53 | First to change the column types
54 | ```ruby
55 | class MoveMyEnumToNewEnumerationsColumns
56 | def up
57 | rename_column :my_table, :my_enum_id, :my_enum
58 | change_column :my_table, :my_enum, :string
59 | end
60 |
61 | def down
62 | change_column :my_table, :my_enum, 'integer USING CAST(my_enum AS integer)'
63 | rename_column :my_table, :my_enum, :my_enum_id
64 | end
65 | end
66 | ```
67 |
68 | And now for the actual data
69 |
70 | ```ruby
71 | class MoveMyEnumToNewEnumerations < ActiveRecord::Migration
72 | def up
73 | execute(<<-SQL
74 | UPDATE my_table
75 | SET my_enum =
76 | CASE my_enum
77 | WHEN '1' THEN 'first'
78 | WHEN '2' THEN 'second'
79 | WHEN '3' THEN 'third'
80 | END
81 | SQL
82 | )
83 | end
84 |
85 | def down
86 | execute(<<-SQL
87 | UPDATE my_table
88 | SET my_enum =
89 | CASE my_enum
90 | WHEN 'first' THEN '1'
91 | WHEN 'second' THEN '2'
92 | WHEN 'third' THEN '3'
93 | END
94 | SQL
95 | )
96 | end
97 | ```
98 |
99 | And that's it, you won't need an initializer for this to work anymore, because these are the default options Enumeration gem ships with.
100 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Enumerations
2 | ============
3 |
4 | [](https://badge.fury.io/rb/enumerations)
5 | [](https://codeclimate.com/github/infinum/enumerations/maintainability)
6 | [](https://codeclimate.com/github/infinum/enumerations/test_coverage)
7 | 
8 |
9 | Rails plugin for Enumerations in ActiveRecord models.
10 |
11 | Installation
12 | ============
13 |
14 | Inside your `Gemfile` add the following:
15 |
16 | ```ruby
17 | gem 'enumerations'
18 | ```
19 |
20 | Usage
21 | =====
22 |
23 | ## Defining enumerations
24 |
25 | Create a model for your enumerations:
26 |
27 | ```ruby
28 | class Status < Enumerations::Base
29 | values draft: { name: 'Draft' },
30 | review_pending: { name: 'Review pending' },
31 | published: { name: 'Published' }
32 | end
33 | ```
34 |
35 | Or you can use `value` method for defining your enumerations:
36 |
37 | ```ruby
38 | class Status < Enumerations::Base
39 | value :draft, name: 'Draft'
40 | value :review_pending, name: 'Review pending'
41 | value :published, name: 'Published'
42 | end
43 | ```
44 |
45 | Include enumerations for integer fields in other models:
46 |
47 | ```ruby
48 | class Post < ActiveRecord::Base
49 | enumeration :status
50 |
51 | validates :status, presence: true
52 | end
53 | ```
54 |
55 | You can pass attributes to specify which enumeration and which column to use:
56 |
57 | ```ruby
58 | class Post < ActiveRecord::Base
59 | enumeration :status,
60 | foreign_key: :post_status, # specifies which column to use
61 | class_name: Post::Status # specifies the class of the enumerator
62 |
63 | validates :post_status, presence: true
64 | end
65 | ```
66 | 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`.
67 |
68 |
69 |
70 | ## Setting enumeration value to objects
71 |
72 | Set enumerations:
73 |
74 | ```ruby
75 | @post = Post.first
76 | @post.status = Status.draft
77 | @post.save
78 | ```
79 |
80 | Or you can set enumerations by `symbol`:
81 |
82 | ```ruby
83 | @post.status = Status.find(:draft)
84 | ```
85 |
86 | > If you try to set value that is not an Enumeration value (except `nil`), you will get an `Enumerations::InvalidValueError` exception. You can turn this exception off in configuration.
87 |
88 | Also, you can set enumeration value like this:
89 |
90 | ```ruby
91 | @post.status_draft!
92 | ```
93 |
94 | > When you include enumerations into your model, you'll get methods for setting each enumeration value.
95 | Each method name is consists from enumeration name and enumeration value name with **!** at the end. Examples:
96 |
97 | ```ruby
98 | class Post < ActiveRecord::Base
99 | enumeration :status
100 | end
101 |
102 | @post.status_draft!
103 | ```
104 |
105 | ```ruby
106 | class User < ActiveRecord::Base
107 | enumeration :role
108 | end
109 |
110 | @user.role_admin!
111 | ```
112 |
113 | ```ruby
114 | class User < ActiveRecord::Base
115 | enumeration :type, class_name: Role
116 | end
117 |
118 | @user.type_editor!
119 | ```
120 |
121 |
122 |
123 | ## Finder methods
124 |
125 | Find enumerations by `id`:
126 |
127 | ```ruby
128 | @post.status = Status.find(2) # => Review pending
129 | @post.save
130 | ```
131 |
132 | Other finding methods:
133 |
134 | ```ruby
135 | # Find by key as a Symbol
136 | Status.find(:review_pending) # => Review pending
137 |
138 | # Find by key as a String
139 | Status.find('draft') # => Draft
140 |
141 | # Find by multiple attributes
142 | Status.find_by(name: 'None', visible: true) # => None
143 | ```
144 |
145 | Compare enumerations:
146 |
147 | ```ruby
148 | @post.status == :published # => true
149 | @post.status == 'published' # => true
150 | @post.status == Status.published # => true
151 | @post.status.published? # => true
152 | ```
153 |
154 | Get all enumerations:
155 |
156 | ```ruby
157 | Status.all
158 | ```
159 |
160 |
161 |
162 | ## Filtering methods
163 |
164 | Enumerations can be filtered with `where` method, similar to `ActiveRecord::QueryMethods#where`.
165 |
166 | ```ruby
167 | Role.where(admin: true) # => [Role.admin, Role.editor]
168 | Role.where(admin: true, active: true) # => [Role.admin]
169 | ```
170 |
171 |
172 |
173 | ## Scopes on model
174 |
175 | With enumerations, you'll get scope for each enumeration value in the
176 | following format:
177 |
178 | ```ruby
179 | with_#{enumeration_name}_#{enumeration_value_name}
180 | without_#{enumeration_name}_#{enumeration_value_name}
181 | ```
182 |
183 | or you can use the following scope and pass an array of enumerations:
184 |
185 | ```ruby
186 | with_#{enumeration_name}(enumeration, ...)
187 | without_#{enumeration_name}(enumeration, ...)
188 | ```
189 |
190 | Examples:
191 |
192 | ```ruby
193 | class Post < ActiveRecord::Base
194 | enumeration :status
195 | end
196 |
197 | Post.with_status_draft # => <#ActiveRecord::Relation []>
198 | Post.without_status_review_pending # => <#ActiveRecord::Relation []>
199 | Post.with_status(:draft) # => <#ActiveRecord::Relation []>
200 | Post.without_status(:draft) # => <#ActiveRecord::Relation []>
201 | Post.with_status(Status.draft) # => <#ActiveRecord::Relation []>
202 | Post.with_status(:draft, :review_pending) # => <#ActiveRecord::Relation []>
203 | Post.with_status(Status.draft, 'published') # => <#ActiveRecord::Relation []>
204 | Post.with_status([:draft, :review_pending]) # => <#ActiveRecord::Relation []>
205 | ```
206 |
207 | ```ruby
208 | class Post < ActiveRecord::Base
209 | enumeration :my_status, class_name: Status
210 | end
211 |
212 | Post.with_my_status_draft # => <#ActiveRecord::Relation []>
213 | Post.with_my_status_review_pending # => <#ActiveRecord::Relation []>
214 | Post.with_my_status(:draft) # => <#ActiveRecord::Relation []>
215 | Post.without_my_status(:draft) # => <#ActiveRecord::Relation []>
216 | ```
217 |
218 | Each scope returns all records with specified enumeration value.
219 |
220 |
221 |
222 | ## Forms usage
223 |
224 | Use in forms:
225 |
226 | ```ruby
227 | %p
228 | = f.label :status
229 | %br
230 | = f.collection_select :status, Status.all, :symbol, :name
231 | ```
232 |
233 |
234 |
235 | ## Validating input
236 |
237 | Enumerations will by default raise an exception if you try to set an invalid value. This prevents usage of validations, which you might want to add if you're developing an API and have to return meaningful errors to API clients.
238 |
239 | You can enable validations by first disabling error raising on invalid input (see [configuration](#configuration)). Then, you should add an inclusion validation to enumerated attributes:
240 | ```ruby
241 | class Post < ActiveRecord::Base
242 | enumeration :status
243 |
244 | validates :status, inclusion: { in: Status.all }
245 | end
246 | ```
247 |
248 | You'll now get an appropriate error message when you insert an invalid value:
249 | ```ruby
250 | > post = Post.new(status: 'invalid')
251 | > post.valid?
252 | => false
253 | > post.errors.full_messages.to_sentence
254 | => "Status is not included in the list"
255 | > post.status
256 | => "invalid"
257 | ```
258 |
259 | Advanced Usage
260 | =====
261 |
262 | Except `name` you can specify any other attributes to your enumerations:
263 |
264 | ```ruby
265 | class Status < Enumerations::Base
266 | value :draft, id: 1, name: 'Draft', published: false
267 | value :review_pending, id: 2, name: 'Review pending', description: 'Some description...'
268 | value :published, id: 3, name: 'Published', published: true
269 | value :other # passing no attributes is also allowed
270 | end
271 | ```
272 |
273 | Every enumeration has `id`, `name`, `description` and `published` methods.
274 | If you call method that is not in attribute list for enumeration, it will return `nil`.
275 |
276 | ```ruby
277 | Status.review_pending.description # => 'Some description...'
278 | Status.draft.description # => nil
279 | ```
280 |
281 | For each attribute, you have predicate method. Predicate methods are actually calling `present?`
282 | method on attribute value:
283 |
284 | ```ruby
285 | Status.draft.name? # => true
286 | Status.draft.published? # => false
287 | Status.published.published? # => true
288 | Status.other.name? # => false
289 | ```
290 |
291 | Translations
292 | =====
293 |
294 | **Enumerations** uses power of I18n API (if translate_attributes configuration is set to true) to enable you to create a locale file
295 | for enumerations like this:
296 |
297 | ```yaml
298 | ---
299 | en:
300 | enumerations:
301 | status:
302 | draft:
303 | name: Draft
304 | description: Article draft...
305 | ...
306 | role:
307 | admin:
308 | name: Administrator
309 | ```
310 |
311 | > You can separate enumerations locales into a separate `*.yml` files.
312 | > Then you should add locale file paths to I18n load path:
313 |
314 | ```ruby
315 | # config/initializers/locale.rb
316 |
317 | # Where the I18n library should search for translation files (e.g.):
318 | I18n.load_path += Dir[Rails.root.join('config', 'locales', 'enumerations', '*.yml')]
319 | ```
320 |
321 | Configuration
322 | =============
323 |
324 | Basically no configuration is needed.
325 |
326 | **Enumerations** has four configuration options.
327 | You can customize primary key, foreign key suffix, whether to translate attributes and whether to raise `Enumerations::InvalidValueError` exception when setting invalid values.
328 | Just add initializer file to `config/initializers/enumerations.rb`.
329 |
330 | Example of configuration:
331 |
332 | ```ruby
333 | # config/initializers/enumerations.rb
334 |
335 | Enumerations.configure do |config|
336 | config.primary_key = :id
337 | config.foreign_key_suffix = :id
338 | config.translate_attributes = true
339 | config.raise_invalid_value_error = true
340 | end
341 | ```
342 |
343 | By default, `primary_key` and `foreign_key_suffix` are set to `nil`.
344 |
345 | By default model enumeration value is saved to column with same name as enumeration.
346 | If you set enumeration as:
347 | ```ruby
348 | enumeration :status
349 | ```
350 | then model should have `status` column (as `String` type).
351 | If you want save an `ID` to this column, you can set `foreign_key_suffix` to `id`.
352 | Then model should have `status_id` column.
353 |
354 | If you set `primary_key` then you need provide this attribute for all enumerations values.
355 | Also, value from `primary_key` attribute will be stored to model as enumeration value.
356 |
357 | For example:
358 |
359 | ```ruby
360 | # with default configuration
361 |
362 | post = Post.new
363 | post.status = Status.draft # => post.status = 'draft'
364 |
365 | # with configured primary_key and foreign_key_suffix:
366 |
367 | Enumerations.configure do |config|
368 | config.primary_key = :id
369 | config.foreign_key_suffix = :id
370 | end
371 |
372 | class Status < Enumerations::Base
373 | value :draft, id: 1, name: 'Draft'
374 | value :review_pending, id: 2, name: 'Review pending',
375 | value :published, id: 3, name: 'Published', published: true
376 | end
377 |
378 | post = Post.new
379 | post.status = Status.draft # => post.status_id = 1
380 | ```
381 |
382 | If you want to configure primary key or foreign key suffix per enumeration class, you can use `primary_key=` and `foreign_key_suffix=` class method:
383 |
384 | ```ruby
385 | class Status < Enumerations::Base
386 | self.primary_key = :id
387 | self.foreign_key_suffix = :id
388 |
389 | value :draft, id: 1, name: 'Draft'
390 | value :review_pending, id: 2, name: 'Review pending'
391 | end
392 | ```
393 |
394 | Database Enumerations
395 | =====================
396 |
397 | By default, enumeration values are saved to database as `String`.
398 | If you want, you can define `Enum` type in database:
399 |
400 | ```sql
401 | CREATE TYPE status AS ENUM ('draft', 'review_pending', 'published');
402 | ```
403 |
404 | Then you'll have enumeration as type in database and you can use it in database migrations:
405 |
406 | ```ruby
407 | add_column :posts, :status, :status, index: true
408 | ```
409 |
410 | With configuration option `primary_key`, you can store any type you want (e.g. `Integer`).
411 |
412 | Also, for performance reasons, you should add indices to enumeration column.
413 |
414 | [Here](https://www.postgresql.org/docs/9.1/static/datatype-enum.html) you can find more informations about ENUM types.
415 |
416 |
417 |
418 | Contributing
419 | ============
420 |
421 | 1. Fork it
422 | 2. Create your feature branch (`git checkout -b my-new-feature`)
423 | 3. Commit your changes (`git commit -am 'Add some feature'`)
424 | 4. Push to the branch (`git push origin my-new-feature`)
425 | 5. Create new Pull Request
426 |
427 | Credits
428 | =======
429 | **Enumerations** is maintained and sponsored by [Infinum](https://infinum.co)
430 |
431 | Copyright © 2010 - 2018 Infinum Ltd.
432 |
433 | License
434 | =======
435 |
436 | The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
437 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require 'rake'
2 | require 'rake/testtask'
3 | require 'bundler/gem_tasks'
4 |
5 | desc 'Default: run unit tests.'
6 | task default: :test
7 |
8 | desc 'Run unit tests.'
9 | Rake::TestTask.new(:test) do |t|
10 | t.libs << 'lib'
11 | t.pattern = 'test/**/*_test.rb'
12 | t.verbose = true
13 | end
14 |
--------------------------------------------------------------------------------
/enumerations.gemspec:
--------------------------------------------------------------------------------
1 | require File.expand_path('../lib/enumerations/version', __FILE__)
2 |
3 | Gem::Specification.new do |s|
4 | s.name = 'enumerations'
5 | s.version = Enumerations::VERSION
6 | s.date = '2017-09-08'
7 | s.summary = 'Enumerations for ActiveRecord!'
8 | s.description = 'Extends ActiveRecord with enumeration capabilites.'
9 | s.homepage = 'https://github.com/infinum/enumerations'
10 | s.license = 'MIT'
11 |
12 | s.authors = [
13 | 'Tomislav Car', 'Nikica Jokić', 'Nikola Santić', 'Stjepan Hadjić', 'Petar Ćurković'
14 | ]
15 |
16 | s.email = [
17 | 'tomislav@infinum.hr', 'nikica.jokic@infinum.hr', 'nikola.santic@infinum.hr',
18 | 'stjepan.hadjic@infinum.hr', 'petar.curkovic@infinum.hr'
19 | ]
20 |
21 | s.add_dependency 'activerecord'
22 | s.add_dependency 'activesupport'
23 | s.add_dependency 'i18n'
24 | s.add_development_dependency 'pry-byebug'
25 | s.add_development_dependency 'rake'
26 | s.add_development_dependency 'simplecov'
27 | s.add_development_dependency 'sqlite3'
28 |
29 | s.files = `git ls-files`.split("\n")
30 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
31 | end
32 |
--------------------------------------------------------------------------------
/lib/enumerations.rb:
--------------------------------------------------------------------------------
1 | require 'active_support'
2 | require 'active_support/concern'
3 | require 'active_support/core_ext/class/attribute'
4 | require 'active_support/core_ext/string/inflections'
5 |
6 | require 'enumerations/configuration'
7 | require 'enumerations/version'
8 | require 'enumerations/base'
9 | require 'enumerations/reflection'
10 | require 'enumerations/errors'
11 |
12 | module Enumerations
13 | extend ActiveSupport::Concern
14 |
15 | included do
16 | class_attribute :_enumerations
17 | self._enumerations = []
18 | end
19 |
20 | module ClassMethods
21 | # Create an enumeration for the symbol name.
22 | # Options include foreign_key attribute and class_name
23 | #
24 | # Example:
25 | #
26 | # class User < ActiveRecord::Base
27 | # enumeration :role
28 | # end
29 | #
30 | # user.role => #
31 | #
32 | # user.role = Role.staff
33 | #
34 | def enumeration(name, options = {})
35 | reflection = Reflection.new(name, options)
36 |
37 | add_enumeration(reflection)
38 | end
39 |
40 | # Output all the enumerations that this model has defined
41 | # Returns an array of Reflection objects for all the
42 | # enumerations in the class.
43 | #
44 | # Example:
45 | #
46 | # User.reflect_on_all_enumerations => # [
47 | # #,
48 | # #
49 | # ]
50 | #
51 | def reflect_on_all_enumerations
52 | _enumerations
53 | end
54 |
55 | def fetch_foreign_key_values(reflection, *symbols)
56 | symbols.flatten.map do |symbol|
57 | enumeration_value = reflection.enumerator_class.find(symbol)
58 |
59 | enumeration_value &&
60 | enumeration_value.send(reflection.enumerator_class.primary_key || :symbol)
61 | end
62 | end
63 |
64 | private
65 |
66 | def add_enumeration(reflection)
67 | define_getter_method(reflection)
68 | define_setter_method(reflection)
69 | define_bang_methods(reflection)
70 | define_scopes_for_each_enumeration_value(reflection)
71 | define_enumeration_scope(reflection)
72 |
73 | self._enumerations += [reflection]
74 | end
75 |
76 | # Getter for belongs_to
77 | #
78 | # Example:
79 | #
80 | # user.role = Role.admin
81 | # user.role => #
82 | #
83 | def define_getter_method(reflection)
84 | define_method(reflection.name) do
85 | reflection.enumerator_class.find(self[reflection.foreign_key]) || self[reflection.foreign_key]
86 | end
87 | end
88 |
89 | # Setter for belongs_to
90 | #
91 | # Example:
92 | #
93 | # user.role = Role.admin
94 | #
95 | def define_setter_method(reflection)
96 | define_method("#{reflection.name}=") do |other|
97 | enumeration_value = reflection.enumerator_class.find(other)
98 |
99 | if other.present? && enumeration_value.nil?
100 | raise Enumerations::InvalidValueError if Enumerations.configuration.raise_invalid_value_error
101 |
102 | self[reflection.foreign_key] = other
103 | else
104 | self[reflection.foreign_key] =
105 | enumeration_value &&
106 | enumeration_value.send(reflection.enumerator_class.primary_key || :symbol)
107 | end
108 | end
109 | end
110 |
111 | # Add bang methods for setting all enumeration values.
112 | # All methods are prefixed with enumeration name.
113 | #
114 | # Example:
115 | #
116 | # user.role_admin!
117 | # user.role => #
118 | #
119 | def define_bang_methods(reflection)
120 | reflection.enumerator_class.all.each do |enumeration|
121 | define_method("#{reflection.name}_#{enumeration.to_sym}!") do
122 | send("#{reflection.name}=", enumeration)
123 | end
124 | end
125 | end
126 |
127 | # Scopes for enumerated ActiveRecord model.
128 | # Format of scope name is with_#{enumeration_name}_#{enumeration_value_name}.
129 | #
130 | # Example:
131 | #
132 | # User.with_role_admin => <#ActiveRecord::Relation []>
133 | # User.with_role_editor => <#ActiveRecord::Relation []>
134 | #
135 | def define_scopes_for_each_enumeration_value(reflection)
136 | reflection.enumerator_class.all.each do |enumeration|
137 | foreign_key = enumeration.send(reflection.enumerator_class.primary_key || :symbol)
138 |
139 | scope("with_#{reflection.name}_#{enumeration.symbol}",
140 | -> { where(reflection.foreign_key => foreign_key) })
141 |
142 | scope("without_#{reflection.name}_#{enumeration.symbol}",
143 | -> { where.not(reflection.foreign_key => foreign_key) })
144 | end
145 | end
146 |
147 | # Scope for enumerated ActiveRecord model.
148 | # Format of scope name is with_#{enumeration_name}(*enumerations).
149 | #
150 | # Example:
151 | #
152 | # User.with_role(:admin) => <#ActiveRecord::Relation []>
153 | # User.with_role(:admin, Role.editor) => <#ActiveRecord::Relation []>
154 | #
155 | def define_enumeration_scope(reflection)
156 | scope("with_#{reflection.name}",
157 | lambda do |*symbols|
158 | where(reflection.foreign_key => fetch_foreign_key_values(reflection, symbols))
159 | end)
160 |
161 | scope("without_#{reflection.name}",
162 | lambda do |*symbols|
163 | where.not(reflection.foreign_key => fetch_foreign_key_values(reflection, symbols))
164 | end)
165 | end
166 | end
167 | end
168 |
169 | # Extend ActiveRecord with Enumeration capabilites
170 | ActiveSupport.on_load(:active_record) do
171 | include Enumerations
172 | end
173 |
--------------------------------------------------------------------------------
/lib/enumerations/base.rb:
--------------------------------------------------------------------------------
1 | require 'active_support/core_ext/class/attribute'
2 | require 'active_support/core_ext/string/inflections'
3 | require 'enumerations/value'
4 | require 'enumerations/finder_methods'
5 |
6 | module Enumerations
7 | class Base < ActiveSupport::Multibyte::Chars
8 | extend Enumerations::FinderMethods
9 | include Enumerations::Value
10 |
11 | class_attribute :_values, :_symbol_index, :_primary_key, :_foreign_key_suffix
12 |
13 | self._values = {}
14 | self._symbol_index = {}
15 | self._primary_key = nil
16 | self._foreign_key_suffix = nil
17 |
18 | # Adding new value to enumeration
19 | #
20 | # Example:
21 | #
22 | # value :admin, id: 1, name: 'Admin', description: 'Some description...'
23 | #
24 | # Role.find(:admin).name => # "Admin"
25 | # Role.find(:admin).description => # "Some description..."
26 | #
27 | def self.value(symbol, attributes = {})
28 | validate_symbol_and_primary_key(symbol, attributes)
29 |
30 | self._values = _values.merge(symbol => new(symbol, attributes))
31 |
32 | # Adds name base finder methods
33 | #
34 | # Example:
35 | #
36 | # Role.admin => #
37 | # Role.staff => #
38 | #
39 | singleton_class.send(:define_method, symbol) do
40 | find(symbol)
41 | end
42 | end
43 |
44 | # Adding multiple values to enumeration
45 | #
46 | # Example:
47 | #
48 | # values admin: { id: 1, name: 'Admin' },
49 | # manager: { id: 2, name: 'Manager' },
50 | # staff: { id: 3, name: 'Staff', description: 'Some description...' }
51 | #
52 | # Role.admin.id => # 1
53 | # Role.find(:manager).name => # "Manager"
54 | # Role.find(:manager).description => # "Some description..."
55 | #
56 | def self.values(values)
57 | values.each do |symbol, attributes|
58 | value(symbol, attributes)
59 | end
60 | end
61 |
62 | # Returns an array of all enumeration symbols
63 | #
64 | # Example:
65 | #
66 | # Role.symbols => # [:admin, :manager, :staff]
67 | #
68 | def self.symbols
69 | _values.keys
70 | end
71 |
72 | # Returns an array of all enumeration values
73 | #
74 | # Example:
75 | #
76 | # Role.all => # [#,
77 | # #,
78 | # #]
79 | #
80 | def self.all
81 | _values.values
82 | end
83 |
84 | # Sets primary key for enumeration class.
85 | #
86 | # Example:
87 | #
88 | # class Status < Enumeration::Base
89 | # primary_key = :id
90 | # end
91 | #
92 | def self.primary_key=(key)
93 | self._primary_key = key && key.to_sym
94 | end
95 |
96 | # Gets primary key for enumeration class.
97 | #
98 | # Example:
99 | #
100 | # Status.primary_key => # nil
101 | #
102 | # class Status < Enumeration::Base
103 | # primary_key = :id
104 | # end
105 | #
106 | # Status.primary_key => # :id
107 | #
108 | def self.primary_key
109 | _primary_key || Enumerations.configuration.primary_key
110 | end
111 |
112 | # Sets foreign key suffix for enumeration class.
113 | #
114 | # Example:
115 | #
116 | # class Status < Enumeration::Base
117 | # foreign_key_suffix = :id
118 | # end
119 |
120 | def self.foreign_key_suffix=(suffix)
121 | self._foreign_key_suffix = suffix && suffix.to_sym
122 | end
123 |
124 | # Gets foreign key suffix for enumeration class.
125 | #
126 | # Example:
127 | #
128 | # Status.foreign_key_suffix => # nil
129 | #
130 | # class Status < Enumeration::Base
131 | # foreign_key_suffix = :id
132 | # end
133 | #
134 | # Status.foreign_key_suffix => # :id
135 | #
136 | def self.foreign_key_suffix
137 | _foreign_key_suffix || Enumerations.configuration.foreign_key_suffix
138 | end
139 |
140 | def self.validate_symbol_and_primary_key(symbol, attributes)
141 | raise Enumerations::DuplicatedSymbolError if find(symbol)
142 |
143 | return if primary_key.nil?
144 |
145 | primary_key_value = attributes[primary_key]
146 |
147 | raise Enumerations::MissingPrimaryKeyError if primary_key_value.nil?
148 | raise Enumerations::DuplicatedPrimaryKeyError if find(primary_key_value)
149 |
150 | self._symbol_index = _symbol_index.merge(symbol => primary_key_value)
151 | end
152 |
153 | private_class_method :validate_symbol_and_primary_key
154 |
155 | attr_reader :symbol, :attributes
156 |
157 | def initialize(symbol, attributes)
158 | super(symbol.to_s)
159 |
160 | @symbol = symbol
161 | @attributes = attributes
162 |
163 | create_instance_methods
164 | end
165 |
166 | private
167 |
168 | def chars(string)
169 | string
170 | end
171 | end
172 | end
173 |
--------------------------------------------------------------------------------
/lib/enumerations/configuration.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Enumerations
4 | def self.configuration
5 | @configuration ||= Configuration.new
6 | end
7 |
8 | def self.configure
9 | yield(configuration)
10 | end
11 |
12 | def self.restore_default_configuration
13 | @configuration = nil
14 | end
15 |
16 | class Configuration
17 | attr_accessor :primary_key
18 | attr_accessor :foreign_key_suffix
19 | attr_accessor :translate_attributes
20 | attr_accessor :raise_invalid_value_error
21 |
22 | def initialize
23 | @primary_key = nil
24 | @foreign_key_suffix = nil
25 | @translate_attributes = true
26 | @raise_invalid_value_error = true
27 | end
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/lib/enumerations/errors.rb:
--------------------------------------------------------------------------------
1 | module Enumerations
2 | class Error < StandardError; end
3 | class DuplicatedSymbolError < Error; end
4 | class MissingPrimaryKeyError < Error; end
5 | class DuplicatedPrimaryKeyError < Error; end
6 | class InvalidValueError < Error; end
7 | end
8 |
--------------------------------------------------------------------------------
/lib/enumerations/finder_methods.rb:
--------------------------------------------------------------------------------
1 | module Enumerations
2 | module FinderMethods
3 | # Finds an enumeration by symbol or name
4 | #
5 | # Example:
6 | #
7 | # Role.find(:admin) => #
8 | # Role.find('staff') => #
9 | #
10 | def find(key)
11 | case key
12 | when Symbol, String, Enumerations::Base then find_by_key(key.to_sym)
13 | end || find_by_primary_key(key)
14 | end
15 |
16 | # Finds all enumerations which meets given attributes.
17 | # Similar to ActiveRecord::QueryMethods#where.
18 | #
19 | # Example:
20 | #
21 | # Role.find_by(name: 'Admin') => #
22 | #
23 | def where(**args)
24 | _values.values.select { |value| args.map { |k, v| value.attributes[k] == v }.all? }
25 | end
26 |
27 | # Finds an enumeration by defined attribute. Similar to ActiveRecord::FinderMethods#find_by
28 | #
29 | # Example:
30 | #
31 | # Role.find_by(name: 'Admin') => #
32 | #
33 | def find_by(**args)
34 | where(**args).first
35 | end
36 |
37 | def find_by_key(key)
38 | _values[key]
39 | end
40 |
41 | def find_by_primary_key(primary_key)
42 | return value_from_symbol_index(primary_key.to_i) if primary_key.is_a?(String)
43 |
44 | value_from_symbol_index(primary_key)
45 | end
46 |
47 | private
48 |
49 | def value_from_symbol_index(key)
50 | _values[_symbol_index.key(key)]
51 | end
52 | end
53 | end
54 |
--------------------------------------------------------------------------------
/lib/enumerations/reflection.rb:
--------------------------------------------------------------------------------
1 | module Enumerations
2 | class Reflection
3 | attr_reader :name, :options
4 |
5 | def initialize(name, options = {})
6 | @name = name
7 | @options = options
8 | end
9 |
10 | def class_name
11 | @class_name ||= (options[:class_name] || name).to_s.camelize
12 | end
13 |
14 | def foreign_key
15 | @foreign_key ||= (options[:foreign_key] || default_foreign_key_name).to_sym
16 | end
17 |
18 | def enumerator_class
19 | @enumerator_class ||= class_name.constantize
20 | end
21 |
22 | private
23 |
24 | def default_foreign_key_name
25 | [name, enumerator_class.foreign_key_suffix].compact.join('_')
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/lib/enumerations/value.rb:
--------------------------------------------------------------------------------
1 | module Enumerations
2 | module Value
3 | def to_i
4 | _values.keys.index(symbol) + 1
5 | end
6 |
7 | def to_s
8 | symbol.to_s
9 | end
10 |
11 | def to_param
12 | to_s
13 | end
14 |
15 | def to_sym
16 | symbol
17 | end
18 |
19 | # Comparison by symbol or object
20 | #
21 | # Example:
22 | #
23 | # Role.admin == :admin => true
24 | # Role.admin == Role.admin => true
25 | # Role.admin == :staff => false
26 | # Role.admin == Role.staff => false
27 | #
28 | # TODO: test if case..when is working with this
29 | def ==(other)
30 | case other
31 | when String then other == to_s
32 | when Symbol then other == symbol
33 | else super
34 | end
35 | end
36 |
37 | private
38 |
39 | def create_instance_methods
40 | define_attributes_getters
41 | define_value_checking_method
42 | define_predicate_methods_for_attributes
43 | end
44 |
45 | # Getters for all attributes
46 | #
47 | # Example:
48 | #
49 | # Role.admin => #1, :name=>"Admin", :description=>"Some description..."}>
51 | # user.role.id => # 1
52 | # user.role.name => # "Admin"
53 | # user.role.description => # "Some description..."
54 | #
55 | def define_attributes_getters
56 | @attributes.each do |key, _|
57 | next if self.class.method_defined?(key)
58 |
59 | self.class.send :define_method, key do |locale: I18n.locale|
60 | case @attributes[key]
61 | when String, Symbol then translate_attribute(key, locale)
62 | else @attributes[key]
63 | end
64 | end
65 | end
66 | end
67 |
68 | def translate_attribute(key, locale)
69 | return @attributes[key].to_s unless Enumerations.configuration.translate_attributes
70 |
71 | I18n.t(key, scope: [:enumerations, self.class.name.demodulize.underscore, symbol],
72 | default: @attributes[key].to_s,
73 | locale: locale)
74 | end
75 |
76 | # Predicate methods for values
77 | #
78 | # Example:
79 | #
80 | # user.role = Role.admin
81 | # user.role.admin? => # true
82 | # user.role.staff? => # false
83 | #
84 | def define_value_checking_method
85 | self.class.send :define_method, "#{symbol}?" do
86 | __callee__[0..-2].to_sym == symbol
87 | end
88 | end
89 |
90 | # Predicate methods for all attributes
91 | #
92 | # Example:
93 | #
94 | # class Role < Enumerations::Base
95 | # value :admin, name: 'Administrator', active: false
96 | # end
97 | #
98 | # user.role.name? => # true
99 | # user.role.admin? => # false
100 | #
101 | def define_predicate_methods_for_attributes
102 | @attributes.each do |key, _|
103 | method_name = "#{key}?"
104 |
105 | next if self.class.method_defined?(method_name.to_sym)
106 |
107 | self.class.send :define_method, method_name do
108 | @attributes[key].present?
109 | end
110 | end
111 | end
112 | end
113 | end
114 |
--------------------------------------------------------------------------------
/lib/enumerations/version.rb:
--------------------------------------------------------------------------------
1 | module Enumerations
2 | VERSION = '2.6.0'.freeze
3 | end
4 |
--------------------------------------------------------------------------------
/test/base_test.rb:
--------------------------------------------------------------------------------
1 | require_relative 'helpers/test_helper'
2 |
3 | class BaseTest < Minitest::Test
4 | def test_all
5 | statuses = Status.all
6 |
7 | assert_equal 5, statuses.size
8 | assert_equal statuses.first, Status.draft
9 | end
10 |
11 | def test_symbols
12 | status_symbols = Status.symbols
13 |
14 | assert_equal 5, status_symbols.size
15 | assert_equal status_symbols.first, Status.draft.to_sym
16 | end
17 |
18 | def test_duplicated_symbol
19 | assert_raises Enumerations::DuplicatedSymbolError do
20 | obj = Class.new(Enumerations::Base)
21 |
22 | obj.value :draft, id: 1, name: 'Draft'
23 | obj.value :draft, id: 2, name: 'Draft Again'
24 | end
25 | end
26 |
27 | def test_all_has_custom_attributes
28 | statuses = Status.all
29 |
30 | assert_silent do
31 | statuses.map(&:visible)
32 | statuses.map(&:deleted)
33 | end
34 | end
35 |
36 | def test_enumerations_custom_instance_method
37 | role = Role.find(:admin)
38 |
39 | assert_equal 'user_Admin', role.my_custom_name
40 | end
41 |
42 | def test_all_enumerations_has_custom_instance_methods
43 | roles = Role.all
44 |
45 | assert_silent do
46 | roles.map(&:my_custom_name)
47 | end
48 | end
49 | end
50 |
--------------------------------------------------------------------------------
/test/configuration/configuration.rb:
--------------------------------------------------------------------------------
1 | module Configuration
2 | Enumerations.configure do |config|
3 | config.primary_key = :id
4 | config.foreign_key_suffix = :id
5 | end
6 |
7 | ::CustomEnum = Class.new(Enumerations::Base)
8 |
9 | CustomEnum.values draft: { id: 1, name: 'Draft' },
10 | review: { id: 2, name: 'Review' },
11 | published: { id: 3, name: 'Published', published: true }
12 |
13 | ::CustomModel = Class.new(ActiveRecord::Base)
14 |
15 | CustomModel.enumeration :custom_enum
16 |
17 | Enumerations.restore_default_configuration
18 | end
19 |
--------------------------------------------------------------------------------
/test/configuration/custom_configuration_test.rb:
--------------------------------------------------------------------------------
1 | module Configuration
2 | ::CustomConfigurationEnum = Class.new(Enumerations::Base)
3 |
4 | CustomConfigurationEnum.primary_key = :id
5 | CustomConfigurationEnum.value :draft, id: 1
6 |
7 | ::CustomConfigurationModel = Class.new(ActiveRecord::Base)
8 | CustomConfigurationModel.table_name = :custom_models
9 | CustomConfigurationModel.enumeration :custom_configuration_enum, foreign_key: :custom_enum_id
10 |
11 | class CustomConfigurationTest < Minitest::Test
12 | def test_primary_key_value
13 | assert_equal :id, CustomConfigurationEnum.primary_key
14 | end
15 |
16 | def test_primary_key_value_is_not_set_in_configuration
17 | assert_nil Enumerations.configuration.primary_key
18 | end
19 |
20 | def test_value_set_for_custom_foreign_key_config
21 | model = CustomConfigurationModel.new
22 | model.custom_configuration_enum = :draft
23 |
24 | assert_equal :draft, model.custom_configuration_enum.symbol
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/test/configuration/enumerations_test.rb:
--------------------------------------------------------------------------------
1 | require_relative '../helpers/test_helper'
2 | require_relative 'configuration'
3 |
4 | module Configuration
5 | class EnumerationsTest < Minitest::Test
6 | def setup
7 | Enumerations.configure do |config|
8 | config.primary_key = :id
9 | config.foreign_key_suffix = :id
10 | end
11 | end
12 |
13 | def teardown
14 | Enumerations.restore_default_configuration
15 | end
16 |
17 | def test_model_enumeration_assignment
18 | model = CustomModel.new
19 | model.custom_enum = CustomEnum.draft
20 |
21 | assert_equal 'draft', model.custom_enum.to_s
22 | end
23 |
24 | def test_model_bang_assignment
25 | model = CustomModel.new
26 | model.custom_enum_draft!
27 |
28 | assert_equal 'draft', model.custom_enum.to_s
29 | end
30 |
31 | def test_model_via_symbol_assignment
32 | model = CustomModel.new
33 | model.custom_enum = CustomEnum.published.symbol
34 |
35 | assert_equal 'published', model.custom_enum.to_s
36 | end
37 |
38 | def test_model_via_foreign_key_assignment
39 | model = CustomModel.new
40 | model.custom_enum_id = CustomEnum.published.id
41 |
42 | assert_equal 'published', model.custom_enum.to_s
43 | end
44 |
45 | def test_enumerated_class_scope_hash_value
46 | query_hash = CustomModel.with_custom_enum_draft.where_values_hash.symbolize_keys
47 |
48 | assert_equal query_hash, custom_enum_id: 1
49 | end
50 |
51 | def test_enumerations_overrides
52 | assert_equal :id, OverridableStatus.primary_key
53 | assert_equal :id, OverridableStatus.foreign_key_suffix
54 |
55 | model = OverridableModel.new
56 | model.overridable_status = OverridableStatus.draft
57 |
58 | assert_equal 1, model.overridable_status_id
59 | end
60 | end
61 | end
62 |
--------------------------------------------------------------------------------
/test/configuration/finder_test.rb:
--------------------------------------------------------------------------------
1 | require_relative '../helpers/test_helper'
2 | require_relative 'configuration'
3 |
4 | module Configuration
5 | class FinderTest < Minitest::Test
6 | def setup
7 | Enumerations.configure do |config|
8 | config.primary_key = :id
9 | config.foreign_key_suffix = :id
10 | end
11 | end
12 |
13 | def teardown
14 | Enumerations.restore_default_configuration
15 | end
16 |
17 | def test_lookup_by_key
18 | enum = CustomEnum.find(:draft)
19 |
20 | assert_equal :draft, enum.symbol
21 | end
22 |
23 | def test_lookup_by_string_key
24 | enum = CustomEnum.find('draft')
25 |
26 | assert_equal :draft, enum.symbol
27 | end
28 |
29 | def test_lookup_by_primary_key
30 | enum = CustomEnum.find(1)
31 |
32 | assert_equal :draft, enum.symbol
33 | end
34 |
35 | def test_lookup_by_primary_key_as_string
36 | enum = CustomEnum.find('1')
37 |
38 | assert_equal :draft, enum.symbol
39 | end
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/test/configuration_test.rb:
--------------------------------------------------------------------------------
1 | require_relative 'helpers/test_helper'
2 |
3 | class ConfigurationTest < Minitest::Test
4 | def setup
5 | Enumerations.restore_default_configuration
6 | end
7 |
8 | def teardown
9 | Enumerations.restore_default_configuration
10 | end
11 |
12 | def test_default_configuration
13 | assert_nil Enumerations.configuration.primary_key
14 | assert_nil Enumerations.configuration.foreign_key_suffix
15 | end
16 |
17 | def test_custom_configuration
18 | Enumerations.configure do |config|
19 | config.primary_key = :id
20 | config.foreign_key_suffix = :id
21 | end
22 |
23 | assert_equal :id, Enumerations.configuration.primary_key
24 | assert_equal :id, Enumerations.configuration.foreign_key_suffix
25 | end
26 |
27 | def test_reflection_with_configured_foreign_key_suffix
28 | Enumerations.configure { |config| config.foreign_key_suffix = :id }
29 |
30 | reflection = Enumerations::Reflection.new(:status)
31 |
32 | assert_equal 'Status', reflection.class_name
33 | assert_equal ::Status, reflection.enumerator_class
34 | assert_equal :status_id, reflection.foreign_key
35 | end
36 |
37 | def test_required_primary_key_when_primary_key_configured
38 | Enumerations.configure { |config| config.primary_key = :id }
39 |
40 | assert_raises Enumerations::MissingPrimaryKeyError, 'Enumeration primary key is required' do
41 | Class.new(Enumerations::Base).value :draft, name: 'Draft'
42 | end
43 | end
44 |
45 | def test_duplicated_primary_key_when_primary_key_configured
46 | Enumerations.configure { |config| config.primary_key = :id }
47 |
48 | assert_raises Enumerations::DuplicatedPrimaryKeyError, 'Duplicate primary key 1' do
49 | Class.new(Enumerations::Base).values draft: { id: 1, name: 'Draft' },
50 | test: { id: 1, name: 'Draft' }
51 | end
52 | end
53 | end
54 |
--------------------------------------------------------------------------------
/test/enumerations_test.rb:
--------------------------------------------------------------------------------
1 | require_relative 'helpers/test_helper'
2 | require 'enumerations/errors'
3 |
4 | class EnumerationsTest < Minitest::Test
5 | def test_reflect_on_all_enumerations
6 | enumerations = Post.reflect_on_all_enumerations
7 |
8 | assert_equal 2, enumerations.size
9 | assert_equal :status, enumerations.first.name
10 | assert_equal 'Status', enumerations.first.class_name
11 |
12 | assert_equal :some_other_status, enumerations[1].foreign_key
13 | end
14 |
15 | def test_model_enumeration_assignment
16 | p = Post.new
17 | p.status = Status.draft
18 |
19 | assert_equal 'draft', p.status.to_s
20 | end
21 |
22 | def test_model_bang_assignment
23 | p = Post.new
24 | p.status_draft!
25 |
26 | assert_equal 'draft', p.status.to_s
27 | end
28 |
29 | def test_model_uniqueness_validation
30 | p = Post.new(status: :draft)
31 |
32 | assert_equal true, p.valid?
33 | end
34 |
35 | def test_model_bang_assignment_with_custom_name
36 | p = Post.new
37 | p.different_status_draft!
38 |
39 | assert_equal 'draft', p.different_status.to_s
40 | end
41 |
42 | def test_model_via_symbol_assignment
43 | p = Post.new
44 | p.some_other_status = Status.published.symbol
45 |
46 | assert_equal 'published', p.some_other_status.to_s
47 | end
48 |
49 | def test_boolean_lookup
50 | p = Post.new
51 | p.status = Status.draft
52 |
53 | assert_equal true, p.status.draft?
54 | end
55 |
56 | def test_false_boolean_lookup
57 | p = Post.new
58 | p.status = Status.draft
59 |
60 | assert_equal false, p.status.published?
61 | end
62 |
63 | def test_multiple_enumerations_on_model
64 | enumerations = User.reflect_on_all_enumerations
65 |
66 | assert_equal 2, enumerations.size
67 |
68 | assert_equal :role, enumerations.first.name
69 | assert_equal :role, enumerations.first.foreign_key
70 |
71 | assert_equal :status, enumerations[1].name
72 | assert_equal :status, enumerations[1].foreign_key
73 | end
74 |
75 | def test_multiple_enumeration_assignments_on_model
76 | u = User.new
77 | u.role = Role.admin
78 | u.status = Status.published
79 |
80 | assert_equal 'admin', u.role.to_s
81 | assert_equal 'published', u.status.to_s
82 | end
83 |
84 | def test_enumerated_class_has_enumeration_scope
85 | assert_respond_to User, :with_role
86 | end
87 |
88 | def test_enumerated_class_has_scopes
89 | Role.all.each do |role|
90 | assert_respond_to User, ['with_role', role.to_s].join('_').to_sym
91 | end
92 | end
93 |
94 | def test_enumerated_class_enumeration_scope_hash_value
95 | query_hash = User.with_role(:admin).where_values_hash.symbolize_keys
96 |
97 | assert_equal query_hash, role: :admin
98 | end
99 |
100 | def test_enumerated_class_enumeration_without_scope_results
101 | User.create(role: :admin)
102 | User.create(role: :editor)
103 |
104 | results = User.without_role(:admin)
105 |
106 | assert_equal results, User.where.not(role: :admin)
107 | end
108 |
109 | def test_enumerated_class_enumeration_scope_hash_value_for_multiple_enums
110 | query_hash = User.with_role(:admin, Role.author, 'editor').where_values_hash.symbolize_keys
111 |
112 | assert_equal query_hash, role: [:admin, :author, :editor]
113 | end
114 |
115 | def test_enumerated_class_enumeration_scope_hash_value_for_multiple_enums_as_array
116 | query_hash = User.with_role([:admin, Role.author, 'editor']).where_values_hash.symbolize_keys
117 |
118 | assert_equal query_hash, role: [:admin, :author, :editor]
119 | end
120 |
121 | def test_enumerated_class_without_scope_hash_value
122 | query_hash = User.with_role_admin.where_values_hash.symbolize_keys
123 |
124 | assert_equal query_hash, role: :admin
125 | end
126 |
127 | def test_enumerated_class_without_scope_results
128 | User.create(role: :admin)
129 | User.create(role: :editor)
130 |
131 | results = User.without_role_admin
132 |
133 | assert_equal results, User.where.not(role: :admin)
134 | end
135 |
136 | def test_nonexistent_value_assignment
137 | assert_raises Enumerations::InvalidValueError do
138 | User.new(role: :nonexistent_value)
139 | end
140 | end
141 |
142 | def test_nonexistent_value_assignment_with_error_raising_turned_off
143 | Enumerations.configuration.raise_invalid_value_error = false
144 |
145 | user = User.new(role: :nonexistent_value)
146 |
147 | assert_equal user.role, 'nonexistent_value'
148 |
149 | user.role = :other_nonexistent_value
150 |
151 | assert_equal user.role, 'other_nonexistent_value'
152 |
153 | Enumerations.configuration.raise_invalid_value_error = true
154 | end
155 |
156 | def test_on_nil_value_assignment
157 | user = User.new(role: nil)
158 | assert_nil user.role
159 | end
160 | end
161 |
--------------------------------------------------------------------------------
/test/finder_test.rb:
--------------------------------------------------------------------------------
1 | require_relative 'helpers/test_helper'
2 |
3 | class FinderTest < Minitest::Test
4 | def test_lookup_by_symbol
5 | status = Status.find(:draft)
6 |
7 | assert_equal :draft, status.symbol
8 | end
9 |
10 | def test_lookup_fail_by_symbol
11 | status = Status.find(:draft)
12 |
13 | refute_same :published, status.symbol
14 | end
15 |
16 | def test_lookup_by_string_key
17 | status = Status.find('draft')
18 |
19 | assert_equal :draft, status.symbol
20 | end
21 |
22 | def test_find_by
23 | status = Status.find_by(name: 'Draft')
24 |
25 | assert_equal :draft, status.symbol
26 | end
27 |
28 | def test_fail_find_by
29 | status = Status.find_by(name: 'Draft1')
30 |
31 | assert_nil status
32 | end
33 |
34 | def test_where_by_name
35 | statuses = Status.where(name: 'Draft')
36 |
37 | assert_equal [Status.find(:draft)], statuses
38 | end
39 |
40 | def test_where_by_name_with_no_results
41 | statuses = Status.where(name: 'Draft1')
42 |
43 | assert_equal [], statuses
44 | end
45 |
46 | def test_where_by_custom_attributes
47 | roles = Role.where(admin: true)
48 |
49 | assert_equal 2, roles.count
50 | assert_equal [:admin, :editor], roles.map(&:symbol)
51 | end
52 |
53 | def test_where_by_multiple_custom_attributes
54 | roles = Role.where(admin: true, active: true)
55 |
56 | assert_equal 1, roles.count
57 | assert_equal [:admin], roles.map(&:symbol)
58 | end
59 |
60 | def test_where_by_undefined_custom_attributes
61 | roles = Role.where(description1: false)
62 |
63 | assert_equal [], roles
64 | end
65 | end
66 |
--------------------------------------------------------------------------------
/test/helpers/database_helper.rb:
--------------------------------------------------------------------------------
1 | require 'logger'
2 |
3 | ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
4 |
5 | ActiveRecord::Schema.define do
6 | create_table :posts, force: true do |t|
7 | t.string :status
8 | t.string :some_other_status
9 | end
10 |
11 | create_table :users, force: true do |t|
12 | t.string :role
13 | t.string :status
14 | end
15 |
16 | create_table :custom_models, force: true do |t|
17 | t.integer :custom_enum_id
18 | t.integer :custom_enum
19 | end
20 |
21 | create_table :overridable_models, force: true do |t|
22 | t.integer :overridable_status_id
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/test/helpers/locale_helper.rb:
--------------------------------------------------------------------------------
1 | require 'i18n'
2 |
3 | I18n.available_locales = [:en, :hr, :fr]
4 | I18n.load_path = Dir[File.join('test', 'locales', '*.yml')]
5 |
--------------------------------------------------------------------------------
/test/helpers/test_helper.rb:
--------------------------------------------------------------------------------
1 | require 'simplecov'
2 | SimpleCov.start
3 |
4 | require 'minitest/autorun'
5 | require 'enumerations'
6 | require 'active_record'
7 | require 'pry'
8 |
9 | require_relative 'database_helper'
10 | require_relative 'locale_helper'
11 |
12 | class Status < Enumerations::Base
13 | values draft: { id: 1, name: 'Draft' },
14 | review_pending: { id: 2, name: 'Review pending' },
15 | published: { id: 3, name: 'Published' }
16 |
17 | value :none, id: 4, name: 'None', visible: true, deleted: false
18 | value :deleted, id: 5, deleted: true
19 | end
20 |
21 | class Role < Enumerations::Base
22 | value :admin, name: 'Admin', admin: true, active: true
23 | value :editor, name: 'Editor', admin: true, active: false, description: 'Edits newspapers'
24 | value :author, name: 'Author'
25 | value :lecturer, type: :croatist
26 |
27 | def my_custom_name
28 | ['user', name].join('_')
29 | end
30 | end
31 |
32 | class Post < ActiveRecord::Base
33 | enumeration :status
34 | enumeration :different_status, foreign_key: :some_other_status, class_name: 'Status'
35 |
36 | validates :status, uniqueness: true
37 | end
38 |
39 | class User < ActiveRecord::Base
40 | enumeration :role
41 | enumeration :status
42 | end
43 |
44 | class OverridableStatus < Enumerations::Base
45 | self.primary_key = :id
46 | self.foreign_key_suffix = :id
47 |
48 | value :draft, id: 1
49 | end
50 |
51 | class OverridableModel < ActiveRecord::Base
52 | enumeration :overridable_status
53 | end
54 |
--------------------------------------------------------------------------------
/test/locales/hr.yml:
--------------------------------------------------------------------------------
1 | ---
2 | hr:
3 | enumerations:
4 | status:
5 | draft:
6 | name: Nacrt
7 | review_pending:
8 | name: Čeka pregled
9 | published:
10 | name: Published
11 |
12 | role:
13 | admin:
14 | name: Admin
15 | editor:
16 | name: Urednik
17 | description: Uređuje novine
18 | author:
19 | name: Autor
20 | description: Autor novina
21 |
--------------------------------------------------------------------------------
/test/reflection_test.rb:
--------------------------------------------------------------------------------
1 | require_relative 'helpers/test_helper'
2 |
3 | class ReflectionTest < Minitest::Test
4 | def test_reflection_with_all_attributes
5 | reflection = Enumerations::Reflection.new(:status, class_name: 'Status',
6 | foreign_key: :status)
7 |
8 | assert_equal :status, reflection.name
9 | assert_equal 'Status', reflection.class_name
10 | assert_equal :status, reflection.foreign_key
11 | assert_equal ::Status, reflection.enumerator_class
12 | end
13 |
14 | def test_reflection_without_class_name_and_foreign_key
15 | reflection = Enumerations::Reflection.new(:status)
16 |
17 | assert_equal :status, reflection.name
18 | assert_equal 'Status', reflection.class_name
19 | assert_equal :status, reflection.foreign_key
20 | assert_equal ::Status, reflection.enumerator_class
21 | end
22 |
23 | def test_reflection_with_custom_name_and_without_foreign_key
24 | reflection = Enumerations::Reflection.new(:my_status, class_name: 'Status')
25 |
26 | assert_equal :my_status, reflection.name
27 | assert_equal 'Status', reflection.class_name
28 | assert_equal :my_status, reflection.foreign_key
29 | assert_equal ::Status, reflection.enumerator_class
30 | end
31 |
32 | def test_reflection_with_class_name_as_constant
33 | reflection = Enumerations::Reflection.new(:status, class_name: Status)
34 |
35 | assert_equal 'Status', reflection.class_name
36 | assert_equal ::Status, reflection.enumerator_class
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/test/translation_test.rb:
--------------------------------------------------------------------------------
1 | require_relative 'helpers/test_helper'
2 |
3 | class TranslationTest < Minitest::Test
4 | def test_translated_value_with_default_locale
5 | status = Status.find(:draft)
6 |
7 | assert_equal 'Draft', status.name
8 | end
9 |
10 | def test_translated_value_with_i18n_locale
11 | status = Status.find(:draft)
12 |
13 | I18n.locale = :hr
14 | translated_name = status.name
15 | I18n.locale = :en
16 |
17 | assert_equal 'Nacrt', translated_name
18 | end
19 |
20 | def test_translated_value_with_i18n_locales
21 | status = Status.find(:draft)
22 |
23 | I18n.locale = :hr
24 | assert_equal 'Nacrt', status.name
25 | I18n.locale = :en
26 | assert_equal 'Draft', status.name
27 | end
28 |
29 | def test_translated_value_with_custom_locale
30 | status = Status.find(:draft)
31 |
32 | assert_equal 'Nacrt', status.name(locale: :hr)
33 | end
34 |
35 | def test_boolean_value_true_not_changed_by_translations
36 | status = Status.find(:none)
37 |
38 | assert_equal true, status.visible
39 | end
40 |
41 | def test_boolean_value_false_not_changed_by_translations
42 | status = Status.find(:none)
43 |
44 | assert_equal false, status.deleted
45 | end
46 |
47 | def test_boolean_value_false_not_changed_by_translations_with_custom_locale
48 | status = Status.find(:none)
49 |
50 | assert_equal false, status.deleted(locale: :hr)
51 | end
52 |
53 | def test_translated_custom_attribute_with_i18n_locale
54 | role = Role.find(:editor)
55 |
56 | I18n.locale = :hr
57 | translated_value = role.description
58 | I18n.locale = :en
59 |
60 | assert_equal 'Uređuje novine', translated_value
61 | end
62 |
63 | def test_translated_custom_attribute_with_custom_locale
64 | role = Role.find(:editor)
65 |
66 | assert_equal 'Uređuje novine', role.description(locale: :hr)
67 | end
68 |
69 | def test_empty_custom_attribute_with_custom_locale
70 | role = Role.find(:admin)
71 |
72 | assert_nil role.description(locale: :hr)
73 | end
74 |
75 | def test_empty_custom_attribute_with_custom_locale_and_defined_translation
76 | role = Role.find(:author)
77 |
78 | assert_nil role.description(locale: :hr)
79 | end
80 |
81 | def test_find_by_localized_name
82 | status = Status.find_by(name: 'Draft')
83 |
84 | assert_equal :draft, status.symbol
85 | end
86 |
87 | def test_find_by_localized_name_with_i18n_locale
88 | I18n.locale = :hr
89 | status = Status.find_by(name: 'Nacrt')
90 | I18n.locale = :en
91 |
92 | assert_nil status
93 | end
94 |
95 | def test_turn_of_translate
96 | status = Status.find(:draft)
97 | Enumerations.configuration.translate_attributes = false
98 | name = status.name
99 | Enumerations.configuration.translate_attributes = true
100 |
101 | assert_equal name, 'Draft'
102 | end
103 | end
104 |
--------------------------------------------------------------------------------
/test/value_test.rb:
--------------------------------------------------------------------------------
1 | require_relative 'helpers/test_helper'
2 |
3 | class ValueTest < Minitest::Test
4 | def test_equal_by_symbol
5 | status = Status.draft
6 |
7 | assert_equal true, status == :draft
8 | end
9 |
10 | def test_equal_by_string
11 | status = Status.draft
12 |
13 | assert_equal true, status == 'draft'
14 | end
15 |
16 | def test_equal_by_enumeration
17 | status = Status.draft
18 |
19 | assert_equal true, status == Status.draft
20 | end
21 |
22 | def test_not_equal_by_enumeration
23 | status = Status.draft
24 |
25 | assert_equal false, status == Status.published
26 | end
27 |
28 | def test_with_defined_custom_attributes_visible
29 | status = Status.find(:none)
30 |
31 | assert_equal true, status.visible
32 | end
33 |
34 | def test_with_defined_custom_attributes_deleted
35 | status = Status.find(:deleted)
36 |
37 | assert_equal true, status.deleted
38 | end
39 |
40 | def test_without_defined_custom_attributes
41 | status = Status.find(:draft)
42 |
43 | assert_nil status.visible
44 | end
45 |
46 | def test_enumeration_to_i_return_ordinal
47 | first_status = Status.find(:draft)
48 | second_status = Status.find(:review_pending)
49 |
50 | assert_equal first_status.to_i, 1
51 | assert_equal second_status.to_i, 2
52 | end
53 |
54 | def test_enumeration_to_sym
55 | status = Status.find(:draft)
56 |
57 | assert_equal status.to_sym, :draft
58 | end
59 |
60 | def test_enumeration_to_param
61 | status = Status.find(:draft)
62 |
63 | assert_equal status.to_param, 'draft'
64 | end
65 |
66 | def test_enumeration_when_attribute_value_is_symbol
67 | role = Role.find(:lecturer)
68 |
69 | assert_equal role.type, 'croatist'
70 | end
71 |
72 | def test_enumeration_attribute_predicate_methods
73 | status = Status.none
74 |
75 | assert_equal status.name?, true
76 | assert_equal status.visible?, true
77 | assert_equal status.deleted?, false
78 | end
79 |
80 | def test_enumeration_attribute_predicate_method_on_undefined_attribute
81 | status = Status.deleted
82 |
83 | assert_equal status.name?, false
84 | end
85 |
86 | def test_enumeration_raises_error_for_nonexistent_attribute
87 | enum = Class.new(Enumerations::Base) do
88 | value :foobar
89 | end
90 |
91 | assert_raises NoMethodError do
92 | enum.foobar.name
93 | end
94 | end
95 |
96 | def test_enumeration_does_not_respond_to_nonexistent_attribute
97 | enum = Class.new(Enumerations::Base) do
98 | value :foobar
99 | end
100 |
101 | assert_equal enum.foobar.respond_to?(:name), false
102 | end
103 |
104 | def test_string_methods_on_value
105 | enum = Class.new(Enumerations::Base) do
106 | value :foobar
107 | end
108 |
109 | assert_equal enum.foobar.upcase, 'FOOBAR'
110 | end
111 | end
112 |
--------------------------------------------------------------------------------