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 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
+ [![Tests](https://github.com/philiprehberger/rb-enum/actions/workflows/ci.yml/badge.svg)](https://github.com/philiprehberger/rb-enum/actions/workflows/ci.yml)
4
+ [![Gem Version](https://badge.fury.io/rb/philiprehberger-enum.svg)](https://rubygems.org/gems/philiprehberger-enum)
5
+ [![License](https://img.shields.io/github/license/philiprehberger/rb-enum)](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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Philiprehberger
4
+ class Enum
5
+ VERSION = '0.1.0'
6
+ end
7
+ end
@@ -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: []