ruby-enum 1.0.0 → 1.2.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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/Dangerfile +2 -0
  4. data/Gemfile +6 -5
  5. data/Gemfile.lock +128 -93
  6. data/README.md +37 -4
  7. data/RELEASING.md +14 -5
  8. data/Rakefile +10 -0
  9. data/UPGRADING.md +39 -0
  10. data/benchmarks/basic.rb +61 -0
  11. data/benchmarks/inheritance.rb +53 -0
  12. data/lib/ruby-enum/enum.rb +65 -25
  13. data/lib/ruby-enum/errors/base.rb +2 -2
  14. data/lib/ruby-enum/version.rb +1 -1
  15. data/pkg/ruby-enum-0.8.0.gem +0 -0
  16. data/pkg/ruby-enum-0.9.0.gem +0 -0
  17. data/pkg/ruby-enum-1.1.0.gem +0 -0
  18. data/spec/ruby-enum/enum/case_spec.rb +35 -34
  19. data/spec/ruby-enum/enum_spec.rb +212 -39
  20. data/spec/spec_helper.rb +2 -2
  21. data/spec_i18n/Gemfile.lock +26 -16
  22. data/spec_i18n/spec/i18n_spec.rb +9 -7
  23. metadata +8 -33
  24. data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_asc.png +0 -0
  25. data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_asc_disabled.png +0 -0
  26. data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_both.png +0 -0
  27. data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_desc.png +0 -0
  28. data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_desc_disabled.png +0 -0
  29. data/coverage/assets/0.12.3/application.css +0 -1
  30. data/coverage/assets/0.12.3/application.js +0 -7
  31. data/coverage/assets/0.12.3/colorbox/border.png +0 -0
  32. data/coverage/assets/0.12.3/colorbox/controls.png +0 -0
  33. data/coverage/assets/0.12.3/colorbox/loading.gif +0 -0
  34. data/coverage/assets/0.12.3/colorbox/loading_background.png +0 -0
  35. data/coverage/assets/0.12.3/favicon_green.png +0 -0
  36. data/coverage/assets/0.12.3/favicon_red.png +0 -0
  37. data/coverage/assets/0.12.3/favicon_yellow.png +0 -0
  38. data/coverage/assets/0.12.3/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  39. data/coverage/assets/0.12.3/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  40. data/coverage/assets/0.12.3/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  41. data/coverage/assets/0.12.3/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  42. data/coverage/assets/0.12.3/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  43. data/coverage/assets/0.12.3/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  44. data/coverage/assets/0.12.3/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  45. data/coverage/assets/0.12.3/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  46. data/coverage/assets/0.12.3/images/ui-icons_222222_256x240.png +0 -0
  47. data/coverage/assets/0.12.3/images/ui-icons_2e83ff_256x240.png +0 -0
  48. data/coverage/assets/0.12.3/images/ui-icons_454545_256x240.png +0 -0
  49. data/coverage/assets/0.12.3/images/ui-icons_888888_256x240.png +0 -0
  50. data/coverage/assets/0.12.3/images/ui-icons_cd0a0a_256x240.png +0 -0
  51. data/coverage/assets/0.12.3/loading.gif +0 -0
  52. data/coverage/assets/0.12.3/magnify.png +0 -0
  53. data/coverage/index.html +0 -8785
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aca947b7b06abbc0504549ab9fa43e99be4d3e0dc408a92b8e10efea700d9bf7
4
- data.tar.gz: 7e57ab06229ac0552b984630493f04dd98a3579b7ca8151c819dc792417d505f
3
+ metadata.gz: 0424fcb26d8fc260165a669fa7be1d83e41a3a8e967ad606c5eb72abb656b349
4
+ data.tar.gz: 2ea8e9dc094a9612447679c7d5bebf88ac466b1c1b9a5f666d3ce5a8d0981965
5
5
  SHA512:
