enum_fields 0.3.2 → 0.4.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 711e9c8ec5416674480e0d3abf02ac97e53fe5ab3a02ede7c8f70838a05461eb
4
- data.tar.gz: 9fc0c1811f467705a7f6f0d6be1c957b26095f23514d953d4240e7ee721c3a4c
3
+ metadata.gz: 18fd94d3a61872ff4800c07619df202cc07042b9783bde8e01dc5536c4607fb1
4
+ data.tar.gz: 4a51908ee73495023625864865863bff9ae851667a9db4ab18860008a135421d
5
5
  SHA512:
6
- metadata.gz: dfd868cdc669171dda053b068d84719a78758f1b3a01dac77e3c084efe9f87b190054bcec517c51dafe5143bfbd35ecdd4fe06da90a6c829cc586182ba59881c
7
- data.tar.gz: 7b2c111b3a8c779e8f46073a8cb244fd2a385cfbc50fcd018eae254d4dda617a57224bf844826028a2987d0a3ab49a890c98cbd67687c6cfbfadae1b520848a4
6
+ metadata.gz: 26c5cf4fb41d9bb1e3026e55919981bfd2dedbfd5075f89813546c9aeec50974c95d5c905444a1e82cbb7fc90571ffe963186cf9e03b32ca9be0f71bd7b6befb
7
+ data.tar.gz: 28d4711b052bab41d6b03a68d5d77ee1f9d846c4e406d9b239d0fc1d2bd2eaee5991c0b501f29ec566fcfc507ec8fa4b3fa2ee2fafe498e5bdb2996de0a892bd
data/CHANGELOG.md CHANGED
@@ -5,89 +5,80 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## [0.3.2] - 2026-03-08
8
+ ## [X.X.X] - YYYY-MM-DD
9
+
10
+ ## [0.4.1] - 2026-03-26
11
+
12
+ - Update `bin/ci` and `bin/release` to use new formatting functions
13
+ - Add `--quiet` flag to `bin/ci` to suppress output
14
+ - Add `--dry-run` flag to `bin/release` to perform a dry run of the release process
15
+
16
+ ## [0.4.0] - 2026-03-08
17
+
18
+ - Extract out Configuration to define global configuration for all enum fields
19
+ - Rename `validate` option to `validatable` to better reflect its purpose
20
+ - Rename `scope` option to `scopeable` to better reflect its purpose
21
+ - Rename `allow_nil` option to `nullable` to better reflect its purpose
22
+ - Add `inquirable` option to control the generation of inquiry methods
23
+ - Polymorphic validation now respects `nullable` option and falls back to the association's `optional` setting
24
+ - Update release script to keep CHANGELOG.md up to date
25
+ - Add Continuous Integration suite to run tests and style checks
26
+ - Update rake release to run release script
9
27
 
10
- ### Added
28
+ ## [0.3.2] - 2026-03-08
11
29
 
12
30
  - Restored CHANGELOG.md file
13
31
 
14
32
  ## [0.3.1] - 2026-03-08
15
33
 
16
- ### Added
17
-
18
34
  - Release script to simplify gem deployment
19
35
 
20
36
  ## [0.3.0] - 2026-03-08
21
37
 
22
- ### Added
23
-
24
38
  - Virtual attribute support — enum metadata, property, and inquiry methods now work against user-defined methods that aren't backed by a database column
25
39
  - `scope: false` option to skip scope generation (required for virtual attributes, but useful independently)
26
40
 
27
41
  ## [0.2.1] - 2026-02-25
28
42
 
29
- ### Added
30
-
31
43
  - Flexibility to array definitions, supporting more input formats
32
44
 
33
45
  ## [0.2.0] - 2026-02-24
34
46
 
35
- ### Added
36
-
37
47
  - Namespace support for standalone enum field registration via `EnumFields.namespace(:name) { enum_field ... }`, decoupling definitions from ActiveRecord models
