philiprehberger-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 +7 -0
- data/CHANGELOG.md +22 -0
- data/LICENSE +21 -0
- data/README.md +122 -0
- data/lib/philiprehberger/enum/version.rb +7 -0
- data/lib/philiprehberger/enum.rb +159 -0
- metadata +55 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 1c57d423a66fad422a2a57e996e53a2b0bcf32e11de057e0dc0eeeac2dde3a4f
|
|
4
|
+
data.tar.gz: 6c9121f4a1be73222f09a58df5d42b305f75a47eb6d662bac79c30648010c568
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: b205fdb4a98c0188db035118d32f8d7d95a56fbf8dc3638317dfeeb56e1cb77fa36e02b02573f937d8aeba84a4eedf2bd6659b67ccc7226f56d7c3a89cf02835
|
|
7
|
+
data.tar.gz: 9b838f375c6141ce9a2ab3d4fc3bb1482da6c2b17b08eda07b639641072714a66aa85ee09503a39c54110ef508fecb4dcf8da10894dffed18687f4dc7daa5a37
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this gem 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-03-21
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Initial release
|
|
15
|
+
- Type-safe enum base class with `member` class method
|
|
16
|
+
- Automatic ordinals based on declaration order
|
|
17
|
+
- Custom values via `value:` keyword parameter
|
|
18
|
+
- Lookup methods: `from_name`, `from_value`, `from_string`, `valid?`
|
|
19
|
+
- Comparable by ordinal for sorting and comparison
|
|
20
|
+
- Pattern matching support via `deconstruct_keys`
|
|
21
|
+
- JSON serialization via `to_json`
|
|
22
|
+
- Frozen singleton members for immutability
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 philiprehberger
|
|
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,122 @@
|
|
|
1
|
+
# philiprehberger-enum
|
|
2
|
+
|
|
3
|
+
[](https://github.com/philiprehberger/rb-enum/actions/workflows/ci.yml)
|
|
4
|
+
[](https://rubygems.org/gems/philiprehberger-enum)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
Type-safe enumerations with ordinals, custom values, and pattern matching
|
|
8
|
+
|
|
9
|
+
## Requirements
|
|
10
|
+
|
|
11
|
+
- Ruby >= 3.1
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
Add to your Gemfile:
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
gem 'philiprehberger-enum'
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or install directly:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
gem install philiprehberger-enum
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
require 'philiprehberger/enum'
|
|
31
|
+
|
|
32
|
+
class Status < Philiprehberger::Enum
|
|
33
|
+
member :draft
|
|
34
|
+
member :published
|
|
35
|
+
member :archived
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
Status::DRAFT.name # => :draft
|
|
39
|
+
Status::DRAFT.ordinal # => 0
|
|
40
|
+
Status.members # => [DRAFT, PUBLISHED, ARCHIVED]
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Custom Values
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
class HttpCode < Philiprehberger::Enum
|
|
47
|
+
member :ok, value: 200
|
|
48
|
+
member :not_found, value: 404
|
|
49
|
+
member :server_error, value: 500
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
HttpCode::OK.value # => 200
|
|
53
|
+
HttpCode.from_value(404) # => HttpCode::NOT_FOUND
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Lookup Methods
|
|
57
|
+
|
|
58
|
+
```ruby
|
|
59
|
+
Status.from_name(:draft) # => Status::DRAFT
|
|
60
|
+
Status.from_string('draft') # => Status::DRAFT
|
|
61
|
+
Status.valid?(:draft) # => true
|
|
62
|
+
Status.valid?(:unknown) # => false
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Comparison
|
|
66
|
+
|
|
67
|
+
Members are comparable by ordinal:
|
|
68
|
+
|
|
69
|
+
```ruby
|
|
70
|
+
Status::DRAFT < Status::PUBLISHED # => true
|
|
71
|
+
Status.members.sort # sorted by declaration order
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Pattern Matching
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
77
|
+
case Status::DRAFT
|
|
78
|
+
in { name: :draft }
|
|
79
|
+
'is draft'
|
|
80
|
+
in { name: :published }
|
|
81
|
+
'is published'
|
|
82
|
+
end
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Serialization
|
|
86
|
+
|
|
87
|
+
```ruby
|
|
88
|
+
Status::DRAFT.to_s # => "draft"
|
|
89
|
+
Status::DRAFT.to_json # => '{"name":"draft","ordinal":0,"value":null}'
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## API
|
|
93
|
+
|
|
94
|
+
### `Philiprehberger::Enum` (base class)
|
|
95
|
+
|
|
96
|
+
| Method | Description |
|
|
97
|
+
|--------|-------------|
|
|
98
|
+
| `.member(name, value: nil)` | Define a new enum member with optional custom value |
|
|
99
|
+
| `.members` | Return all members in declaration order |
|
|
100
|
+
| `.from_name(name)` | Look up a member by symbol or string name |
|
|
101
|
+
| `.from_string(string)` | Look up a member by string name |
|
|
102
|
+
| `.from_value(val)` | Look up a member by custom value |
|
|
103
|
+
| `.valid?(name)` | Check if a name is a valid member |
|
|
104
|
+
| `#name` | Return the member name as a symbol |
|
|
105
|
+
| `#ordinal` | Return the ordinal position |
|
|
106
|
+
| `#value` | Return the custom value, or nil |
|
|
107
|
+
| `#to_s` | Return the member name as a string |
|
|
108
|
+
| `#to_json` | Serialize to JSON with name, ordinal, and value |
|
|
109
|
+
| `#deconstruct_keys(keys)` | Pattern matching support |
|
|
110
|
+
| `#<=>(other)` | Compare by ordinal |
|
|
111
|
+
|
|
112
|
+
## Development
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
bundle install
|
|
116
|
+
bundle exec rspec # Run tests
|
|
117
|
+
bundle exec rubocop # Check code style
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## License
|
|
121
|
+
|
|
122
|
+
MIT
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'enum/version'
|
|
4
|
+
require 'json'
|
|
5
|
+
|
|
6
|
+
module Philiprehberger
|
|
7
|
+
# Type-safe enumerations with ordinals, custom values, and pattern matching.
|
|
8
|
+
#
|
|
9
|
+
# Subclass Enum and use the `member` class method to define members:
|
|
10
|
+
#
|
|
11
|
+
# class Status < Philiprehberger::Enum
|
|
12
|
+
# member :draft
|
|
13
|
+
# member :published
|
|
14
|
+
# member :archived
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
class Enum
|
|
18
|
+
include Comparable
|
|
19
|
+
|
|
20
|
+
class Error < StandardError; end
|
|
21
|
+
|
|
22
|
+
# @return [Symbol] the member name
|
|
23
|
+
attr_reader :name
|
|
24
|
+
|
|
25
|
+
# @return [Integer] the ordinal position (declaration order)
|
|
26
|
+
attr_reader :ordinal
|
|
27
|
+
|
|
28
|
+
# @return [Object, nil] the custom value, or nil if not set
|
|
29
|
+
attr_reader :value
|
|
30
|
+
|
|
31
|
+
# @param name [Symbol] the member name
|
|
32
|
+
# @param ordinal [Integer] the ordinal position
|
|
33
|
+
# @param value [Object, nil] an optional custom value
|
|
34
|
+
def initialize(name, ordinal, value)
|
|
35
|
+
@name = name
|
|
36
|
+
@ordinal = ordinal
|
|
37
|
+
@value = value
|
|
38
|
+
freeze
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Compare members by ordinal
|
|
42
|
+
#
|
|
43
|
+
# @param other [Enum] another member of the same enum class
|
|
44
|
+
# @return [Integer, nil] -1, 0, 1, or nil if not comparable
|
|
45
|
+
def <=>(other)
|
|
46
|
+
return nil unless other.is_a?(self.class)
|
|
47
|
+
|
|
48
|
+
ordinal <=> other.ordinal
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# @return [String] the member name as a string
|
|
52
|
+
def to_s
|
|
53
|
+
name.to_s
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# @return [String] a human-readable representation
|
|
57
|
+
def inspect
|
|
58
|
+
"#<#{self.class} #{name}>"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Serialize to a JSON-compatible hash
|
|
62
|
+
#
|
|
63
|
+
# @return [String] JSON string with name, ordinal, and value
|
|
64
|
+
def to_json(*args)
|
|
65
|
+
{ name: name, ordinal: ordinal, value: value }.to_json(*args)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Support Ruby 3.x pattern matching with `in` expressions
|
|
69
|
+
#
|
|
70
|
+
# @param keys [Array<Symbol>, nil] the keys to deconstruct
|
|
71
|
+
# @return [Hash] hash of requested keys
|
|
72
|
+
def deconstruct_keys(keys)
|
|
73
|
+
h = { name: name, ordinal: ordinal, value: value }
|
|
74
|
+
keys ? h.slice(*keys) : h
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
class << self
|
|
78
|
+
# Define a new member on this enum class
|
|
79
|
+
#
|
|
80
|
+
# @param name [Symbol] the member name
|
|
81
|
+
# @param value [Object, nil] an optional custom value
|
|
82
|
+
# @return [Enum] the created member
|
|
83
|
+
# @raise [Error] if the enum is frozen or name is already defined
|
|
84
|
+
def member(name, value: nil)
|
|
85
|
+
raise Error, "cannot add members to #{self} after freeze" if @frozen
|
|
86
|
+
|
|
87
|
+
name = name.to_sym
|
|
88
|
+
raise Error, "member #{name} is already defined on #{self}" if member_registry.key?(name)
|
|
89
|
+
|
|
90
|
+
ord = member_registry.size
|
|
91
|
+
instance = new(name, ord, value)
|
|
92
|
+
|
|
93
|
+
member_registry[name] = instance
|
|
94
|
+
const_set(name.upcase, instance)
|
|
95
|
+
|
|
96
|
+
instance
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Return all members in declaration order
|
|
100
|
+
#
|
|
101
|
+
# @return [Array<Enum>] frozen array of all members
|
|
102
|
+
def members
|
|
103
|
+
freeze_members!
|
|
104
|
+
member_registry.values.freeze
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Look up a member by its name
|
|
108
|
+
#
|
|
109
|
+
# @param name [Symbol, String] the member name
|
|
110
|
+
# @return [Enum, nil] the member, or nil if not found
|
|
111
|
+
def from_name(name)
|
|
112
|
+
freeze_members!
|
|
113
|
+
member_registry[name.to_sym]
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Look up a member by its string representation
|
|
117
|
+
#
|
|
118
|
+
# @param string [String] the member name as a string
|
|
119
|
+
# @return [Enum, nil] the member, or nil if not found
|
|
120
|
+
def from_string(string)
|
|
121
|
+
from_name(string)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Look up a member by its custom value
|
|
125
|
+
#
|
|
126
|
+
# @param val [Object] the value to search for
|
|
127
|
+
# @return [Enum, nil] the member, or nil if not found
|
|
128
|
+
def from_value(val)
|
|
129
|
+
freeze_members!
|
|
130
|
+
member_registry.values.find { |m| m.value == val }
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Check if a name is a valid member
|
|
134
|
+
#
|
|
135
|
+
# @param name [Symbol, String] the member name
|
|
136
|
+
# @return [Boolean] true if the name is a valid member
|
|
137
|
+
def valid?(name)
|
|
138
|
+
freeze_members!
|
|
139
|
+
member_registry.key?(name.to_sym)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
private
|
|
143
|
+
|
|
144
|
+
def inherited(subclass)
|
|
145
|
+
super
|
|
146
|
+
subclass.instance_variable_set(:@member_registry, {})
|
|
147
|
+
subclass.instance_variable_set(:@frozen, false)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def member_registry
|
|
151
|
+
@member_registry ||= {}
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def freeze_members!
|
|
155
|
+
@frozen = true
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: philiprehberger-enum
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Philip Rehberger
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-03-22 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: Define type-safe enums in Ruby with automatic ordinals, custom values,
|
|
14
|
+
lookup methods, and Ruby 3.x pattern matching support. A cleaner alternative to
|
|
15
|
+
ad-hoc constants and symbol sets.
|
|
16
|
+
email:
|
|
17
|
+
- me@philiprehberger.com
|
|
18
|
+
executables: []
|
|
19
|
+
extensions: []
|
|
20
|
+
extra_rdoc_files: []
|
|
21
|
+
files:
|
|
22
|
+
- CHANGELOG.md
|
|
23
|
+
- LICENSE
|
|
24
|
+
- README.md
|
|
25
|
+
- lib/philiprehberger/enum.rb
|
|
26
|
+
- lib/philiprehberger/enum/version.rb
|
|
27
|
+
homepage: https://github.com/philiprehberger/rb-enum
|
|
28
|
+
licenses:
|
|
29
|
+
- MIT
|
|
30
|
+
metadata:
|
|
31
|
+
homepage_uri: https://github.com/philiprehberger/rb-enum
|
|
32
|
+
source_code_uri: https://github.com/philiprehberger/rb-enum
|
|
33
|
+
changelog_uri: https://github.com/philiprehberger/rb-enum/blob/main/CHANGELOG.md
|
|
34
|
+
bug_tracker_uri: https://github.com/philiprehberger/rb-enum/issues
|
|
35
|
+
rubygems_mfa_required: 'true'
|
|
36
|
+
post_install_message:
|
|
37
|
+
rdoc_options: []
|
|
38
|
+
require_paths:
|
|
39
|
+
- lib
|
|
40
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
41
|
+
requirements:
|
|
42
|
+
- - ">="
|
|
43
|
+
- !ruby/object:Gem::Version
|
|
44
|
+
version: 3.1.0
|
|
45
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
46
|
+
requirements:
|
|
47
|
+
- - ">="
|
|
48
|
+
- !ruby/object:Gem::Version
|
|
49
|
+
version: '0'
|
|
50
|
+
requirements: []
|
|
51
|
+
rubygems_version: 3.5.22
|
|
52
|
+
signing_key:
|
|
53
|
+
specification_version: 4
|
|
54
|
+
summary: Type-safe enumerations with ordinals, custom values, and pattern matching
|
|
55
|
+
test_files: []
|