enum_fields 0.3.1 → 0.4.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 +81 -0
- data/README.md +83 -7
- data/lib/enum_fields/configuration.rb +22 -0
- data/lib/enum_fields/enum_field.rb +30 -11
- data/lib/enum_fields/version.rb +1 -1
- data/lib/enum_fields.rb +13 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b8cef70b80a3d7295656083f0f02fa89ecf8c5909159e8f3739083b6628adecb
|
|
4
|
+
data.tar.gz: 52b5ef83feac0853df498cf0e2249ba05e59cf986ffa08b56ac3145a41b559f1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b302a85589635931d64c7e44d8cff16833b6b6c2041d22886e410a929e092b3ca8de55f43c22b72079a87565b9bc1e5f1c1aacbf8d4a26254665578930302079
|
|
7
|
+
data.tar.gz: '0477991e1b705e53dd5283e581dd43f38fa4ff7dc82d3428c48190ea8ddb918c0865dee701be939823aff23c22b3c535b55e574ed9dbffa00e87634fe88deeb3'
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [X.X.X] - YYYY-MM-DD
|
|
9
|
+
|
|
10
|
+
## [0.4.0] - 2026-03-08
|
|
11
|
+
|
|
12
|
+
- Extract out Configuration to define global configuration for all enum fields
|
|
13
|
+
- Rename `validate` option to `validatable` to better reflect its purpose
|
|
14
|
+
- Rename `scope` option to `scopeable` to better reflect its purpose
|
|
15
|
+
- Rename `allow_nil` option to `nullable` to better reflect its purpose
|
|
16
|
+
- Add `inquirable` option to control the generation of inquiry methods
|
|
17
|
+
- Polymorphic validation now respects `nullable` option and falls back to the association's `optional` setting
|
|
18
|
+
- Update release script to keep CHANGELOG.md up to date
|
|
19
|
+
- Add Continuous Integration suite to run tests and style checks
|
|
20
|
+
- Update rake release to run release script
|
|
21
|
+
|
|
22
|
+
## [0.3.2] - 2026-03-08
|
|
23
|
+
|
|
24
|
+
- Restored CHANGELOG.md file
|
|
25
|
+
|
|
26
|
+
## [0.3.1] - 2026-03-08
|
|
27
|
+
|
|
28
|
+
- Release script to simplify gem deployment
|
|
29
|
+
|
|
30
|
+
## [0.3.0] - 2026-03-08
|
|
31
|
+
|
|
32
|
+
- Virtual attribute support — enum metadata, property, and inquiry methods now work against user-defined methods that aren't backed by a database column
|
|
33
|
+
- `scope: false` option to skip scope generation (required for virtual attributes, but useful independently)
|
|
34
|
+
|
|
35
|
+
## [0.2.1] - 2026-02-25
|
|
36
|
+
|
|
37
|
+
- Flexibility to array definitions, supporting more input formats
|
|
38
|
+
|
|
39
|
+
## [0.2.0] - 2026-02-24
|
|
40
|
+
|
|
41
|
+
- Namespace support for standalone enum field registration via `EnumFields.namespace(:name) { enum_field ... }`, decoupling definitions from ActiveRecord models
|
|
42
|
+
- `EnumFields.catalog` method for a sorted, serialization-friendly view of all registered definitions across model and standalone namespaces
|
|
43
|
+
|
|
44
|
+
## [0.1.2] - 2026-02-20
|
|
45
|
+
|
|
46
|
+
- Global `EnumFields::Registry` for cross-model enum field lookup and introspection
|
|
47
|
+
|
|
48
|
+
## [0.1.1] - 2026-02-02
|
|
49
|
+
|
|
50
|
+
- Polymorphic association resolution now falls back to finding by value when key lookup fails
|
|
51
|
+
|
|
52
|
+
## [0.1.0] - 2026-01-27
|
|
53
|
+
|
|
54
|
+
- `validate: false` option to disable validations on an enum field
|
|
55
|
+
- Validation support for columns used in polymorphic associations
|
|
56
|
+
|
|
57
|
+
## [0.0.5] - 2026-01-03
|
|
58
|
+
|
|
59
|
+
- Class-level value accessors for enum field definitions
|
|
60
|
+
|
|
61
|
+
## [0.0.4] - 2025-11-19
|
|
62
|
+
|
|
63
|
+
- `enum_fields_metadata` now returns `HashWithIndifferentAccess` instead of a plain hash
|
|
64
|
+
|
|
65
|
+
## [0.0.3] - 2025-11-19
|
|
66
|
+
|
|
67
|
+
- `enum_fields_metadata` instance method for accessing field metadata on model instances
|
|
68
|
+
- Extracted core interface functionality into separate `Base` module
|
|
69
|
+
|
|
70
|
+
## [0.0.2] - 2025-11-19
|
|
71
|
+
|
|
72
|
+
- `enum_field?` class method to check whether a given attribute is a registered enum field
|
|
73
|
+
|
|
74
|
+
## [0.0.1] - 2025-11-17
|
|
75
|
+
|
|
76
|
+
- Initial release with core `enum_field` DSL
|
|
77
|
+
- Hash and array definition formats
|
|
78
|
+
- Column override support
|
|
79
|
+
- Additional properties on enum values
|
|
80
|
+
- Inclusion validations
|
|
81
|
+
- Inquiry methods, property accessors, and human-readable labels
|
data/README.md
CHANGED
|
@@ -21,6 +21,45 @@ And then execute:
|
|
|
21
21
|
bundle install
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
+
## Configuration
|
|
25
|
+
|
|
26
|
+
### Global Configuration
|
|
27
|
+
|
|
28
|
+
Configure default behavior for all `enum_field` declarations:
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
# config/initializers/enum_fields.rb
|
|
32
|
+
EnumFields.configure do |config|
|
|
33
|
+
config.scopeable = true # default: true
|
|
34
|
+
config.validatable = true # default: true
|
|
35
|
+
config.nullable = true # default: true
|
|
36
|
+
config.inquirable = true # default: true
|
|
37
|
+
end
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
| Option | Default | Description |
|
|
41
|
+
| --- | --- | --- |
|
|
42
|
+
| `scopeable` | `true` | Generate query scopes for each enum value |
|
|
43
|
+
| `validatable` | `true` | Add inclusion validation for enum values |
|
|
44
|
+
| `nullable` | `true` | Allow `nil` values in validation (polymorphic columns derive this from the association's `optional` flag instead) |
|
|
45
|
+
| `inquirable` | `true` | Generate `?` inquiry methods for each enum value |
|
|
46
|
+
|
|
47
|
+
Individual `enum_field` options override global configuration:
|
|
48
|
+
|
|
49
|
+
```ruby
|
|
50
|
+
EnumFields.configure do |config|
|
|
51
|
+
config.scopeable = false
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
class Campaign < ApplicationRecord
|
|
55
|
+
# Uses global scopeable: false
|
|
56
|
+
enum_field :stage, definitions
|
|
57
|
+
|
|
58
|
+
# Overrides global — scopes are generated for this field
|
|
59
|
+
enum_field :priority, definitions, scopeable: true
|
|
60
|
+
end
|
|
61
|
+
```
|
|
62
|
+
|
|
24
63
|
## Usage
|
|
25
64
|
|
|
26
65
|
### Basic Setup
|
|
@@ -215,7 +254,7 @@ Campaign.completed_stage
|
|
|
215
254
|
|
|
216
255
|
#### Validation
|
|
217
256
|
|
|
218
|
-
Automatically validates that the column value is included in the defined values
|
|
257
|
+
Automatically validates that the column value is included in the defined values. By default, `nil` values are allowed (see [`nullable`](#nullable) option).
|
|
219
258
|
|
|
220
259
|
### Options
|
|
221
260
|
|
|
@@ -227,25 +266,62 @@ Map the accessor to a different database column name:
|
|
|
227
266
|
enum_field :role, definitions, column: :user_role
|
|
228
267
|
```
|
|
229
268
|
|
|
230
|
-
#### `
|
|
269
|
+
#### `scopeable`
|
|
231
270
|
|
|
232
271
|
Controls whether query scopes are generated. Defaults to `true`. Set to `false` to skip scope generation:
|
|
233
272
|
|
|
234
273
|
```ruby
|
|
235
|
-
enum_field :speed, definitions,
|
|
274
|
+
enum_field :speed, definitions, scopeable: false
|
|
236
275
|
```
|
|
237
276
|
|
|
238
|
-
#### `
|
|
277
|
+
#### `validatable`
|
|
239
278
|
|
|
240
279
|
Controls whether inclusion validation is added. Defaults to `true`. Set to `false` to skip validation:
|
|
241
280
|
|
|
242
281
|
```ruby
|
|
243
|
-
enum_field :speed, definitions,
|
|
282
|
+
enum_field :speed, definitions, validatable: false
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
#### `nullable`
|
|
286
|
+
|
|
287
|
+
Controls whether `nil` values pass validation. Defaults to `true`. Set to `false` to require a value:
|
|
288
|
+
|
|
289
|
+
```ruby
|
|
290
|
+
enum_field :speed, definitions, nullable: false
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
For polymorphic columns, nullability is derived from the association's `optional` flag rather than the global default. A `belongs_to` with `optional: true` allows nil; without it, nil is rejected. An explicit `nullable` option on the field still takes precedence:
|
|
294
|
+
|
|
295
|
+
```ruby
|
|
296
|
+
class Comment < ApplicationRecord
|
|
297
|
+
belongs_to :commentable, polymorphic: true, optional: true
|
|
298
|
+
|
|
299
|
+
# nil allowed — derived from optional: true
|
|
300
|
+
enum_field :commentable_type, definitions
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
class Attachment < ApplicationRecord
|
|
304
|
+
belongs_to :attachable, polymorphic: true
|
|
305
|
+
|
|
306
|
+
# nil rejected — association is required by default
|
|
307
|
+
enum_field :attachable_type, definitions
|
|
308
|
+
|
|
309
|
+
# Override: allow nil despite required association
|
|
310
|
+
enum_field :attachable_type, definitions, nullable: true
|
|
311
|
+
end
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
#### `inquirable`
|
|
315
|
+
|
|
316
|
+
Controls whether `?` inquiry methods are generated. Defaults to `true`. Set to `false` to skip:
|
|
317
|
+
|
|
318
|
+
```ruby
|
|
319
|
+
enum_field :speed, definitions, inquirable: false
|
|
244
320
|
```
|
|
245
321
|
|
|
246
322
|
### Virtual Attributes
|
|
247
323
|
|
|
248
|
-
`enum_field` works with computed/virtual attributes that aren't backed by a database column. Define a method on the model and use `
|
|
324
|
+
`enum_field` works with computed/virtual attributes that aren't backed by a database column. Define a method on the model and use `scopeable: false` and `validatable: false` since those features require a real column:
|
|
249
325
|
|
|
250
326
|
```ruby
|
|
251
327
|
class Segment < ApplicationRecord
|
|
@@ -262,7 +338,7 @@ class Segment < ApplicationRecord
|
|
|
262
338
|
value: "large",
|
|
263
339
|
label: "Large (< 10K)",
|
|
264
340
|
},
|
|
265
|
-
},
|
|
341
|
+
}, scopeable: false, validatable: false
|
|
266
342
|
|
|
267
343
|
def size_category
|
|
268
344
|
case profiles_count
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EnumFields
|
|
4
|
+
class Configuration
|
|
5
|
+
DEFAULTS = {
|
|
6
|
+
scopeable: true,
|
|
7
|
+
validatable: true,
|
|
8
|
+
nullable: true,
|
|
9
|
+
inquirable: true,
|
|
10
|
+
}.freeze
|
|
11
|
+
|
|
12
|
+
attr_accessor :scopeable, :validatable, :nullable, :inquirable
|
|
13
|
+
|
|
14
|
+
def initialize
|
|
15
|
+
reset!
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def reset!
|
|
19
|
+
DEFAULTS.each { |key, value| public_send("#{key}=", value) }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -133,6 +133,8 @@ module EnumFields
|
|
|
133
133
|
end
|
|
134
134
|
|
|
135
135
|
def define_inquiry_methods!
|
|
136
|
+
return unless column_inquirable?
|
|
137
|
+
|
|
136
138
|
accessor = @accessor
|
|
137
139
|
column_name = @column_name
|
|
138
140
|
|
|
@@ -158,8 +160,9 @@ module EnumFields
|
|
|
158
160
|
def define_validation!
|
|
159
161
|
return unless column_validatable?
|
|
160
162
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
+
reflection = polymorphic_reflection
|
|
164
|
+
if reflection
|
|
165
|
+
define_polymorphic_validation!(reflection)
|
|
163
166
|
else
|
|
164
167
|
define_standard_validation!
|
|
165
168
|
end
|
|
@@ -167,15 +170,17 @@ module EnumFields
|
|
|
167
170
|
|
|
168
171
|
def define_standard_validation!
|
|
169
172
|
valid_values = @definition.values.pluck(:value)
|
|
173
|
+
allow_nil = resolve_option(:nullable)
|
|
170
174
|
|
|
171
175
|
@model_class.validates(@column_name, inclusion: {
|
|
172
176
|
in: valid_values,
|
|
173
|
-
allow_nil:
|
|
177
|
+
allow_nil: allow_nil,
|
|
174
178
|
})
|
|
175
179
|
end
|
|
176
180
|
|
|
177
|
-
def define_polymorphic_validation!
|
|
178
|
-
association_name =
|
|
181
|
+
def define_polymorphic_validation!(reflection)
|
|
182
|
+
association_name = reflection.name
|
|
183
|
+
allow_nil = resolve_polymorphic_nullable(reflection)
|
|
179
184
|
column_name = @column_name
|
|
180
185
|
accessor = @accessor
|
|
181
186
|
|
|
@@ -187,29 +192,43 @@ module EnumFields
|
|
|
187
192
|
errors.add(association_name, "must be one of: #{valid_values.join(', ')}") unless valid_values.include?(association_obj.class.name)
|
|
188
193
|
else
|
|
189
194
|
column_value = attributes[column_name.to_s]
|
|
190
|
-
next if column_value.nil?
|
|
195
|
+
next if column_value.nil? && allow_nil
|
|
191
196
|
|
|
192
197
|
errors.add(column_name, "must be one of: #{valid_values.join(', ')}") unless valid_values.include?(column_value)
|
|
193
198
|
end
|
|
194
199
|
end
|
|
195
200
|
end
|
|
196
201
|
|
|
197
|
-
def
|
|
202
|
+
def polymorphic_reflection
|
|
198
203
|
return nil unless @model_class.respond_to?(:reflect_on_all_associations)
|
|
199
204
|
|
|
200
|
-
|
|
205
|
+
@model_class.reflect_on_all_associations(:belongs_to).find do |r|
|
|
201
206
|
r.options[:polymorphic] && "#{r.name}_type" == @column_name.to_s
|
|
202
207
|
end
|
|
208
|
+
end
|
|
203
209
|
|
|
204
|
-
|
|
210
|
+
def resolve_polymorphic_nullable(reflection)
|
|
211
|
+
return @options[:nullable] if @options.key?(:nullable)
|
|
212
|
+
|
|
213
|
+
reflection.options.fetch(:optional, false)
|
|
205
214
|
end
|
|
206
215
|
|
|
207
216
|
def column_scopeable?
|
|
208
|
-
@definition.present? &&
|
|
217
|
+
@definition.present? && resolve_option(:scopeable)
|
|
209
218
|
end
|
|
210
219
|
|
|
211
220
|
def column_validatable?
|
|
212
|
-
@definition.present? &&
|
|
221
|
+
@definition.present? && resolve_option(:validatable)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def column_inquirable?
|
|
225
|
+
@definition.present? && resolve_option(:inquirable)
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def resolve_option(key)
|
|
229
|
+
return @options[key] if @options.key?(key)
|
|
230
|
+
|
|
231
|
+
EnumFields.configuration.public_send(key)
|
|
213
232
|
end
|
|
214
233
|
end
|
|
215
234
|
end
|
data/lib/enum_fields/version.rb
CHANGED
data/lib/enum_fields.rb
CHANGED
|
@@ -8,6 +8,7 @@ require "active_support/core_ext/array/extract_options"
|
|
|
8
8
|
require "active_support/inflector"
|
|
9
9
|
require "active_record"
|
|
10
10
|
|
|
11
|
+
require_relative "enum_fields/configuration"
|
|
11
12
|
require_relative "enum_fields/errors"
|
|
12
13
|
require_relative "enum_fields/version"
|
|
13
14
|
require_relative "enum_fields/base"
|
|
@@ -21,6 +22,18 @@ module EnumFields
|
|
|
21
22
|
autoload :Namespace
|
|
22
23
|
autoload :Registry
|
|
23
24
|
|
|
25
|
+
def self.configuration
|
|
26
|
+
@configuration ||= Configuration.new
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.configure
|
|
30
|
+
yield(configuration)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.reset_configuration!
|
|
34
|
+
@configuration = Configuration.new
|
|
35
|
+
end
|
|
36
|
+
|
|
24
37
|
def self.registry
|
|
25
38
|
@registry ||= Registry.new
|
|
26
39
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: enum_fields
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kinnell Shah
|
|
@@ -60,10 +60,12 @@ executables: []
|
|
|
60
60
|
extensions: []
|
|
61
61
|
extra_rdoc_files: []
|
|
62
62
|
files:
|
|
63
|
+
- CHANGELOG.md
|
|
63
64
|
- LICENSE
|
|
64
65
|
- README.md
|
|
65
66
|
- lib/enum_fields.rb
|
|
66
67
|
- lib/enum_fields/base.rb
|
|
68
|
+
- lib/enum_fields/configuration.rb
|
|
67
69
|
- lib/enum_fields/definition.rb
|
|
68
70
|
- lib/enum_fields/enum_field.rb
|
|
69
71
|
- lib/enum_fields/errors.rb
|