enumy 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +102 -0
- data/LICENSE.txt +21 -0
- data/README.md +286 -0
- data/lib/enumy/enum.rb +14 -2
- data/lib/enumy/errors.rb +10 -1
- data/lib/enumy/model.rb +34 -5
- data/lib/enumy/rails/generators/enumy/install/USAGE +11 -0
- data/lib/enumy/rails/helpers/form_builder.rb +12 -0
- data/lib/enumy/rails/helpers/form_helper.rb +11 -0
- data/lib/enumy/rails/model.rb +22 -3
- data/lib/enumy/version.rb +1 -1
- data/lib/enumy.rb +2 -0
- metadata +7 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5ca792d4165a95092a74b557d88a61d7a7a6e0d7315ec1e75e22a3f82505a1ca
|
|
4
|
+
data.tar.gz: b092cc6b1eb561643dd0da92153921e2683d1dafb1dcd2003e919a7b95d649de
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4e939f579145cf979a8575cfdf62498aa3ba6917fbf78f6f1c8df5ddd959892ed8dc5c1d1a04f7c96e332a5bbb44bf087b66c8d1044114802dcc8e5d6a0f37d2
|
|
7
|
+
data.tar.gz: 139d995c55b5f2f8cfe3d8a3d40a5c97a5712ba2678bfef187370c14c4b5ad702f48df6b64c9fa454b2963d17ed7a0013463d6d7aeadb82ea2bcfeb0fb4c79ec
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
## [Unreleased]
|
|
2
|
+
|
|
3
|
+
## 0.10.0 - 2026-09-10
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Add `enum_select` Rails helpers — a plain ActionView helper and a `FormBuilder` mixin — for rendering `<select>` inputs from an enum class [17](https://gitlab.com/BevanHolborn/enumy/-/issues/17)
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **BREAKING CHANGE**: `Enumy::Model#has_enum` now accepts `class_name:` instead of `enum_class:`, accepting a Class/Module directly or a String/Symbol resolved to a constant, and defaulting to the attribute name when omitted [15](https://gitlab.com/BevanHolborn/enumy/-/issues/15)
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- `Enumy::Enum.define` no longer raises `DuplicateEnumInstanceError` when the same source location redefines a key [16](https://gitlab.com/BevanHolborn/enumy/-/issues/16)
|
|
16
|
+
- `Enumy::Rails::Model#has_enum` no longer relies on `classify`/`safe_constantize` round-tripping when a Class is passed directly as `class_name:` [15](https://gitlab.com/BevanHolborn/enumy/-/issues/15)
|
|
17
|
+
- The packaged gem now ships `README.md`, `LICENSE.txt`, `CHANGELOG.md`, and every file under `lib/` (not just `.rb` files), instead of only `.rb` files under `lib/` [14](https://gitlab.com/BevanHolborn/enumy/-/issues/14)
|
|
18
|
+
|
|
19
|
+
## 0.9.0 - 2026-09-08
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- Make `EnumSerializer#klass` public to avoid a Rails 8.2 deprecation warning [18](https://gitlab.com/BevanHolborn/enumy/-/issues/18)
|
|
24
|
+
|
|
25
|
+
## 0.8.0 - 2025-11-29
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- Added support for using Ruby only enums as an attribute on objects [13](https://gitlab.com/BevanHolborn/enumy/-/issues/13)
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
|
|
33
|
+
- **BREAKING CHANGE**: Rename `Enumy::RailsEnum` to `Enumy::Rails::Enum` [13](https://gitlab.com/BevanHolborn/enumy/-/issues/13)
|
|
34
|
+
- Updated rake test script to properly set up the dummy Rails app [13](https://gitlab.com/BevanHolborn/enumy/-/issues/13)
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
|
|
38
|
+
- Fix `Enumy::Enum` referencing the `ActiveModel` `attributes` method in `inspect` [13](https://gitlab.com/BevanHolborn/enumy/-/issues/13)
|
|
39
|
+
- Fix `Enumy::Rails::Model` referencing the removed `Enumy::Base` instead of `Enumy::Enum` [13](https://gitlab.com/BevanHolborn/enumy/-/issues/13)
|
|
40
|
+
|
|
41
|
+
## 0.7.0 - 2025-11-25
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
|
|
45
|
+
- Add support for Enums in non-Rails Ruby applications [6](https://gitlab.com/BevanHolborn/enumy/-/issues/6)
|
|
46
|
+
|
|
47
|
+
### Changed
|
|
48
|
+
|
|
49
|
+
- **BREAKING CHANGE**: Rename `Enumy::Base` to `Enumy::RailsEnum` [6](https://gitlab.com/BevanHolborn/enumy/-/issues/6)
|
|
50
|
+
|
|
51
|
+
## 0.6.0 - 2025-06-24
|
|
52
|
+
|
|
53
|
+
### Changed
|
|
54
|
+
|
|
55
|
+
- Only require files for Rails initializers if they are supported [10](https://gitlab.com/BevanHolborn/enumy/-/issues/10)
|
|
56
|
+
- For Enumy::Base methods that wrap String methods for the `key`, follow the same name as the String method [9](https://gitlab.com/BevanHolborn/enumy/-/issues/9)
|
|
57
|
+
- Add CI/CD stage for Rspec tests [12](https://gitlab.com/BevanHolborn/enumy/-/issues/12)
|
|
58
|
+
|
|
59
|
+
## 0.5.1 - 2025-06-23
|
|
60
|
+
|
|
61
|
+
### Changed
|
|
62
|
+
|
|
63
|
+
- Change the minimum Ruby version to 2.7.0 [8](https://gitlab.com/BevanHolborn/enumy/-/issues/8)
|
|
64
|
+
|
|
65
|
+
## 0.5.0 - 2025-06-23
|
|
66
|
+
|
|
67
|
+
### Changed
|
|
68
|
+
|
|
69
|
+
- `Enumy::Base#name` now returns the I18n translation for the `key` attributes [7](https://gitlab.com/BevanHolborn/enumy/-/issues/7)
|
|
70
|
+
|
|
71
|
+
### Added
|
|
72
|
+
|
|
73
|
+
- `Enumy::Base#title`: Return the titleized `key` attribute [7](https://gitlab.com/BevanHolborn/enumy/-/issues/7)
|
|
74
|
+
|
|
75
|
+
## 0.4.0 - 2025-04-17
|
|
76
|
+
|
|
77
|
+
### Changed
|
|
78
|
+
|
|
79
|
+
- Downgrade Dockerfile ruby version to 3.1 [5](https://gitlab.com/BevanHolborn/enumy/-/issues/5)
|
|
80
|
+
- Explicitly cast key to string in the ActiveJob serializer [5](https://gitlab.com/BevanHolborn/enumy/-/issues/5)
|
|
81
|
+
- Explicitly define the serialize? method in the ActiveJob serializer [5](https://gitlab.com/BevanHolborn/enumy/-/issues/5)
|
|
82
|
+
- Serialize invalid Enum keys to nil [5](https://gitlab.com/BevanHolborn/enumy/-/issues/5)
|
|
83
|
+
- Only register the ActiveJob serializer if the Rails version supports it [5](https://gitlab.com/BevanHolborn/enumy/-/issues/5)
|
|
84
|
+
- Applied proper module scoping across the project [5](https://gitlab.com/BevanHolborn/enumy/-/issues/5)
|
|
85
|
+
- Moved the Dummy app to /spec. It is now regenerated with each test run. [5](https://gitlab.com/BevanHolborn/enumy/-/issues/5)
|
|
86
|
+
- Gemspec dependencies [5](https://gitlab.com/BevanHolborn/enumy/-/issues/5)
|
|
87
|
+
|
|
88
|
+
### Added
|
|
89
|
+
|
|
90
|
+
- Rake [5](https://gitlab.com/BevanHolborn/enumy/-/issues/5)
|
|
91
|
+
- Rspec Tests [5](https://gitlab.com/BevanHolborn/enumy/-/issues/5)
|
|
92
|
+
- DuplicateEnumInstanceError for clearer errors [5](https://gitlab.com/BevanHolborn/enumy/-/issues/5)
|
|
93
|
+
- Additional safety checks in Enumy::Base#find_by [5](https://gitlab.com/BevanHolborn/enumy/-/issues/5)
|
|
94
|
+
- Changelog [5](https://gitlab.com/BevanHolborn/enumy/-/issues/5)
|
|
95
|
+
|
|
96
|
+
### Removed
|
|
97
|
+
|
|
98
|
+
- Various unused Dockerfile dependencies [5](https://gitlab.com/BevanHolborn/enumy/-/issues/5)
|
|
99
|
+
|
|
100
|
+
### Fixed
|
|
101
|
+
|
|
102
|
+
- Enumy::Model#assert_valid_enum_type! referencing a value on a nil object [5](https://gitlab.com/BevanHolborn/enumy/-/issues/5)
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Bevan Holborn
|
|
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
|
|
13
|
+
all 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
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
# Enumy
|
|
2
|
+
|
|
3
|
+
Rich Enumeration objects in Ruby and Rails.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Installation](#installation)
|
|
8
|
+
- [Usage](#usage)
|
|
9
|
+
- [Enums](#enums)
|
|
10
|
+
- [Attributes](#attributes)
|
|
11
|
+
- [Customization](#customization)
|
|
12
|
+
- [Rails Integration](#rails-integration)
|
|
13
|
+
- [Enums](#enums-1)
|
|
14
|
+
- [Attributes](#attributes-1)
|
|
15
|
+
- [Associating Enums with Models](#associating-enums-with-models)
|
|
16
|
+
- [Migrations](#migrations)
|
|
17
|
+
- [Usage](#usage-1)
|
|
18
|
+
- [Additional Methods](#additional-methods)
|
|
19
|
+
- [Form Helpers](#form-helpers)
|
|
20
|
+
- [License](#license)
|
|
21
|
+
- [Contributing](#contributing)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
bundle add enumy
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Enums
|
|
31
|
+
|
|
32
|
+
Create a class that inherits from `Enumy::Enum` and define your enums as follows:
|
|
33
|
+
|
|
34
|
+
```ruby
|
|
35
|
+
class RoleType < Enumy::Enum
|
|
36
|
+
define FREE = self.new(:free)
|
|
37
|
+
define PAID = self.new(:paid)
|
|
38
|
+
define ADMIN = self.new(:admin)
|
|
39
|
+
end
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
RoleType.all # => [RoleType::FREE, RoleType::PAID, RoleType::ADMIN]
|
|
44
|
+
|
|
45
|
+
RoleType.find(:free) #=> RoleType::FREE
|
|
46
|
+
RoleType.find(:invalid) #=> raises EnumNotFoundError
|
|
47
|
+
|
|
48
|
+
RoleType.find_by(key: :paid) # => RoleType::PAID
|
|
49
|
+
RoleType.find_by(key: :invalid) # => nil
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Attributes
|
|
53
|
+
|
|
54
|
+
All enums have a required, unique `key` attribute by default.
|
|
55
|
+
You can add additional attributes as needed the same way you would with a regular PORO
|
|
56
|
+
|
|
57
|
+
```ruby
|
|
58
|
+
class RoleType < Enumy::Enum
|
|
59
|
+
|
|
60
|
+
attr_reader :color
|
|
61
|
+
|
|
62
|
+
def initialize(key, color:)
|
|
63
|
+
super(key)
|
|
64
|
+
@color = color
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
define FREE = self.new(:free, color: :blue)
|
|
68
|
+
define PAID = self.new(:paid, color: :purple)
|
|
69
|
+
define ADMIN = self.new(:admin, color: :red)
|
|
70
|
+
end
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
```ruby
|
|
74
|
+
RoleType::FREE.key #=> :free
|
|
75
|
+
RoleType::FREE.color #=> :blue
|
|
76
|
+
|
|
77
|
+
RoleType.find_by(color: :purple) # => RoleType::PAID
|
|
78
|
+
RoleType.find_by(key: :admin, color: :gray) # => nil
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Customization
|
|
82
|
+
|
|
83
|
+
Because Enums are POROs, you can add methods to them as needed:
|
|
84
|
+
|
|
85
|
+
```ruby
|
|
86
|
+
class RoleType < Enumy::Enum
|
|
87
|
+
|
|
88
|
+
attr_reader :color
|
|
89
|
+
|
|
90
|
+
def initialize(key, color:)
|
|
91
|
+
super(key)
|
|
92
|
+
@color = color
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
define FREE = self.new(:free, color: :blue)
|
|
96
|
+
define PAID = self.new(:paid, color: :purple)
|
|
97
|
+
define ADMIN = self.new(:admin, color: :red)
|
|
98
|
+
|
|
99
|
+
def admin?
|
|
100
|
+
self == ADMIN
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
class << self
|
|
104
|
+
|
|
105
|
+
def customer
|
|
106
|
+
[FREE, PAID]
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
```ruby
|
|
114
|
+
RoleType::ADMIN.admin? # => true
|
|
115
|
+
RoleType::FREE.admin? # => false
|
|
116
|
+
RoleType.customer # => [RoleType::FREE, RoleType::PAID]
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Rails integration
|
|
120
|
+
|
|
121
|
+
Enumy integrates with Rails applications to leverage ActiveModel as well as provide a way to associate enums with models.
|
|
122
|
+
|
|
123
|
+
Run the generator, which will set up an application enum and add the required concern to ApplicationRecord.
|
|
124
|
+
```ruby
|
|
125
|
+
rails generate enumy:install
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Rails enums usually live in `app/enums`. Create a class that inherits from `ApplicationEnum` and define your enum as follows:
|
|
129
|
+
|
|
130
|
+
```ruby
|
|
131
|
+
class RoleType < ApplicationEnum
|
|
132
|
+
define FREE = self.new(:free)
|
|
133
|
+
define PAID = self.new(:paid)
|
|
134
|
+
define ADMIN = self.new(:admin)
|
|
135
|
+
end
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
```ruby
|
|
139
|
+
RoleType.all # => [RoleType::FREE, RoleType::PAID, RoleType::ADMIN]
|
|
140
|
+
|
|
141
|
+
RoleType.find(:free) #=> RoleType::FREE
|
|
142
|
+
RoleType.find(:invalid) #=> raises EnumNotFoundError
|
|
143
|
+
|
|
144
|
+
RoleType.find_by(key: :paid) # => RoleType::PAID
|
|
145
|
+
RoleType.find_by(key: :invalid) # => nil
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
So far it's exactly the same as a regular enum.
|
|
149
|
+
|
|
150
|
+
### Attributes
|
|
151
|
+
|
|
152
|
+
All enums have a required, unique `key` attribute by default.
|
|
153
|
+
Because this is a ActiveModel backed PORO, you can add additional attributes simply as follows:
|
|
154
|
+
|
|
155
|
+
```ruby
|
|
156
|
+
class RoleType < ApplicationEnum
|
|
157
|
+
|
|
158
|
+
attribute :color
|
|
159
|
+
|
|
160
|
+
define FREE = self.new(:free, color: :blue)
|
|
161
|
+
define PAID = self.new(:paid, color: :purple)
|
|
162
|
+
define ADMIN = self.new(:admin, color: :red)
|
|
163
|
+
end
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
```ruby
|
|
167
|
+
RoleType::FREE.key #=> :free
|
|
168
|
+
RoleType::FREE.color #=> :blue
|
|
169
|
+
|
|
170
|
+
RoleType.find_by(color: :purple) # => RoleType::PAID
|
|
171
|
+
RoleType.find_by(key: :admin, color: :gray) # => nil
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Additional methods
|
|
175
|
+
|
|
176
|
+
In addition to the standard Enumy methods, the Rails implementation supports the following Rails-specific methods:
|
|
177
|
+
|
|
178
|
+
**titleize**
|
|
179
|
+
Returns the titleized version of the enum's key.
|
|
180
|
+
|
|
181
|
+
**human_attribute_name**
|
|
182
|
+
Looks up human-readable attribute names for the enum using I18n translations.
|
|
183
|
+
The lookup path is `enum.attributes.models.{enum_class_name}.{enum_key}.{attribute_name}`.
|
|
184
|
+
|
|
185
|
+
**name**
|
|
186
|
+
An alias for human_attribute_name(:key)
|
|
187
|
+
|
|
188
|
+
**model_name**
|
|
189
|
+
Returns an ActiveModel::Name instance for the enum class
|
|
190
|
+
|
|
191
|
+
### Associating enums with models
|
|
192
|
+
|
|
193
|
+
Enums can be associated with ActiveRecord models using the `has_enum` method provided by Enumy.
|
|
194
|
+
|
|
195
|
+
Enable this by including the `Enumy::Model` concern in your `ApplicationRecord`:
|
|
196
|
+
|
|
197
|
+
```ruby
|
|
198
|
+
class ApplicationRecord
|
|
199
|
+
include Enumy::Rails::Model
|
|
200
|
+
end
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Then in any model, you can define an enum association as follows:
|
|
204
|
+
|
|
205
|
+
```ruby
|
|
206
|
+
class User < ApplicationRecord
|
|
207
|
+
has_enum :role_type
|
|
208
|
+
end
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
`has_enum` requires a name (e.g. `has_enum :role_type`)
|
|
212
|
+
You can also the following kwargs:
|
|
213
|
+
- `class_name`: The enum class to use (e.g. class_name: "RoleType"). If not set, it is derived from the name
|
|
214
|
+
- `optional`: If true, the enum can be `nil`. If false, validations are added - `validates :role_type, presence: true, inclusion: { in: RoleType.all }`
|
|
215
|
+
- `default`: Sets a default enum value (e.g. default: RoleType::FREE).
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
#### Migrations
|
|
219
|
+
|
|
220
|
+
Enumy stores the key of the enum in the model's table, and then attaches the enum to the model when it is loaded into memory.
|
|
221
|
+
This means that a table column is required when adding an enum to a model:
|
|
222
|
+
|
|
223
|
+
```ruby
|
|
224
|
+
class AddRoleTypeToUsers < ActiveRecord::Migration
|
|
225
|
+
def change
|
|
226
|
+
add_column :users, :role_type, :string
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
#### Usage
|
|
232
|
+
|
|
233
|
+
With the enum set up on your model you can use it as follows:
|
|
234
|
+
|
|
235
|
+
```ruby
|
|
236
|
+
user = User.new(role_type: RoleType::FREE)
|
|
237
|
+
user.role_type # => RoleType::FREE
|
|
238
|
+
|
|
239
|
+
user.role_type = RoleType::PAID
|
|
240
|
+
user.role_type # => RoleType::PAID
|
|
241
|
+
|
|
242
|
+
User.where(role_type: RoleType::ADMIN) # SELECT * FROM users WHERE role_type = 'admin'
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Form helpers
|
|
246
|
+
|
|
247
|
+
Enumy provides two helpers for rendering `<select>` inputs from an enum class. Neither is auto-registered, so include them where you need them:
|
|
248
|
+
|
|
249
|
+
```ruby
|
|
250
|
+
module ApplicationHelper
|
|
251
|
+
include Enumy::Rails::Helpers::FormHelper
|
|
252
|
+
end
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
```ruby
|
|
256
|
+
class ApplicationFormBuilder < ActionView::Helpers::FormBuilder
|
|
257
|
+
include Enumy::Rails::Helpers::FormBuilder
|
|
258
|
+
end
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
**`enum_select`** is a plain ActionView helper, following the same `object`/`method`/`collection` convention as Rails' own `select`/`collection_select`:
|
|
262
|
+
|
|
263
|
+
```ruby
|
|
264
|
+
enum_select(:user, :role_type, RoleType.all)
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
`collection` is required — the plain helper has no model context to derive one from. It also accepts the following kwargs:
|
|
268
|
+
- `value_method`: The method (or callable) used to compute each option's value. Defaults to `:key`
|
|
269
|
+
- `text_method`: The method (or callable) used to compute each option's text. Defaults to `:name`
|
|
270
|
+
- `options`: Passed through to the underlying `select` tag
|
|
271
|
+
- `html_options`: Passed through to the underlying `select` tag
|
|
272
|
+
|
|
273
|
+
**`f.enum_select`** is a FormBuilder method that delegates to the plain helper above:
|
|
274
|
+
|
|
275
|
+
```ruby
|
|
276
|
+
f.enum_select(:role_type)
|
|
277
|
+
f.enum_select(:role_type, RoleType.customer)
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
`collection` is an optional second positional argument. When omitted, it's derived from the field's declared `has_enum` class (`RoleType.all`) — this requires `:role_type` to have been set up with `has_enum` on the model. It also accepts `options` and `html_options`, both passed through unchanged.
|
|
281
|
+
|
|
282
|
+
## License
|
|
283
|
+
This project is licensed under the MIT License. See the LICENSE file for details.
|
|
284
|
+
|
|
285
|
+
## Contributing
|
|
286
|
+
Contributions are welcome! Please open issues or submit merge requests via GitLab.
|
data/lib/enumy/enum.rb
CHANGED
|
@@ -28,10 +28,14 @@ module Enumy
|
|
|
28
28
|
def define(enum)
|
|
29
29
|
raise ArgumentError, "Expected an object of type `Enumy::Enum`, got #{enum.class.name}" unless enum.is_a?(Enumy::Enum)
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
call_site = caller_locations(1..1).first
|
|
32
|
+
existing_location = definition_locations[enum.key]
|
|
33
|
+
|
|
34
|
+
if instances[enum.key] && !locations_match?(existing_location, call_site)
|
|
35
|
+
raise Errors::DuplicateEnumInstanceError.new(enum, existing_location, call_site)
|
|
33
36
|
else
|
|
34
37
|
instances[enum.key] = enum
|
|
38
|
+
definition_locations[enum.key] = call_site
|
|
35
39
|
end
|
|
36
40
|
end
|
|
37
41
|
|
|
@@ -59,6 +63,14 @@ module Enumy
|
|
|
59
63
|
@instances ||= {}
|
|
60
64
|
end
|
|
61
65
|
|
|
66
|
+
def definition_locations
|
|
67
|
+
@definition_locations ||= {}
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def locations_match?(location_a, location_b)
|
|
71
|
+
!location_a.nil? && location_a.path == location_b.path && location_a.lineno == location_b.lineno
|
|
72
|
+
end
|
|
73
|
+
|
|
62
74
|
end
|
|
63
75
|
|
|
64
76
|
end
|
data/lib/enumy/errors.rb
CHANGED
|
@@ -3,7 +3,16 @@ module Enumy
|
|
|
3
3
|
|
|
4
4
|
class EnumNotFoundError < StandardError; end
|
|
5
5
|
class InvalidEnumClassError < NameError; end
|
|
6
|
-
class DuplicateEnumInstanceError < ArgumentError
|
|
6
|
+
class DuplicateEnumInstanceError < ArgumentError
|
|
7
|
+
def initialize(enum, existing_location, conflicting_location)
|
|
8
|
+
message = <<~MESSAGE.chomp
|
|
9
|
+
Enum `#{enum.key.to_s.upcase}` is using a duplicate key `#{enum.key}` for Enum '#{enum.class.name}'.
|
|
10
|
+
First defined at #{existing_location.path}:#{existing_location.lineno}, redefined at #{conflicting_location.path}:#{conflicting_location.lineno}.
|
|
11
|
+
MESSAGE
|
|
12
|
+
|
|
13
|
+
super(message)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
7
16
|
|
|
8
17
|
class KeyMissingError < ArgumentError
|
|
9
18
|
def initialize(enum)
|
data/lib/enumy/model.rb
CHANGED
|
@@ -4,8 +4,9 @@ require 'enumy/errors'
|
|
|
4
4
|
module Enumy
|
|
5
5
|
module Model
|
|
6
6
|
|
|
7
|
-
def has_enum(name,
|
|
8
|
-
|
|
7
|
+
def has_enum(name, class_name: name, **options)
|
|
8
|
+
enum_class = resolve_enum_class(class_name)
|
|
9
|
+
assert_valid_enum_type!(enum_class, class_name)
|
|
9
10
|
define_enum_methods(name, enum_class, default: options[:default])
|
|
10
11
|
end
|
|
11
12
|
|
|
@@ -38,9 +39,37 @@ module Enumy
|
|
|
38
39
|
end
|
|
39
40
|
end
|
|
40
41
|
|
|
41
|
-
def
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
def resolve_enum_class(class_name)
|
|
43
|
+
if class_name.is_a?(Module)
|
|
44
|
+
class_name
|
|
45
|
+
else
|
|
46
|
+
resolve_constant(class_name)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def resolve_constant(class_name)
|
|
51
|
+
constant_name = class_name.to_s.split('::').map do |namespace_segment|
|
|
52
|
+
namespace_segment.split('_').map { |word| word[0].upcase + word[1..] }.join
|
|
53
|
+
end.join('::')
|
|
54
|
+
|
|
55
|
+
constant = Object.const_get(constant_name)
|
|
56
|
+
|
|
57
|
+
if constant.is_a?(Module)
|
|
58
|
+
constant
|
|
59
|
+
else
|
|
60
|
+
nil
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
rescue NameError
|
|
64
|
+
nil
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def assert_valid_enum_type!(enum_class, class_name)
|
|
68
|
+
if enum_class.nil?
|
|
69
|
+
raise Errors::InvalidEnumClassError, "Could not resolve class `#{class_name}`"
|
|
70
|
+
elsif !enum_class.ancestors.include?(Enumy::Enum)
|
|
71
|
+
raise Errors::InvalidEnumClassError, "`#{class_name}` does not inherit from `#{Enumy::Enum.name}`"
|
|
72
|
+
end
|
|
44
73
|
end
|
|
45
74
|
|
|
46
75
|
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Description:
|
|
2
|
+
Generates an ApplicationEnum class and extends ApplicationRecord as a starting point for your application.
|
|
3
|
+
|
|
4
|
+
Example:
|
|
5
|
+
bin/rails generate enumy:install
|
|
6
|
+
|
|
7
|
+
This will create:
|
|
8
|
+
app/enums/application_enum.rb
|
|
9
|
+
|
|
10
|
+
This will modify:
|
|
11
|
+
app/models/application_record.rb
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module Enumy
|
|
2
|
+
module Rails
|
|
3
|
+
module Helpers
|
|
4
|
+
module FormBuilder
|
|
5
|
+
def enum_select(method, collection = nil, options: {}, html_options: {})
|
|
6
|
+
collection ||= @object.class.public_send("#{method}_class").all
|
|
7
|
+
@template.enum_select(@object_name, method, collection, options: options, html_options: html_options)
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module Enumy
|
|
2
|
+
module Rails
|
|
3
|
+
module Helpers
|
|
4
|
+
module FormHelper
|
|
5
|
+
def enum_select(object, method, collection, value_method: :key, text_method: :name, options: {}, html_options: {})
|
|
6
|
+
collection_select(object, method, collection, value_method, text_method, options, html_options)
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
data/lib/enumy/rails/model.rb
CHANGED
|
@@ -5,15 +5,34 @@ module Enumy
|
|
|
5
5
|
module Rails
|
|
6
6
|
module Model
|
|
7
7
|
def has_enum(name, class_name: name, **options)
|
|
8
|
-
enum_class = class_name
|
|
8
|
+
enum_class = resolve_enum_class(class_name)
|
|
9
9
|
|
|
10
10
|
assert_valid_enum_type!(enum_class, class_name)
|
|
11
11
|
define_enum_attributes(name, enum_class, default: options[:default])
|
|
12
12
|
define_enum_validations(name, enum_class) unless options[:optional]
|
|
13
|
+
define_enum_class_reader(name, enum_class)
|
|
13
14
|
end
|
|
14
15
|
|
|
15
16
|
private
|
|
16
17
|
|
|
18
|
+
def define_enum_class_reader(name, enum_class)
|
|
19
|
+
define_singleton_method("#{name}_class") { enum_class }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def resolve_enum_class(class_name)
|
|
23
|
+
if class_name.is_a?(Module)
|
|
24
|
+
class_name
|
|
25
|
+
else
|
|
26
|
+
constant = class_name.to_s.classify.safe_constantize
|
|
27
|
+
|
|
28
|
+
if constant.is_a?(Module)
|
|
29
|
+
constant
|
|
30
|
+
else
|
|
31
|
+
nil
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
17
36
|
def define_enum_attributes(name, enum_class, **options)
|
|
18
37
|
attribute name, ActiveModel::Type::Enum.new(enum_class), default: -> { options[:default] }
|
|
19
38
|
end
|
|
@@ -24,9 +43,9 @@ module Enumy
|
|
|
24
43
|
|
|
25
44
|
def assert_valid_enum_type!(enum_class, class_name)
|
|
26
45
|
if enum_class.nil?
|
|
27
|
-
raise Errors::InvalidEnumClassError, "Could not resolve class `#{class_name
|
|
46
|
+
raise Errors::InvalidEnumClassError, "Could not resolve class `#{class_name}`"
|
|
28
47
|
elsif !enum_class.ancestors.include?(Enumy::Enum)
|
|
29
|
-
raise Errors::InvalidEnumClassError, "`#{
|
|
48
|
+
raise Errors::InvalidEnumClassError, "`#{class_name}` does not inherit from `#{Enumy::Enum.name}`"
|
|
30
49
|
end
|
|
31
50
|
end
|
|
32
51
|
end
|
data/lib/enumy/version.rb
CHANGED
data/lib/enumy.rb
CHANGED
|
@@ -7,5 +7,7 @@ if defined?(Rails)
|
|
|
7
7
|
require 'enumy/rails/model'
|
|
8
8
|
require 'enumy/rails/generators/enumy/install/install_generator'
|
|
9
9
|
require 'enumy/rails/active_model/type/enum'
|
|
10
|
+
require 'enumy/rails/helpers/form_helper'
|
|
11
|
+
require 'enumy/rails/helpers/form_builder'
|
|
10
12
|
require 'enumy/rails/railtie'
|
|
11
13
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: enumy
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.10.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Bevan Holborn
|
|
@@ -56,6 +56,9 @@ executables: []
|
|
|
56
56
|
extensions: []
|
|
57
57
|
extra_rdoc_files: []
|
|
58
58
|
files:
|
|
59
|
+
- CHANGELOG.md
|
|
60
|
+
- LICENSE.txt
|
|
61
|
+
- README.md
|
|
59
62
|
- lib/enumy.rb
|
|
60
63
|
- lib/enumy/enum.rb
|
|
61
64
|
- lib/enumy/errors.rb
|
|
@@ -63,8 +66,11 @@ files:
|
|
|
63
66
|
- lib/enumy/rails/active_job/serializers/enum_serializer.rb
|
|
64
67
|
- lib/enumy/rails/active_model/type/enum.rb
|
|
65
68
|
- lib/enumy/rails/enum.rb
|
|
69
|
+
- lib/enumy/rails/generators/enumy/install/USAGE
|
|
66
70
|
- lib/enumy/rails/generators/enumy/install/install_generator.rb
|
|
67
71
|
- lib/enumy/rails/generators/enumy/install/templates/app/enums/application_enum.rb
|
|
72
|
+
- lib/enumy/rails/helpers/form_builder.rb
|
|
73
|
+
- lib/enumy/rails/helpers/form_helper.rb
|
|
68
74
|
- lib/enumy/rails/initializer_interceptor.rb
|
|
69
75
|
- lib/enumy/rails/model.rb
|
|
70
76
|
- lib/enumy/rails/railtie.rb
|