6
- metadata.gz: f626bd0c2c072f17734527253a8ae95e96dc0631779f6d56b398c540a9d63c3b41eb000647d276d81f859ae92f11e5a0a74e9966cf96f1796cad5ef172c42239
7
- data.tar.gz: 2d58616dc04acf94a9a7e65029fe6422224717bb849cd07513782206ecc543b4d478a7d539b5fa1d434ab3b841430772406cd112438152541242d32a3081e38e
6
+ metadata.gz: 024aac1db4f01d3528a1130817e3db22e7e413d598e99d5dc333a040057896129420193731cec1706922a5c462322809e4e6ce5de2d871f1d2492e67990b56ab
7
+ data.tar.gz: c8a5b49346df275afd18502410a9e24a5806fdfdb0d31d2a7a53783edf8b592b3fe27789ca19d26f32d3937142f40c561b2a9d87ef5cff66e9aa7d05c3054cf6
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ### 1.2.0 (2026/8/14)
2
+
3
+ * [#49](https://github.com/dblock/ruby-enum/issues/49): Fixed `NoMethodError` on `keys`, `values`, `key?`, `value?`, `key`, `value`, `to_h`, `parse` and `each` when a subclass defines no enums of its own - [@dblock](https://github.com/dblock).
4
+ * [#49](https://github.com/dblock/ruby-enum/issues/49): `keys`, `key?`, `value?`, `key`, `value`, `to_h`, `parse` and `each` now include enums defined in a superclass, matching the existing behavior of `values` - [@dblock](https://github.com/dblock).
5
+ * [#61](https://github.com/dblock/ruby-enum/pull/61): Document performance overhead in README, add `rake benchmark:basic` - [@dblock](https://github.com/dblock).
6
+
7
+ ### 1.1.0 (2026/6/20)
8
+
9
+ * [#56](https://github.com/dblock/ruby-enum/pull/56): Fix `DuplicateKeyError` when the enum class is reloaded - [@flvrone](https://github.com/flvrone).
10
+ * [#54](https://github.com/dblock/ruby-enum/pull/54): Add support for Ruby 4.0 - [@dblock](https://github.com/dblock).
11
+ * [#53](https://github.com/dblock/ruby-enum/pull/53): Replace code climate with coveralls - [@dblock](https://github.com/dblock).
12
+
1
13
  ### 1.0.0 (2023/01/10)
2
14
 
3
15
  * [#41](https://github.com/dblock/ruby-enum/pull/41): Make i18n dependency optional - [@peterfication](https://github.com/peterfication).
data/Dangerfile CHANGED
@@ -1,4 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ danger.import_dangerfile(gem: 'danger-pr-comment')
4
+
3
5
  changelog.check!
4
6
  toc.check!
data/Gemfile CHANGED
@@ -8,14 +8,15 @@ gem 'rake'
8
8
 
9
9
  group :development, :test do
10
10
  gem 'danger'
11
- gem 'danger-changelog', '0.6.1'
12
- gem 'danger-toc', '0.2.0'
13
- gem 'rspec', '~> 3.0'
14
- gem 'rubocop', '~> 1.0'
11
+ gem 'danger-changelog'
12
+ gem 'danger-pr-comment'
13
+ gem 'danger-toc'
14
+ gem 'rspec'
15
+ gem 'rubocop', '1.82.1'
15
16
  gem 'rubocop-rake'
16
17
  gem 'rubocop-rspec'
17
18
  end
18
19
 
19
20
  group :test do
20
- gem 'simplecov', require: false
21
+ gem 'coveralls_reborn', require: false
21
22
  end
data/Gemfile.lock CHANGED
@@ -1,166 +1,201 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ruby-enum (1.0.0)
4
+ ruby-enum (1.2.0)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
8
8
  specs:
9
- activesupport (7.1.2)
9
+ activesupport (8.1.1)
10
10
  base64
11
11
  bigdecimal
12
- concurrent-ruby (~> 1.0, >= 1.0.2)
12
+ concurrent-ruby (~> 1.0, >= 1.3.1)
13
13
  connection_pool (>= 2.2.5)
14
14
  drb
15
15
  i18n (>= 1.6, < 2)
16
+ json
17
+ logger (>= 1.4.2)
16
18
  minitest (>= 5.1)
17
- mutex_m
18
- tzinfo (~> 2.0)
19
- addressable (2.8.6)
20
- public_suffix (>= 2.0.2, < 6.0)
21
- ast (2.4.2)
22
- base64 (0.2.0)
23
- bigdecimal (3.1.5)
19
+ securerandom (>= 0.3)
20
+ tzinfo (~> 2.0, >= 2.0.5)
21
+ uri (>= 0.13.1)
22
+ addressable (2.8.8)
23
+ public_suffix (>= 2.0.2, < 8.0)
24
+ ast (2.4.3)
25
+ base64 (0.3.0)
26
+ bigdecimal (4.0.1)
24
27
  claide (1.1.0)
25
28
  claide-plugins (0.9.2)
26
29
  cork
27
30
  nap
28
31
  open4 (~> 1.3)
29
- colored2 (3.1.2)
30
- concurrent-ruby (1.2.2)
31
- connection_pool (2.4.1)
32
- cork (0.3.0)
33
- colored2 (~> 3.1)
34
- danger (9.4.2)
32
+ colored (1.2)
33
+ colored2 (4.0.3)
34
+ concurrent-ruby (1.3.6)
35
+ connection_pool (3.0.2)
36
+ cork (0.2.0)
37
+ colored (~> 1.2)
38
+ coveralls_reborn (0.29.0)
39
+ simplecov (~> 0.22.0)
40
+ term-ansicolor (~> 1.7)
41
+ thor (~> 1.2)
42
+ tins (~> 1.32)
43
+ danger (9.5.3)
44
+ base64 (~> 0.2)
35
45
  claide (~> 1.0)
36
46
  claide-plugins (>= 0.9.2)
37
- colored2 (~> 3.1)
47
+ colored2 (>= 3.1, < 5)
38
48
  cork (~> 0.1)
39
49
  faraday (>= 0.9.0, < 3.0)
40
50
  faraday-http-cache (~> 2.0)
41
- git (~> 1.13)
42
- kramdown (~> 2.3)
51
+ git (>= 1.13, < 3.0)
52
+ kramdown (>= 2.5.1, < 3.0)
43
53
  kramdown-parser-gfm (~> 1.0)
44
- no_proxy_fix
45
54
  octokit (>= 4.0)
46
- terminal-table (>= 1, < 4)
47
- danger-changelog (0.6.1)
55
+ pstore (~> 0.1)
56
+ terminal-table (>= 1, < 5)
57
+ danger-changelog (0.8.0)
48
58
  danger-plugin-api (~> 1.0)
49
59
  danger-plugin-api (1.0.0)
50
60
  danger (> 2.0)
61
+ danger-pr-comment (0.1.0)
62
+ danger (~> 9)
51
63
  danger-toc (0.2.0)
52
64
  activesupport
53
65
  danger-plugin-api (~> 1.0)
54
66
  kramdown
55
- diff-lcs (1.5.0)
56
- docile (1.4.0)
57
- drb (2.2.0)
58
- ruby2_keywords
59
- faraday (2.8.1)
60
- base64
61
- faraday-net_http (>= 2.0, < 3.1)
62
- ruby2_keywords (>= 0.0.4)
63
- faraday-http-cache (2.5.0)
67
+ diff-lcs (1.6.2)
68
+ docile (1.4.1)
69
+ drb (2.2.3)
70
+ faraday (2.14.0)
71
+ faraday-net_http (>= 2.0, < 3.5)
72
+ json
73
+ logger
74
+ faraday-http-cache (2.5.1)
64
75
  faraday (>= 0.8)
65
- faraday-net_http (3.0.2)
66
- git (1.19.0)
76
+ faraday-net_http (3.4.2)
77
+ net-http (~> 0.5)
78
+ git (2.3.3)
79
+ activesupport (>= 5.0)
67
80
  addressable (~> 2.8)
81
+ process_executer (~> 1.1)
68
82
  rchardet (~> 1.8)
69
- i18n (1.14.1)
83
+ i18n (1.14.8)
70
84
  concurrent-ruby (~> 1.0)
71
- json (2.7.1)
72
- kramdown (2.4.0)
73
- rexml
85
+ io-console (0.8.2)
86
+ json (2.18.0)
87
+ kramdown (2.5.1)
88
+ rexml (>= 3.3.9)
74
89
  kramdown-parser-gfm (1.1.0)
75
90
  kramdown (~> 2.0)
76
- language_server-protocol (3.17.0.3)
77
- minitest (5.20.0)
78
- mutex_m (0.2.0)
91
+ language_server-protocol (3.17.0.5)
92
+ lint_roller (1.1.0)
93
+ logger (1.7.0)
94
+ minitest (6.0.1)
95
+ prism (~> 1.5)
96
+ mize (0.6.1)
79
97
  nap (1.1.0)
80
- no_proxy_fix (0.1.2)
81
- octokit (8.0.0)
98
+ net-http (0.9.1)
99
+ uri (>= 0.11.1)
100
+ octokit (10.0.0)
82
101
  faraday (>= 1, < 3)
83
102
  sawyer (~> 0.9)
84
103
  open4 (1.3.4)
85
- parallel (1.24.0)
86
- parser (3.2.2.4)
104
+ parallel (1.27.0)
105
+ parser (3.3.10.0)
87
106
  ast (~> 2.4.1)
88
107
  racc
89
- public_suffix (5.0.4)
90
- racc (1.7.3)
108
+ prism (1.7.0)
109
+ process_executer (1.3.0)
110
+ pstore (0.2.0)
111
+ public_suffix (7.0.0)
112
+ racc (1.8.1)
91
113
  rainbow (3.1.1)
92
- rake (13.1.0)
93
- rchardet (1.8.0)
94
- regexp_parser (2.8.3)
95
- rexml (3.2.6)
96
- rspec (3.12.0)
97
- rspec-core (~> 3.12.0)
98
- rspec-expectations (~> 3.12.0)
99
- rspec-mocks (~> 3.12.0)
100
- rspec-core (3.12.2)
101
- rspec-support (~> 3.12.0)
102
- rspec-expectations (3.12.3)
114
+ rake (13.3.1)
115
+ rchardet (1.10.0)
116
+ readline (0.0.4)
117
+ reline
118
+ regexp_parser (2.11.3)
119
+ reline (0.6.3)
120
+ io-console (~> 0.5)
121
+ rexml (3.4.4)
122
+ rspec (3.13.2)
123
+ rspec-core (~> 3.13.0)
124
+ rspec-expectations (~> 3.13.0)
125
+ rspec-mocks (~> 3.13.0)
126
+ rspec-core (3.13.6)
127
+ rspec-support (~> 3.13.0)
128
+ rspec-expectations (3.13.5)
103
129
  diff-lcs (>= 1.2.0, < 2.0)
104
- rspec-support (~> 3.12.0)
105
- rspec-mocks (3.12.6)
130
+ rspec-support (~> 3.13.0)
131
+ rspec-mocks (3.13.7)
106
132
  diff-lcs (>= 1.2.0, < 2.0)
107
- rspec-support (~> 3.12.0)
108
- rspec-support (3.12.1)
109
- rubocop (1.59.0)
133
+ rspec-support (~> 3.13.0)
134
+ rspec-support (3.13.6)
135
+ rubocop (1.82.1)
110
136
  json (~> 2.3)
111
- language_server-protocol (>= 3.17.0)
137
+ language_server-protocol (~> 3.17.0.2)
138
+ lint_roller (~> 1.1.0)
112
139
  parallel (~> 1.10)
113
- parser (>= 3.2.2.4)
140
+ parser (>= 3.3.0.2)
114
141
  rainbow (>= 2.2.2, < 4.0)
115
- regexp_parser (>= 1.8, < 3.0)
116
- rexml (>= 3.2.5, < 4.0)
117
- rubocop-ast (>= 1.30.0, < 2.0)
142
+ regexp_parser (>= 2.9.3, < 3.0)
143
+ rubocop-ast (>= 1.48.0, < 2.0)
118
144
  ruby-progressbar (~> 1.7)
119
- unicode-display_width (>= 2.4.0, < 3.0)
120
- rubocop-ast (1.30.0)
121
- parser (>= 3.2.1.0)
122
- rubocop-capybara (2.20.0)
123
- rubocop (~> 1.41)
124
- rubocop-factory_bot (2.25.0)
125
- rubocop (~> 1.33)
126
- rubocop-rake (0.6.0)
127
- rubocop (~> 1.0)
128
- rubocop-rspec (2.26.0)
129
- rubocop (~> 1.40)
130
- rubocop-capybara (~> 2.17)
131
- rubocop-factory_bot (~> 2.22)
145
+ unicode-display_width (>= 2.4.0, < 4.0)
146
+ rubocop-ast (1.49.0)
147
+ parser (>= 3.3.7.2)
148
+ prism (~> 1.7)
149
+ rubocop-rake (0.7.1)
150
+ lint_roller (~> 1.1)
151
+ rubocop (>= 1.72.1)
152
+ rubocop-rspec (3.8.0)
153
+ lint_roller (~> 1.1)
154
+ rubocop (~> 1.81)
132
155
  ruby-progressbar (1.13.0)
133
- ruby2_keywords (0.0.5)
134
- sawyer (0.9.2)
156
+ sawyer (0.9.3)
135
157
  addressable (>= 2.3.5)
136
158
  faraday (>= 0.17.3, < 3)
159
+ securerandom (0.4.1)
137
160
  simplecov (0.22.0)
138
161
  docile (~> 1.1)
139
162
  simplecov-html (~> 0.11)
140
163
  simplecov_json_formatter (~> 0.1)
141
- simplecov-html (0.12.3)
164
+ simplecov-html (0.13.2)
142
165
  simplecov_json_formatter (0.1.4)
143
- terminal-table (3.0.2)
144
- unicode-display_width (>= 1.1.1, < 3)
166
+ sync (0.5.0)
167
+ term-ansicolor (1.11.3)
168
+ tins (~> 1)
169
+ terminal-table (4.0.0)
170
+ unicode-display_width (>= 1.1.1, < 4)
171
+ thor (1.4.0)
172
+ tins (1.51.0)
173
+ bigdecimal
174
+ mize (~> 0.6)
175
+ readline
176
+ sync
145
177
  tzinfo (2.0.6)
146
178
  concurrent-ruby (~> 1.0)
147
- unicode-display_width (2.5.0)
179
+ unicode-display_width (3.2.0)
180
+ unicode-emoji (~> 4.1)
181
+ unicode-emoji (4.2.0)
182
+ uri (1.1.1)
148
183
 
149
184
  PLATFORMS
150
- arm64-darwin-21
151
185
  ruby
152
186
 
153
187
  DEPENDENCIES
188
+ coveralls_reborn
154
189
  danger
155
- danger-changelog (= 0.6.1)
156
- danger-toc (= 0.2.0)
190
+ danger-changelog
191
+ danger-pr-comment
192
+ danger-toc
157
193
  rake
158
- rspec (~> 3.0)
159
- rubocop (~> 1.0)
194
+ rspec
195
+ rubocop (= 1.82.1)
160
196
  rubocop-rake
161
197
  rubocop-rspec
162
198
  ruby-enum!
163
- simplecov
164
199
 
165
200
  BUNDLED WITH
166
- 2.5.1
201
+ 2.1.4
data/README.md CHANGED
@@ -2,8 +2,8 @@ Ruby::Enum
2
2
  ==========
3
3
 
4
4
  [![Gem Version](http://img.shields.io/gem/v/ruby-enum.svg)](http://badge.fury.io/rb/ruby-enum)
5
- [![Build Status](https://github.com/dblock/ruby-enum/workflows/test/badge.svg?branch=master)](https://github.com/dblock/ruby-enum/actions)
6
- [![Code Climate](https://codeclimate.com/github/dblock/ruby-enum.svg)](https://codeclimate.com/github/dblock/ruby-enum)
5
+ [![Test](https://github.com/dblock/ruby-enum/actions/workflows/test.yml/badge.svg)](https://github.com/dblock/ruby-enum/actions/workflows/test.yml)
6
+ [![Coverage Status](https://coveralls.io/repos/github/dblock/ruby-enum/badge.svg?branch=master)](https://coveralls.io/github/dblock/ruby-enum?branch=master)
7
7
 
8
8
  Enum-like behavior for Ruby, heavily inspired by [this](http://www.rubyfleebie.com/enumerations-and-ruby), and improved upon [another blog post](http://code.dblock.org/how-to-define-enums-in-ruby).
9
9
 
@@ -27,6 +27,7 @@ Enum-like behavior for Ruby, heavily inspired by [this](http://www.rubyfleebie.c
27
27
  - [Exhaustive case matcher](#exhaustive-case-matcher)
28
28
  - [I18n support](#i18n-support)
29
29
  - [Benchmarks](#benchmarks)
30
+ - [Performance](#performance)
30
31
  - [Contributing](#contributing)
31
32
  - [Copyright and License](#copyright-and-license)
32
33
  - [Related Projects](#related-projects)
@@ -232,7 +233,7 @@ The `DuplicateValueError` exception is raised to be consistent with the unique k
232
233
 
233
234
  ### Inheritance
234
235
 
235
- When inheriting from a `Ruby::Enum` class, all defined enums in the parent class will be accessible in sub classes as well. Sub classes can also provide extra enums, as usual.
236
+ When inheriting from a `Ruby::Enum` class, all defined enums in the parent class will be accessible in subclasses as well. Subclasses can also provide extra enums, as usual.
236
237
 
237
238
  ``` ruby
238
239
  class OrderState
@@ -262,6 +263,26 @@ OrderState.values # ['CREATED', 'PAID']
262
263
  ShippedOrderState.values # ['CREATED', 'PAID', 'PREPARED', SHIPPED']
263
264
  ```
264
265
 
266
+ All other enumerating and hashing methods (`keys`, `key?`, `value?`, `key`, `value`, `to_h`, `parse` and `each`) also consider enums defined anywhere in the class hierarchy.
267
+
268
+ ``` ruby
269
+ ShippedOrderState.keys # [:CREATED, :PAID, :PREPARED, :SHIPPED]
270
+ ShippedOrderState.key?(:CREATED) # true
271
+ ShippedOrderState.value(:CREATED) # 'CREATED'
272
+ ```
273
+
274
+ A subclass may redefine a key or value already used by a parent class without raising `DuplicateKeyError` or `DuplicateValueError`; its own definition takes precedence.
275
+
276
+ ``` ruby
277
+ class ShippedOrderState < OrderState
278
+ define :CREATED, 'RECREATED' # does not raise, overrides the parent class' definition
279
+ end
280
+
281
+ ShippedOrderState::CREATED # 'RECREATED'
282
+ ShippedOrderState.value(:CREATED) # 'RECREATED'
283
+ OrderState.value(:CREATED) # 'CREATED', unaffected
284
+ ```
285
+
265
286
  ### Exhaustive case matcher
266
287
 
267
288
  If you want to make sure that you cover all cases in a case stament, you can use the exhaustive case matcher: `Ruby::Enum::Case`. It will raise an error if a case/enum value is not handled, or if a value is specified that's not part of the enum. This is inspired by the [Rust Pattern Syntax](https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html). If multiple cases match, all matches are being executed. The return value is the value from the matched case, or an array of return values if multiple cases matched.
@@ -315,9 +336,21 @@ gem "i18n"
315
336
  Benchmark scripts are defined in the [`benchmarks`](benchmarks) folder and can be run with Rake:
316
337
 
317
338
  ```console
318
- rake benchmarks:case
339
+ rake benchmark:basic
340
+ rake benchmark:case
341
+ rake benchmark:inheritance
319
342
  ```
320
343
 
344
+ ### Performance
345
+
346
+ Constant access (e.g. `Colors::RED`) has no measurable overhead versus a plain Ruby constant, since it's just a constant lookup either way. Basic operations backed by a hash lookup - `value`, `key`, `key?`, `value?`, `keys`, `values` - carry some overhead (roughly 3-5x) compared to using a plain `Hash` directly, due to the extra method dispatch and object wrapping `Ruby::Enum` does internally. Run `rake benchmark:basic` to measure this on your own machine.
347
+
348
+ This overhead is constant regardless of how deep a subclass hierarchy is - `keys`, `key?`, `value?`, `key`, `value`, `to_h`, `parse` and `each` merge and memoize enums inherited from superclasses, so a subclass' lookups are effectively as fast as the base class' (see `rake benchmark:inheritance`).
349
+
350
+ The one notable exception is `Ruby::Enum::Case`, whose exhaustive `case`-like matcher is significantly slower (on the order of 50-100x, see `rake benchmark:case`) than a native Ruby `case`/`when` statement, since it builds and evaluates lambdas on every call rather than being optimized by the Ruby VM. Prefer a native `case` statement in hot code paths and reserve `Ruby::Enum::Case` for cases where its exhaustiveness check is worth the overhead.
351
+
352
+ For most applications this overhead is negligible in absolute terms (low single-digit microseconds per call), but it's worth being aware of in very hot code paths.
353
+
321
354
  ## Contributing
322
355
 
323
356
  You're encouraged to contribute to ruby-enum. See [CONTRIBUTING](CONTRIBUTING.md) for details.
data/RELEASING.md CHANGED
@@ -11,7 +11,7 @@ bundle install
11
11
  rake
12
12
  ```
13
13
 
14
- Check that the last build succeeded in [Travis CI](https://travis-ci.org/dblock/ruby-enum) for all supported platforms.
14
+ Check that the last build succeeded in [GitHub Actions](https://github.com/dblock/ruby-enum/actions) for all supported platforms.
15
15
 
16
16
  Add a date to this release in [CHANGELOG.md](CHANGELOG.md).
17
17
 
@@ -28,15 +28,24 @@ git add README.md CHANGELOG.md lib/ruby-enum/version.rb
28
28
  git commit -m "Preparing for release, 0.2.2."
29
29
  ```
30
30
 
31
- Release.
31
+ Release. If you have MFA enabled on RubyGems (you should), `rake release` will build the gem, tag it, and push commits/tags to GitHub, but fail at the RubyGems push. Push the gem manually with your OTP.
32
32
 
33
33
  ```
34
34
  $ rake release
35
35
 
36
- ruby-enum 0.2.2 built to pkg/ruby-enum-0.2.2.gem.
37
- Tagged v0.2.2.
36
+ ruby-enum 1.1.0 built to pkg/ruby-enum-1.1.0.gem.
37
+ Tagged v1.1.0.
38
38
  Pushed git commits and tags.
39
- Pushed ruby-enum 0.2.2 to rubygems.org.
39
+ ...
40
+ You have enabled multi-factor authentication. Please enter OTP code.
41
+ ```
42
+
43
+ Then push to RubyGems manually:
44
+
45
+ ```
46
+ $ gem push pkg/ruby-enum-1.1.0.gem --otp <OTP>
47
+ Pushing gem to https://rubygems.org...
48
+ Successfully registered gem: ruby-enum (1.1.0)
40
49
  ```
41
50
 
42
51
  ### Prepare for the Next Version
data/Rakefile CHANGED
@@ -18,8 +18,18 @@ RuboCop::RakeTask.new(:rubocop)
18
18
  task default: %i[rubocop spec]
19
19
 
20
20
  namespace :benchmark do
21
+ desc 'Run benchmark for basic Ruby::Enum operations vs. plain Ruby equivalents'
22
+ task :basic do
23
+ require_relative 'benchmarks/basic'
24
+ end
25
+
21
26
  desc 'Run benchmark for the Ruby::Enum::Case'
22
27
  task :case do
23
28
  require_relative 'benchmarks/case'
24
29
  end
30
+
31
+ desc 'Run benchmark for enum lookups on classes with inheritance'
32
+ task :inheritance do
33
+ require_relative 'benchmarks/inheritance'
34
+ end
25
35
  end
data/UPGRADING.md CHANGED
@@ -1,5 +1,44 @@
1
1
  # Upgrading Ruby::Enum
2
2
 
3
+ ## Upgrading to >= 1.2.0
4
+
5
+ ### Inheritance & `keys`, `key?`, `value?`, `key`, `value`, `to_h`, `parse` and `each`
6
+
7
+ This only applies to classes that inherit from another which is a `Ruby::Enum`.
8
+
9
+ Prior to version `1.2.0`, only `values` enumerated enums defined in the entire class hierarchy; `keys`, `key?`, `value?`, `key`, `value`, `to_h`, `parse` and `each` only considered enums defined directly on the class, silently ignoring anything defined in a superclass.
10
+
11
+ As of version `1.2.0`, these methods behave consistently with `values` and also enumerate/consider enums defined anywhere in the class hierarchy, ancestors first. A subclass may still redefine a key or value already used by a superclass; its own definition takes precedence.
12
+
13
+ ``` ruby
14
+ class PrimaryColors
15
+ include Ruby::Enum
16
+
17
+ define :RED, 'RED'
18
+ define :GREEN, 'GREEN'
19
+ end
20
+
21
+ class RainbowColors < PrimaryColors
22
+ define :ORANGE, 'ORANGE'
23
+ end
24
+ ```
25
+
26
+ `gem 'ruby-enum', '< 1.2.0'`
27
+
28
+ ``` ruby
29
+ RainbowColors.keys # => [:ORANGE]
30
+ RainbowColors.key?(:RED) # => false
31
+ ```
32
+
33
+ `gem 'ruby-enum', '>= 1.2.0'`
34
+
35
+ ``` ruby
36
+ RainbowColors.keys # => [:RED, :GREEN, :ORANGE]
37
+ RainbowColors.key?(:RED) # => true
38
+ ```
39
+
40
+ See [#49](https://github.com/dblock/ruby-enum/issues/49) for more information.
41
+
3
42
  ## Upgrading to >= 0.9.0
4
43
 
5
44
  ### Inheritance & `Ruby::Enum.values`
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
4
+
5
+ require 'benchmark'
6
+ require 'ruby-enum'
7
+
8
+ ##
9
+ # Ruby::Enum equivalent of a plain Hash/constant lookup.
10
+ class Colors
11
+ include Ruby::Enum
12
+
13
+ define :RED, 'red'
14
+ define :GREEN, 'green'
15
+ define :BLUE, 'blue'
16
+ end
17
+
18
+ # Plain Ruby equivalents, doing the same lookups without Ruby::Enum.
19
+ PLAIN_HASH = { RED: 'red', GREEN: 'green', BLUE: 'blue' }.freeze
20
+
21
+ module PlainConstants
22
+ RED = 'red'
23
+ GREEN = 'green'
24
+ BLUE = 'blue'
25
+ end
26
+
27
+ n = 1_000_000
28
+
29
+ def benchmark(label, iterations, &block)
30
+ time = Benchmark.realtime { iterations.times(&block) }
31
+ puts "#{label}: #{time.round(4)}"
32
+ time
33
+ end
34
+
35
+ puts "Running #{n} iterations of each scenario below\n\n"
36
+
37
+ puts '--- Constant access ---'
38
+ enum_const_time = benchmark('Ruby::Enum constant (Colors::RED)', n) { Colors::RED }
39
+ plain_const_time = benchmark('plain Ruby constant (PlainConstants::RED)', n) { PlainConstants::RED }
40
+
41
+ puts "\n--- Key to value lookup ---"
42
+ enum_value_time = benchmark('Ruby::Enum (Colors.value(:RED))', n) { Colors.value(:RED) }
43
+ hash_value_time = benchmark('plain Hash (PLAIN_HASH[:RED])', n) { PLAIN_HASH[:RED] }
44
+
45
+ puts "\n--- Value to key lookup ---"
46
+ benchmark('Ruby::Enum (Colors.key(\'red\'))', n) { Colors.key('red') }
47
+ benchmark('plain Hash (PLAIN_HASH.key(\'red\'))', n) { PLAIN_HASH.key('red') }
48
+
49
+ puts "\n--- Existence checks ---"
50
+ benchmark('Ruby::Enum (Colors.key?(:RED))', n) { Colors.key?(:RED) }
51
+ benchmark('plain Hash (PLAIN_HASH.key?(:RED))', n) { PLAIN_HASH.key?(:RED) }
52
+
53
+ puts "\n--- Enumerating all keys/values ---"
54
+ benchmark('Ruby::Enum (Colors.keys)', n) { Colors.keys }
55
+ benchmark('plain Hash (PLAIN_HASH.keys)', n) { PLAIN_HASH.keys }
56
+ benchmark('Ruby::Enum (Colors.values)', n) { Colors.values }
57
+ benchmark('plain Hash (PLAIN_HASH.values)', n) { PLAIN_HASH.values }
58
+
59
+ puts "\n--- Comparison ---"
60
+ puts "Ruby::Enum constant access is #{(enum_const_time / plain_const_time).round(2)}x plain Ruby constant access"
61
+ puts "Ruby::Enum .value is #{(enum_value_time / hash_value_time).round(2)}x plain Hash#[]"
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
4
+
5
+ require 'benchmark'
6
+ require 'ruby-enum'
7
+
8
+ ##
9
+ # Base enum, no inheritance.
10
+ class Colors
11
+ include Ruby::Enum
12
+
13
+ define :RED, 'red'
14
+ define :GREEN, 'green'
15
+ end
16
+
17
+ ##
18
+ # Subclass, adds its own enum on top of an inherited one (1 level).
19
+ class SubColors < Colors
20
+ define :BLUE, 'blue'
21
+ end
22
+
23
+ ##
24
+ # Sub-subclass, adds its own enum on top of 2 inherited levels.
25
+ class SubSubColors < SubColors
26
+ define :YELLOW, 'yellow'
27
+ end
28
+
29
+ n = 1_000_000
30
+
31
+ def benchmark(label, iterations, &block)
32
+ time = Benchmark.realtime { iterations.times(&block) }
33
+ puts "#{label}: #{time.round(4)}"
34
+ time
35
+ end
36
+
37
+ puts "Running #{n} iterations of each scenario below\n\n"
38
+
39
+ puts '--- .value lookups, by depth of inheritance ---'
40
+ base_value = benchmark('base class (no inheritance)', n) { Colors.value(:RED) }
41
+ sub_value = benchmark('subclass (1 level)', n) { SubColors.value(:RED) }
42
+ sub_sub_value = benchmark('sub-subclass (2 levels)', n) { SubSubColors.value(:RED) }
43
+
44
+ puts "\n--- Other methods on a subclass (1 level) ---"
45
+ benchmark('.keys', n) { SubColors.keys }
46
+ benchmark('.key?', n) { SubColors.key?(:RED) }
47
+ benchmark('.to_h', n) { SubColors.to_h }
48
+ count = 0
49
+ benchmark('.each', n) { SubColors.each { |_k, _v| count += 1 } }
50
+
51
+ puts "\n--- Comparison ---"
52
+ puts "subclass value is #{(sub_value / base_value).round(2)}x base class value"
53
+ puts "sub-subclass value is #{(sub_sub_value / base_value).round(2)}x base class value"