unmagic-enum 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2e0707a39bcaed61e94384ad0f2b80da72d62034788b7b5cfcebfe8ed11430e1
4
+ data.tar.gz: 0d54f6ef948a3ffe7667a28032dbab381c974578632cba85eced47f1faac99cf
5
+ SHA512:
6
+ metadata.gz: c5859e7c7836a1e81752180a32c20acd9bd628e6f31dbacd9287376ba4994208dda2e13f4c10dd4ffe60dee98f7ac6f1b35e6759c728f244588c2b3384f539c6
7
+ data.tar.gz: 69d503aa8641966f37525bccc1216c6dd66d32a80cd1f33786b1f9700c5c2c391f270642d790c554d1ca565427750329c788965a2ac5d55c74a294b673a8b300
data/CHANGELOG.md ADDED
@@ -0,0 +1,27 @@
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.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.0] - 2026-06-04
11
+
12
+ ### Added
13
+ - Initial release
14
+ - `Unmagic::Enum` base class for defining type-safe, immutable enums backed by string values
15
+ - Custom attributes via `attribute :name`, with `default:` values and `alias:` reader names
16
+ - Key/value separation (`new("entity", value: "bot")`) for mapping code identifiers to differing database values
17
+ - Support for symbols, integers, and classes as keys (preserving original type), enabling clean STI integration
18
+ - Dynamic query methods (`status.active?`) for checking enum keys
19
+ - Lookups by key or value with `Enum[...]`, plus `all`, `keys`, `values`, and `valid?` helpers
20
+ - Duplicate key and value detection, and reserved-method conflict detection, raised at definition time
21
+ - `InvalidValueError` raised on invalid assignment, mirroring Rails enum behaviour
22
+ - ActiveRecord integration: `Enum.column_type` for serialization, casting, and eager validation in `attribute` declarations
23
+ - Rails presence support (`blank?`/`present?`) and JSON serialization (`as_json`)
24
+ - Empty strings treated as `nil`
25
+
26
+ [Unreleased]: https://github.com/unreasonable-magic/unmagic-enum/compare/v0.1.0...HEAD
27
+ [0.1.0]: https://github.com/unreasonable-magic/unmagic-enum/releases/tag/v0.1.0
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Keith Pitt
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.
data/README.md ADDED
@@ -0,0 +1,139 @@
1
+ # Unmagic::Enum
2
+
3
+ Type-safe enums with attributes for Rails applications.
4
+
5
+ ## Features
6
+
7
+ - Type-safe enumeration values with string storage
8
+ - Custom attributes with defaults and aliases
9
+ - ActiveRecord integration with custom column type
10
+ - STI (Single Table Inheritance) support
11
+ - Query methods for checking enum values
12
+ - Duplicate key/value detection
13
+ - Works with symbols, integers, classes as keys
14
+
15
+ ## Installation
16
+
17
+ Add to your Gemfile:
18
+
19
+ ```ruby
20
+ gem 'unmagic-enum'
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ ### Basic Enum
26
+
27
+ ```ruby
28
+ class Status < Unmagic::Enum
29
+ ACTIVE = new("active")
30
+ PENDING = new("pending")
31
+ ARCHIVED = new("archived")
32
+ end
33
+
34
+ # Usage
35
+ status = Status::ACTIVE
36
+ status.active? # => true
37
+ status == "active" # => true
38
+ status.to_s # => "active"
39
+ Status["active"] # => Status::ACTIVE
40
+ ```
41
+
42
+ ### Enum with Attributes
43
+
44
+ ```ruby
45
+ class Priority < Unmagic::Enum
46
+ attribute :label
47
+ attribute :color
48
+ attribute :level, default: 0
49
+
50
+ HIGH = new("high", label: "High Priority", color: "red", level: 3)
51
+ MEDIUM = new("medium", label: "Medium Priority", color: "yellow", level: 2)
52
+ LOW = new("low", label: "Low Priority", color: "green", level: 1)
53
+ end
54
+
55
+ priority = Priority::HIGH
56
+ priority.label # => "High Priority"
57
+ priority.color # => "red"
58
+ priority.level # => 3
59
+ ```
60
+
61
+ ### ActiveRecord Integration
62
+
63
+ ```ruby
64
+ class Message < ApplicationRecord
65
+ class State < Unmagic::Enum
66
+ DRAFT = new("draft")
67
+ SENT = new("sent")
68
+ DELIVERED = new("delivered")
69
+ end
70
+
71
+ # Use the column type for proper serialization
72
+ attribute :state, State.column_type
73
+
74
+ # Create scopes
75
+ scope :delivered, -> { where(state: State::DELIVERED) }
76
+ end
77
+
78
+ # Usage
79
+ message = Message.new(state: "sent")
80
+ message.state # => Message::State::SENT
81
+ message.state.sent? # => true
82
+ ```
83
+
84
+ ### Key/Value Separation
85
+
86
+ Useful when database values differ from code identifiers:
87
+
88
+ ```ruby
89
+ class MessageType < Unmagic::Enum
90
+ USER = new("user") # key and value both "user"
91
+ ENTITY = new("entity", value: "bot") # key: "entity", value: "bot" (legacy DB)
92
+ SYSTEM = new("system", value: "s") # key: "system", value: "s" (short code)
93
+ end
94
+
95
+ MessageType::ENTITY.key # => "entity"
96
+ MessageType::ENTITY.value # => "bot"
97
+ MessageType["bot"] # => MessageType::ENTITY
98
+ ```
99
+
100
+ ### STI Support
101
+
102
+ ```ruby
103
+ class User < ApplicationRecord
104
+ class Type < Unmagic::Enum
105
+ # Pass the actual class - no constantize needed!
106
+ CUSTOMER = new(Customer)
107
+ ADMIN = new(Admin)
108
+ MODERATOR = new(Moderator)
109
+ end
110
+
111
+ attribute :type, Type.column_type
112
+
113
+ def self.find_sti_class(type_name)
114
+ if enum_value = Type[type_name]
115
+ enum_value.key # Returns the class directly
116
+ else
117
+ super
118
+ end
119
+ end
120
+ end
121
+ ```
122
+
123
+ ## Development
124
+
125
+ After checking out the repo, install dependencies and run the tests:
126
+
127
+ ```bash
128
+ bundle install
129
+ bundle exec rake spec
130
+ ```
131
+
132
+ ## Contributing
133
+
134
+ Bug reports and pull requests are welcome on GitHub at
135
+ https://github.com/unreasonable-magic/unmagic-enum.
136
+
137
+ ## License
138
+
139
+ Released under the [MIT License](LICENSE).
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ # ActiveRecord extensions for Unmagic::Enum
4
+ # This module provides database type casting and serialization support
5
+ module Unmagic
6
+ class Enum
7
+ module ActiveRecordExtensions
8
+ class ColumnType < ActiveRecord::Type::Value
9
+ def initialize(enum_class)
10
+ @enum_class = enum_class
11
+ super()
12
+ end
13
+
14
+ def type
15
+ :string
16
+ end
17
+
18
+ # Cast a value to its enum instance. Lenient, mirroring
19
+ # ActiveRecord::Enum::EnumType#cast: an unknown value resolves to nil
20
+ # rather than raising. Rejection of bad input happens eagerly, on
21
+ # assignment, in #assert_valid_value (the same hook Rails enums use).
22
+ def cast(value)
23
+ return nil if value.nil? || value == ''
24
+ return value if value.is_a?(@enum_class)
25
+
26
+ @enum_class[value.to_s]
27
+ end
28
+
29
+ # Deserialize a database value. Lenient like #cast (and like Rails'
30
+ # EnumType, which maps an unknown column value to nil) so that reading a
31
+ # row never raises on data the enum no longer recognises.
32
+ def deserialize(value)
33
+ return nil if value.nil? || value == ''
34
+
35
+ @enum_class[value]
36
+ end
37
+
38
+ # Validate a value at assignment time, the way ActiveRecord::Enum does:
39
+ # ActiveModel::Attribute#with_value_from_user calls this before storing,
40
+ # so an invalid value raises immediately on `record.attr = ...` instead
41
+ # of later, lazily, when the attribute is read. Blank is allowed (becomes
42
+ # nil); an enum instance or a known key/value passes.
43
+ def assert_valid_value(value)
44
+ return if value.nil? || value == ''
45
+ return if value.is_a?(@enum_class)
46
+ return if @enum_class[value]
47
+
48
+ raise Unmagic::Enum::InvalidValueError, "Invalid #{@enum_class.name} value: #{value.inspect}"
49
+ end
50
+
51
+ # Serialize value for database storage
52
+ def serialize(value)
53
+ return nil if value.nil?
54
+
55
+ value.to_s
56
+ end
57
+
58
+ # Check if the value has changed
59
+ def changed_in_place?(raw_old_value, new_value)
60
+ raw_old_value.to_s != new_value.to_s
61
+ end
62
+ end
63
+
64
+ module ClassMethods
65
+ # For ActiveRecord attribute type definition
66
+ def column_type
67
+ @column_type ||= Unmagic::Enum::ActiveRecordExtensions::ColumnType.new(self)
68
+ end
69
+ end
70
+
71
+ module InstanceMethods
72
+ # Support for ActiveRecord type casting in SQL queries
73
+ # This allows Enum instances to be used directly in where clauses
74
+ def to_type_for_database
75
+ @value
76
+ end
77
+ end
78
+
79
+ def self.included(base)
80
+ base.extend(ClassMethods)
81
+ base.include(InstanceMethods)
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unmagic
4
+ class Enum
5
+ # Current version of the unmagic-enum gem
6
+ VERSION = "0.1.0"
7
+ end
8
+ end
@@ -0,0 +1,355 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+
5
+ require 'unmagic/enum/version'
6
+
7
+ # ActiveRecord is optional. We eagerly (but tolerantly) require it so that the
8
+ # integration below activates regardless of gem load order in a Rails app. When
9
+ # ActiveRecord isn't installed the LoadError is swallowed and the gem works as a
10
+ # plain Ruby enum.
11
+ begin
12
+ require 'active_record'
13
+ rescue LoadError
14
+ # ActiveRecord is optional
15
+ end
16
+
17
+ module Unmagic
18
+ # Base class for creating type-safe enums with string values
19
+ #
20
+ # Basic usage:
21
+ # class Status < Unmagic::Enum
22
+ # ACTIVE = new("active")
23
+ # PENDING = new("pending")
24
+ # ARCHIVED = new("archived")
25
+ # end
26
+ #
27
+ # With attributes:
28
+ # class Priority < Unmagic::Enum
29
+ # attribute :label
30
+ # attribute :color
31
+ #
32
+ # HIGH = new("high", label: "High Priority", color: "red")
33
+ # MEDIUM = new("medium", label: "Medium Priority", color: "yellow")
34
+ # LOW = new("low", label: "Low Priority", color: "green")
35
+ # end
36
+ #
37
+ # Key/Value separation (useful for database migrations):
38
+ # class MessageType < Unmagic::Enum
39
+ # # The key is what you use in code, value is what's stored in DB
40
+ # USER = new("user") # key and value both "user"
41
+ # ENTITY = new("entity", value: "bot") # key: "entity", value: "bot" (legacy DB)
42
+ # SYSTEM = new("system", value: "s") # key: "system", value: "s" (short code)
43
+ # end
44
+ #
45
+ # Different key types (symbols, integers, classes):
46
+ # class MixedEnum < Unmagic::Enum
47
+ # # Keys preserve their original type
48
+ # SYMBOL = new(:active) # key is :active (Symbol)
49
+ # INTEGER = new(1, value: "one") # key is 1 (Integer)
50
+ # CLASS = new(User) # key is User (Class)
51
+ # end
52
+ #
53
+ # STI (Single Table Inheritance) integration:
54
+ # class User < ApplicationRecord
55
+ # class Type < Unmagic::Enum
56
+ # # Pass the actual class - no constantize needed!
57
+ # CUSTOMER = new(Customer) # key: Customer class, value: "Customer"
58
+ # ADMIN = new(Admin, value: "a") # key: Admin class, value: "a"
59
+ # MODERATOR = new(Moderator) # key: Moderator class, value: "Moderator"
60
+ # end
61
+ #
62
+ # attribute :type, Type.column_type
63
+ #
64
+ # # Clean STI integration - enum.key returns the actual class
65
+ # def self.find_sti_class(type_name)
66
+ # if enum_value = Type[type_name]
67
+ # enum_value.key # Returns the class directly, no constantize!
68
+ # else
69
+ # super
70
+ # end
71
+ # end
72
+ #
73
+ # def self.sti_name
74
+ # Type.all.find { |e| e.key == self }&.value || name
75
+ # end
76
+ # end
77
+ #
78
+ # Usage patterns:
79
+ # status = Status::ACTIVE
80
+ # status.active? # => true (query method)
81
+ # status == "active" # => true (string equality)
82
+ # status == :active # => false (symbols don't match strings)
83
+ # status.to_s # => "active" (for database)
84
+ #
85
+ # # Lookups work with any type
86
+ # Status["active"] # => Status::ACTIVE
87
+ # MixedEnum[:active] # => MixedEnum::SYMBOL
88
+ # MixedEnum[1] # => MixedEnum::INTEGER
89
+ # MixedEnum[User] # => MixedEnum::CLASS
90
+ #
91
+ class Enum
92
+ class InvalidValueError < StandardError; end
93
+ class ReservedValueError < StandardError; end
94
+
95
+ class << self
96
+ # Get enum instances dynamically from constants
97
+ def instances_by_key
98
+ # Build hash from constants each time (stateless)
99
+ constants.each_with_object({}) do |const_name, hash|
100
+ const = const_get(const_name)
101
+ next unless const.is_a?(Unmagic::Enum)
102
+
103
+ hash[const.key_string] = const
104
+ end
105
+ end
106
+
107
+ # Get enum instances by value (database value)
108
+ def instances_by_value
109
+ # Build hash from constants each time (stateless)
110
+ constants.each_with_object({}) do |const_name, hash|
111
+ const = const_get(const_name)
112
+ next unless const.is_a?(Unmagic::Enum)
113
+
114
+ hash[const.value] = const
115
+ end
116
+ end
117
+
118
+ # Backward compatibility - instances is an alias for instances_by_key
119
+ def instances
120
+ instances_by_key
121
+ end
122
+
123
+ # Declare attributes for this enum class with options
124
+ def attribute(*names, **options)
125
+ @attribute_metadata ||= {}
126
+
127
+ names.each do |name|
128
+ # Store metadata for this attribute
129
+ @attribute_metadata[name] = options
130
+
131
+ # Create the reader method
132
+ attr_reader name
133
+
134
+ # Create alias if specified
135
+ next unless options[:alias]
136
+
137
+ aliases = Array(options[:alias])
138
+ aliases.each do |alias_name|
139
+ alias_method alias_name, name
140
+
141
+ # Track reserved method names to prevent conflicts
142
+ @reserved_methods ||= Set.new
143
+ @reserved_methods.add(alias_name.to_s)
144
+ end
145
+ end
146
+ end
147
+
148
+ # Get declared attributes (just the names)
149
+ def attributes
150
+ @attribute_metadata&.keys || []
151
+ end
152
+
153
+ # Get metadata for an attribute
154
+ def attribute_metadata
155
+ @attribute_metadata || {}
156
+ end
157
+
158
+ # Get reserved method names (from aliases)
159
+ def reserved_methods
160
+ @reserved_methods || Set.new
161
+ end
162
+
163
+ # Get all enum values
164
+ def all
165
+ instances.values
166
+ end
167
+
168
+ # Look up enum by key or value
169
+ def [](lookup)
170
+ lookup_str = lookup.to_s
171
+ # Try key first, then value
172
+ instances_by_key[lookup_str] || instances_by_value[lookup_str]
173
+ end
174
+
175
+ # Alias for [] to support dry-initializer type coercion
176
+ # dry-initializer expects types to respond to .call with 1 argument
177
+ def call(value)
178
+ self[value]
179
+ end
180
+
181
+ # Get all valid database values (useful for validations)
182
+ def values
183
+ instances_by_value.keys
184
+ end
185
+
186
+ # Get all valid keys (identifiers used in code)
187
+ def keys
188
+ instances_by_key.keys
189
+ end
190
+
191
+ # Check if a value is valid for this enum
192
+ def valid?(value)
193
+ case value
194
+ when self
195
+ true
196
+ when String
197
+ instances.key?(value)
198
+ else
199
+ false
200
+ end
201
+ end
202
+
203
+ # Ensure each subclass has its own metadata
204
+ def inherited(subclass)
205
+ super
206
+ # Initialize metadata for attributes and reserved methods
207
+ subclass.instance_variable_set(:@attribute_metadata, {})
208
+ subclass.instance_variable_set(:@reserved_methods, Set.new)
209
+ end
210
+ end
211
+
212
+ # The key (identifier used in code - preserves original type)
213
+ attr_reader :key
214
+
215
+ # The key as a string (used for lookups and comparisons)
216
+ attr_reader :key_string
217
+
218
+ # The value (what gets stored in database)
219
+ attr_reader :value
220
+
221
+ # Override equality to work with strings and same-class enums
222
+ def ==(other)
223
+ if other.is_a?(Unmagic::Enum)
224
+ # Only equal if same class and same value
225
+ other.class == self.class && @value == other.value
226
+ elsif other.is_a?(String)
227
+ # Check both key_string and value for flexibility
228
+ [@key_string, @value].include?(other)
229
+ else
230
+ # Check if it matches the original key (for symbols, classes, etc.)
231
+ @key == other
232
+ end
233
+ end
234
+
235
+ # Ensure different enum classes don't match
236
+ def eql?(other)
237
+ other.is_a?(self.class) && to_s == other.to_s
238
+ end
239
+
240
+ # Hash code based on string value and class
241
+ def hash
242
+ [self.class, to_s].hash
243
+ end
244
+
245
+ # Human-readable inspect showing how to reference this enum in code
246
+ def inspect
247
+ # Find the constant name for this enum instance
248
+ constant_name = self.class.constants.find do |const|
249
+ self.class.const_get(const) == self
250
+ end
251
+
252
+ if constant_name
253
+ "#{self.class.name}::#{constant_name}"
254
+ else
255
+ # Fallback showing how to access via bracket notation
256
+ # Show the original key type for clarity
257
+ "#{self.class.name}[#{@key.inspect}]"
258
+ end
259
+ end
260
+
261
+ # Allow enum to be used directly in database queries and assignments
262
+ def to_str
263
+ to_s
264
+ end
265
+
266
+ # Return the database value (for serialization)
267
+ def to_s
268
+ @value
269
+ end
270
+
271
+ # Return the value when used in JSON
272
+ def as_json
273
+ to_s
274
+ end
275
+
276
+ # Initialize the enum with key and optional value
277
+ def initialize(key, **attributes)
278
+ @key = key # Keep original type (class, symbol, integer, string, etc.)
279
+ @key_string = key.to_s # String version for lookups and comparisons
280
+
281
+ # Extract the special 'value' option, default to string version of key
282
+ @value = attributes.delete(:value)&.to_s || @key_string
283
+
284
+ # Check for duplicate keys
285
+ if self.class.instances_by_key[@key_string]
286
+ raise InvalidValueError.new("Enum key '#{@key_string}' has already been defined")
287
+ end
288
+
289
+ # Check for duplicate values
290
+ existing = self.class.instances_by_value[@value]
291
+ if existing
292
+ raise InvalidValueError.new("Enum value '#{@value}' has already been defined for key '#{existing.key_string}'")
293
+ end
294
+
295
+ # Check for conflicts with reserved methods (using string key for query methods)
296
+ key_method = "#{@key_string}?"
297
+ if self.class.reserved_methods.include?(key_method)
298
+ raise ReservedValueError.new("Cannot create enum key '#{@key_string}' because it would conflict with alias method '#{key_method}'")
299
+ end
300
+
301
+ # Set declared attributes with defaults
302
+ self.class.attribute_metadata.each do |attr, metadata|
303
+ value = if attributes.key?(attr)
304
+ attributes[attr]
305
+ elsif metadata.key?(:default)
306
+ metadata[:default]
307
+ else
308
+ nil
309
+ end
310
+ instance_variable_set("@#{attr}", value)
311
+ end
312
+
313
+ # Warn about undeclared attributes in development
314
+ if defined?(Rails) && Rails.env.development?
315
+ extra_attrs = attributes.keys - self.class.attributes
316
+ if extra_attrs.any?
317
+ warn "[Unmagic::Enum] Undeclared attributes passed to #{self.class.name}: #{extra_attrs.join(', ')}"
318
+ end
319
+ end
320
+
321
+ freeze
322
+ end
323
+
324
+ # Implement query methods like `user?` for checking enum keys
325
+ def method_missing(method_name, *args)
326
+ if method_name.to_s.end_with?('?')
327
+ key_to_check = method_name.to_s[0..-2] # Remove the '?'
328
+ @key_string == key_to_check # Compare string versions
329
+ else
330
+ super
331
+ end
332
+ end
333
+
334
+ # Properly handle respond_to? for query methods
335
+ def respond_to_missing?(method_name, include_private = false)
336
+ method_name.to_s.end_with?('?') || super
337
+ end
338
+
339
+ # Rails presence validation support - enums are never blank
340
+ def blank?
341
+ false
342
+ end
343
+
344
+ # Rails presence validation support - enums are always present
345
+ def present?
346
+ true
347
+ end
348
+
349
+ # Load ActiveRecord extensions if ActiveRecord is available
350
+ if defined?(ActiveRecord)
351
+ require 'unmagic/enum/active_record_extensions'
352
+ include ActiveRecordExtensions
353
+ end
354
+ end
355
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "unmagic/enum"
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: unmagic-enum
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Keith Pitt
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-06-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '7.0'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 7.0.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '7.0'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 7.0.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: rspec
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '3.12'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '3.12'
47
+ - !ruby/object:Gem::Dependency
48
+ name: rake
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '13.0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '13.0'
61
+ description: A powerful enum system providing type-safe enumerations with custom attributes,
62
+ STI integration, and ActiveRecord support
63
+ email:
64
+ - keith@unreasonable-magic.com
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - CHANGELOG.md
70
+ - LICENSE
71
+ - README.md
72
+ - lib/unmagic/enum.rb
73
+ - lib/unmagic/enum/active_record_extensions.rb
74
+ - lib/unmagic/enum/version.rb
75
+ - lib/unmagic_enum.rb
76
+ homepage: https://github.com/unreasonable-magic/unmagic-enum
77
+ licenses:
78
+ - MIT
79
+ metadata:
80
+ homepage_uri: https://github.com/unreasonable-magic/unmagic-enum
81
+ source_code_uri: https://github.com/unreasonable-magic/unmagic-enum
82
+ changelog_uri: https://github.com/unreasonable-magic/unmagic-enum/blob/main/CHANGELOG.md
83
+ post_install_message:
84
+ rdoc_options: []
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '3.0'
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ requirements: []
98
+ rubygems_version: 3.5.22
99
+ signing_key:
100
+ specification_version: 4
101
+ summary: Type-safe enums with attributes for Rails applications
102
+ test_files: []