38
48
  - `EnumFields.catalog` method for a sorted, serialization-friendly view of all registered definitions across model and standalone namespaces
39
49
 
40
50
  ## [0.1.2] - 2026-02-20
41
51
 
42
- ### Added
43
-
44
52
  - Global `EnumFields::Registry` for cross-model enum field lookup and introspection
45
53
 
46
54
  ## [0.1.1] - 2026-02-02
47
55
 
48
- ### Fixed
49
-
50
56
  - Polymorphic association resolution now falls back to finding by value when key lookup fails
51
57
 
52
58
  ## [0.1.0] - 2026-01-27
53
59
 
54
- ### Added
55
-
56
60
  - `validate: false` option to disable validations on an enum field
57
61
  - Validation support for columns used in polymorphic associations
58
62
 
59
63
  ## [0.0.5] - 2026-01-03
60
64
 
61
- ### Added
62
-
63
65
  - Class-level value accessors for enum field definitions
64
66
 
65
67
  ## [0.0.4] - 2025-11-19
66
68
 
67
- ### Changed
68
-
69
69
  - `enum_fields_metadata` now returns `HashWithIndifferentAccess` instead of a plain hash
70
70
 
71
71
  ## [0.0.3] - 2025-11-19
72
72
 
73
- ### Added
74
-
75
73
  - `enum_fields_metadata` instance method for accessing field metadata on model instances
76
-
77
- ### Changed
78
-
79
74
  - Extracted core interface functionality into separate `Base` module
80
75
 
81
76
  ## [0.0.2] - 2025-11-19
82
77
 
83
- ### Added
84
-
85
78
  - `enum_field?` class method to check whether a given attribute is a registered enum field
86
79
 
87
80
  ## [0.0.1] - 2025-11-17
88
81
 
89
- ### Added
90
-
91
82
  - Initial release with core `enum_field` DSL
92
83
  - Hash and array definition formats
93
84
  - Column override support
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 (with `allow_nil: true`).
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
- #### `scope`
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, scope: false
274
+ enum_field :speed, definitions, scopeable: false
236
275
  ```
237
276
 
238
- #### `validate`
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, validate: false
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 `scope: false` and `validate: false` since those features require a real column:
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
- }, scope: false, validate: false
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
- if column_polymorphic_association_name.present?
162
- define_polymorphic_validation!
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: true,
177
+ allow_nil: allow_nil,
174
178
  })
175
179
  end
176
180
 
177
- def define_polymorphic_validation!
178
- association_name = column_polymorphic_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 column_polymorphic_association_name
202
+ def polymorphic_reflection
198
203
  return nil unless @model_class.respond_to?(:reflect_on_all_associations)
199
204
 
200
- reflection = @model_class.reflect_on_all_associations(:belongs_to).find do |r|
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
- reflection&.name
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? && @options.fetch(:scope, true)
217
+ @definition.present? && resolve_option(:scopeable)
209
218
  end
210
219
 
211
220
  def column_validatable?
212
- @definition.present? && @options.fetch(:validate, true)
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module EnumFields
4
- VERSION = "0.3.2"
4
+ VERSION = "0.4.1"
5
5
  end
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.3.2
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kinnell Shah
@@ -65,6 +65,7 @@ files:
65
65
  - README.md
66
66
  - lib/enum_fields.rb
67
67
  - lib/enum_fields/base.rb
68
+ - lib/enum_fields/configuration.rb
68
69
  - lib/enum_fields/definition.rb
69
70
  - lib/enum_fields/enum_field.rb
70
71
  - lib/enum_fields/errors.rb
@@ -95,7 +96,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
95
96
  - !ruby/object:Gem::Version
96
97
  version: '2.0'
97
98
  requirements: []
98
- rubygems_version: 4.0.7
99
+ rubygems_version: 4.0.9
99
100
  specification_version: 4
100
101
  summary: Enhanced enum-like fields for ActiveRecord models with metadata support
101
102
  test_files: []