nummy 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d9edb3b21dc12078ceec611949ebf6deb80ed4a69309aaba8a2b01c233025e66
4
+ data.tar.gz: e066bb5910d68efdca902e242a1cd9269aecc48b9a77bbf9c246516dc995eb8d
5
+ SHA512:
6
+ metadata.gz: 401f6427a1c27596f411cf11c6686a9164aabe06980c6db046887848424f01245921d45f15d01b4938d26a5ccb274e08003b51810073e1ec3cd7e71c0e2f284d
7
+ data.tar.gz: 5e3c46a452c2967bc52c1ddf0df5993545af18801a3bbbaead6663fa91e0aa9d61fa87919b7c4c334791a0c7162867d023e4e346200c9d407029fb4e1497da53
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2024-01-04
4
+
5
+ - Initial release
data/README.md ADDED
@@ -0,0 +1,383 @@
1
+ # Nummy
2
+
3
+ > Tasty enumeration utilities for Ruby
4
+
5
+ Nummy provides utilities that that build on Ruby's Enumerable module to
6
+ provide functionality like enumerated types ("enums"), enumerating over
7
+ constants in a module, and iterating over the members of data classes.
8
+
9
+ > [!NOTE]
10
+ > This module does NOT add additional methods to the Enumerable module, or change its behavior in any way.
11
+
12
+ ## Installation
13
+
14
+ Install the gem and add to the application's Gemfile by executing:
15
+
16
+ ```console
17
+ bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
18
+ ```
19
+
20
+ If bundler is not being used to manage dependencies, install the gem by executing:
21
+
22
+ ```console
23
+ gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ To load `nummy`, use:
29
+
30
+ ```ruby
31
+ require "nummy"
32
+ ```
33
+
34
+ This will lazily require (using `autoload`) the individual utilities as needed.
35
+
36
+ You can also explicitly load individual modules:
37
+
38
+ ```ruby
39
+ require "nummy/enum"
40
+ require "nummy/member_enumerable"
41
+ # ...
42
+ ```
43
+
44
+ ### `Nummy::Enum`
45
+
46
+ The main feature of Nummy is the `Nummy::Enum` class, which is an opinionated class that can be used to define [enumerated types](https://en.wikipedia.org/wiki/Enumerated_type) ("enums") that use Ruby constants for the key-value pairs.
47
+
48
+ The recommended way to use `Nummy::Enum` is to define constants explicitly:
49
+
50
+ #### Creating (Recommended)
51
+
52
+ ```ruby
53
+ require "nummy"
54
+
55
+ class CardinalDirection < Nummy::Enum
56
+ NORTH = 0
57
+ EAST = 90
58
+ SOUTH = 180
59
+ WEST = 270
60
+ end
61
+ ```
62
+
63
+ > [!TIP]
64
+ > Statically defining the constants like this allows the enums to work really nicely out of the box with language servers like [Ruby LSP](https://github.com/Shopify/ruby-lsp) because the fields are just statically defined constants. The sugary ways of defining enums (see below) use `const_set` behind the scenes, which does not work as well with language servers and tooling.
65
+
66
+ If you don't particularly care about the values, you can use the `auto` helper to define them automatically:
67
+
68
+ ```ruby
69
+ class Status < Nummy::Enum
70
+ DRAFT = auto
71
+ PUBLISHED = auto
72
+ ARCHIVED = auto
73
+ end
74
+ ```
75
+
76
+ > [!TIP]
77
+ > `auto` comes from the `Nummy::AutoSequenceable` module, and has some extra features not shown in this example. For more information, see the section below on `Nummy::AutoSequenceable`.
78
+
79
+ #### Creating (Sugar)
80
+
81
+ There are also two sugary ways to define enums:
82
+
83
+ ```ruby
84
+ require "nummy"
85
+
86
+ Nummy.enum(NORTH: 0, EAST: 90, SOUTH: 180, WEST: 270)
87
+
88
+ # which is an alias for:
89
+ Nummy::Enum.define(NORTH: 0, EAST: 90, SOUTH: 180, WEST: 270)
90
+ ```
91
+
92
+ Or if you want the values to be automatically generated:
93
+
94
+ ```ruby
95
+ require "nummy"
96
+ Status = Nummy.enum(:DRAFT, :PUBLISHED, :ARCHIVED)
97
+
98
+ Status.pairs
99
+ # => [[:DRAFT, 0], [:PUBLISHED, 1], [:ARCHIVED, 2]]
100
+ ```
101
+
102
+ You can customize the generated enum by providing a block:
103
+
104
+ ```ruby
105
+ require "nummy"
106
+
107
+ Status = Nummy.enum(:DRAFT, :PUBLISHED, :ARCHIVED) do |enum|
108
+ def self.custom_method
109
+ puts "Hello from #{self}!"
110
+ end
111
+ end
112
+
113
+ Status.custom_method
114
+ # => Hello from Status!
115
+ ```
116
+
117
+ #### Working with enums
118
+
119
+ The `Nummy::Enum` class provides a number of methods for working with enums. For example, using the `CardinalDirection` example from above:
120
+
121
+ ```ruby
122
+ CardinalDirection.keys
123
+ # => [:NORTH, :EAST, :SOUTH, :WEST]
124
+
125
+ CardinalDirection.values
126
+ # => [0, 90, 180, 270]
127
+
128
+ CardinalDirection.pairs
129
+ # => [[:NORTH, 0], [:EAST, 90], [:SOUTH, 180], [:WEST, 270]]
130
+
131
+ CardinalDirection.include?(90)
132
+ # => true
133
+ ```
134
+
135
+ They can even be used in `case` expressions to see if the case value is `==` to any of the values in the enum:
136
+
137
+ ```ruby
138
+ case some_angle
139
+ when CardinalDirection
140
+ puts "The angle is a cardinal direction!"
141
+ else
142
+ puts "Not a cardinal direction!"
143
+ end
144
+ ```
145
+
146
+ > [!TIP]
147
+ > `Nummy::Enum` extends `Enumerable` and iterates over the _values_ of the enum, so you have access to things like `.include?(value)`, `.any?`, and `.find`.
148
+
149
+ ### `Nummy::MemberEnumerable`
150
+
151
+ `Nummy::MemberEnumerable` is a module that includes `Enumerable` and makes it possible to iterate over any class or module that responds to `members`.
152
+
153
+ The motivation for this module is being able to iterate over `Data` classes that represent a finite collection of similar values.
154
+
155
+ ```ruby
156
+ require "nummy"
157
+
158
+ SomeCollection = Data.define(:foo, :bar, :baz) do
159
+ include Nummy::MemberEnumerable
160
+ end
161
+
162
+ collection = SomeCollection[123, 456, 789]
163
+
164
+ collection.values
165
+ # => [123, 456, 789]
166
+ ```
167
+
168
+ > [!NOTE]
169
+ > The `Nummy::MemberEnumerable` module provides fewer enumeration features than you might expect, because it's modeled after `Struct` rather than `Hash`.
170
+
171
+ ### `Nummy::ConstEnumerable`
172
+
173
+ `Nummy::ConstEnumerable` is a module that only provides methods to iterate over the names, values, and pairs of a module's own constants. It is meant to provide low-level functionality for other classes, such as `Nummy::Enum`.
174
+
175
+ ```ruby
176
+ require "nummy"
177
+
178
+ module SomeModule
179
+ extend Nummy::ConstEnumerable
180
+
181
+ FOO = 123
182
+ BAR = 456
183
+ BAZ = 789
184
+ end
185
+
186
+ SomeModule.each_const_name { |name| puts name }
187
+ # => :BAR
188
+ # => :BAZ
189
+ # => :FOO
190
+
191
+ SomeModule.each_const_pair.to_a
192
+ # => [[:BAR, 456], [:BAZ, 789], [:FOO, 123]]
193
+ ```
194
+
195
+ > [!WARNING]
196
+ > The enumeration order for `Nummy::ConstEnumerable` is _not_ guaranteed because it uses `Module#constants` behind the scenes.
197
+ > If you require stable ordering, see `Nummy::OrderedConstEnumerable`.
198
+
199
+ ### `Nummy::OrderedConstEnumerable`
200
+
201
+ `Nummy::OrderedConstEnumerable` provides the same API as `Nummy::ConstEnumerable`, but guarantees three things:
202
+
203
+ - any constants defined _before_ the module is extended will be sorted _alphabetically_ before any other constants
204
+ - any constants defined _after_ the module is extended will be sorted in insertion order
205
+ - the order of constants is stable across calls
206
+
207
+ For example:
208
+
209
+ ```ruby
210
+ require "nummy"
211
+
212
+ class CustomEnum
213
+ BEFORE_C = :c
214
+ BEFORE_B = :b
215
+ BEFORE_A = :a
216
+
217
+ extend Nummy::OrderedConstEnumerable
218
+
219
+ AFTER_Z = :z
220
+ AFTER_Y = :y
221
+ AFTER_X = :x
222
+ end
223
+
224
+ CustomEnum.each_const_name.to_a
225
+ # => [:BEFORE_A, :BEFORE_B, :BEFORE_C, :AFTER_Z, :AFTER_Y, :AFTER_X]
226
+ ```
227
+
228
+ ### `Nummy::AutoSequenceable`
229
+
230
+ `Nummy::AutoSequenceable` is a module that provides a single method, `auto`, which returns a unique value for each call. It is meant to provide low-level functionality for other classes, such as `Nummy::Enum`.
231
+
232
+ ```ruby
233
+ require "nummy"
234
+
235
+ class Weekday
236
+ extend Nummy::AutoSequenceable
237
+
238
+ SUNDAY = auto
239
+ MONDAY = auto
240
+ TUESDAY = auto
241
+ WEDNESDAY = auto
242
+ THURSDAY = auto
243
+ FRIDAY = auto
244
+ SATURDAY = auto
245
+ end
246
+
247
+ Weekday::SUNDAY
248
+ # => 0
249
+
250
+ Weekday::SATURDAY
251
+ # => 6
252
+ ```
253
+
254
+ Like with [`iota` in Go][iota-golang], or [`Enum.auto` in Python][enum-auto-python], you can also customize the sequence behavior:
255
+
256
+ [iota-golang]: https://go.dev/wiki/Iota
257
+ [enum-auto-python]: https://docs.python.org/3/library/enum.html#enum.auto
258
+
259
+ ```ruby
260
+ require "nummy"
261
+ require "bigdecimal"
262
+
263
+ module MetricPrefix
264
+ extend Nummy::AutoSequenceable
265
+
266
+ DECI = auto(1) { |n| BigDecimal(10) ** -n }
267
+ CENTI = auto
268
+ MILLI = auto
269
+ MICRO = auto(6)
270
+ NANO = auto(9)
271
+
272
+ DECA = auto(1) { |n| BigDecimal(10) ** n }
273
+ HECA = auto
274
+ KILO = auto
275
+ MEGA = auto(6)
276
+ GIGA = auto(9)
277
+ end
278
+
279
+ MetricPrefix::DECI # => 0.1e0
280
+ MetricPrefix::CENTI # => 0.1e-1
281
+ MetricPrefix::MILLI # => 0.1e-2
282
+ MetricPrefix::MICRO # => 0.1e-5
283
+ MetricPrefix::NANO # => 0.1e-8
284
+
285
+ MetricPrefix::DECA # => 0.1e2
286
+ MetricPrefix::HECA # => 0.1e3
287
+ MetricPrefix::KILO # => 0.1e4
288
+ MetricPrefix::MEGA # => 0.1e7
289
+ MetricPrefix::GIGA # => 0.1e10
290
+ ```
291
+
292
+ See the implementation, tests, and documentation for more details.
293
+
294
+ ## Development
295
+
296
+ ### Setup
297
+
298
+ After checking out the repo, run `bin/setup` to install dependencies.
299
+
300
+ To install this gem onto your local machine, run `bundle exec rake install`.
301
+
302
+ ### Console
303
+
304
+ You can run `bundle exec rake console` for an interactive prompt that will allow you to experiment with the gem.
305
+
306
+ The console has a pre-configured `Nummy::Enum` that you can use for testing:
307
+
308
+ ```irb
309
+ irb(main):001> CardinalDirection
310
+ => #<CardinalDirection NORTH=0 EAST=90 SOUTH=180 WEST=270>
311
+ irb(main):002> CardinalDirection.values
312
+ => [0, 90, 180, 270]
313
+ ```
314
+
315
+ ### Documentation
316
+
317
+ You can start a documentation server by running `rake docs`. This will start a server at http://localhost:8808 that will pick up changes to the documentation whenever you refresh the page.
318
+
319
+ > [!NOTE]
320
+ > If you make major changes to the gem, sometimes the server can get in a weird state and display incorrect documentation, especially on the sidebar. If this happens, you can try removing the yard cache with `rm -rf .yardoc`, then restart the server and refresh the docs page.
321
+
322
+ ### Testing
323
+
324
+ To run tests, you can use:
325
+
326
+ ```console
327
+ bundle exec rake test
328
+ ```
329
+
330
+ #### Running subsets of tests
331
+
332
+ To run all of the tests in a specific file, you can use the `TEST` argument:
333
+
334
+ ```console
335
+ bundle exec rake test TEST="test/nummy/enum_test.rb"
336
+ ```
337
+
338
+ To run a specific test by name, you can use the `N` argument and pass it a name or regex pattern:
339
+
340
+ ```console
341
+ bundle exec rake test N="/version/"
342
+ ```
343
+
344
+ > [!TIP]
345
+ > The `N` argument comes from `Minitest::TestTask`. See [the Minitest README](https://github.com/minitest/minitest#label-Rake+Tasks) or [the Minitest documentation](https://docs.seattlerb.org/minitest/Minitest/TestTask.html) for more information.
346
+
347
+ > [!TIP]
348
+ > You can also combine multiple options together:
349
+ >
350
+ > ```console
351
+ > bundle exec rake test N="/positional args/" TEST=test/nummy/enum_test.rb
352
+ > ```
353
+
354
+ #### Coverage
355
+
356
+ > [!NOTE]
357
+ > Coverage is _not_ collected by default, because our configuration reports incorrect metrics when not run against the entire test suite. We could configure coverage to only report against files that were actually required, but because we lazily load some files, it would be easy to miss coverage for an entire file.
358
+
359
+ To collect coverage, you can set `COVERAGE` to `true` or `1`:
360
+
361
+ ```console
362
+ bundle exec rake test COVERAGE=true
363
+ ```
364
+
365
+ ```console
366
+ bundle exec rake test COVERAGE=1
367
+ ```
368
+
369
+ ### Releasing
370
+
371
+ To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
372
+
373
+ ## Contributing
374
+
375
+ Bug reports and pull requests are welcome on GitHub at https://github.com/bdchauvette/nummy. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/bdchauvette/nummy/blob/main/CODE_OF_CONDUCT.md).
376
+
377
+ ## License
378
+
379
+ The gem is available as open source under the terms of the [MIT No Attribution License](https://opensource.org/licenses/MIT-0).
380
+
381
+ ## Code of Conduct
382
+
383
+ Everyone interacting in the Nummy project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/bdchauvette/nummy/blob/main/CODE_OF_CONDUCT.md).
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nummy
4
+ # Provides a method to help automatically generate sequential values.
5
+ #
6
+ # @see Enum
7
+ module AutoSequenceable
8
+ # Automatically generates values in a sequence.
9
+ #
10
+ # Values are calculated using a proc that is called with a succeedable
11
+ # value, that is a value that responds to +#succ+, such as +Integer+ or
12
+ # +String+, in order to produce the next value in the sequence.
13
+ #
14
+ # By default, the proc will use an integer counter that starts at zero and
15
+ # increases by one each time +auto+ is called.
16
+ #
17
+ # This is similar to +iota+ in Go, or +enum.auto+ in Python.
18
+ #
19
+ # @see https://go.dev/wiki/Iota +iota+ in Go
20
+ # @see https://docs.python.org/3/library/enum.html#enum.auto +enum.auto+ in Python
21
+ #
22
+ # @note
23
+ # This is not thread-safe or safe to use when reopening classes. This is
24
+ # only intended to be used in simple cases, such as defining a series of
25
+ # sequential constants.
26
+ #
27
+ # @overload auto
28
+ # Calls the stored block with the current count and returns the block result.
29
+ #
30
+ # By default, returns the number of times that +auto+ has been called.
31
+ # However, if a block is ever given (see next override), then the value
32
+ # will be determined by that block.
33
+ #
34
+ # @return [Object]
35
+ #
36
+ # @overload auto(&)
37
+ # Resets the counter to zero, captures and stores the given block, then
38
+ # calls the block and returns the block result.
39
+ #
40
+ # @yieldparam value [#succ] Number of times this block has been called.
41
+ # @return [Object] the result of the block
42
+ #
43
+ # @overload auto(initial)
44
+ # Sets the counter value to the given value, calls the stored block with
45
+ # the new value, and returns the block result.
46
+ #
47
+ # @param [#succ] initial The counter value to use.
48
+ # @return [Object]
49
+ #
50
+ # @overload auto(initial, &)
51
+ # Resets the counter, calls the given block and returns the block result.
52
+ #
53
+ # @param initial [#succ] The initial counter value to use.
54
+ # @yieldparam value [#succ] Number of times this block has been called.
55
+ # @return [Object] the result of the block
56
+ #
57
+ # @example Generating a simple sequence of numbers.
58
+ # class Weekday
59
+ # extend Nummy::AutoSequenceable
60
+ #
61
+ # SUNDAY = auto
62
+ # MONDAY = auto
63
+ # TUESDAY = auto
64
+ # WEDNESDAY = auto
65
+ # THURSDAY = auto
66
+ # FRIDAY = auto
67
+ # SATURDAY = auto
68
+ # end
69
+ #
70
+ # Weekday::SUNDAY # => 0
71
+ # Weekday::SATURDAY # => 6
72
+ #
73
+ # @example Using a custom block to generate a calculated sequence.
74
+ # module BinaryPrefix
75
+ # extend Nummy::AutoSequenceable
76
+ #
77
+ # _ = auto { |n| 1 << 10*n } # discard first value
78
+ # Ki = auto
79
+ # Mi = auto
80
+ # Gi = auto
81
+ # Ti = auto
82
+ # Pi = auto
83
+ # end
84
+ #
85
+ # BinaryPrefix::Ki == 2**10 # => true
86
+ # BinaryPrefix::Mi == 2**20 # => true
87
+ # BinaryPrefix::Gi == 2**30 # => true
88
+ # BinaryPrefix::Ti == 2**40 # => true
89
+ # BinaryPrefix::Pi == 2**50 # => true
90
+ #
91
+ # @example Using custom blocks and specific counters to generate multiple calculated sequences.
92
+ # require "bigdecimal"
93
+ #
94
+ # module MetricPrefix
95
+ # extend Nummy::AutoSequenceable
96
+ #
97
+ # DECI = auto(1) { |n| BigDecimal(10) ** -n }
98
+ # CENTI = auto
99
+ # MILLI = auto
100
+ # MICRO = auto(6)
101
+ # NANO = auto(9)
102
+ #
103
+ # DECA = auto(1) { |n| BigDecimal(10) ** n }
104
+ # HECA = auto
105
+ # KILO = auto
106
+ # MEGA = auto(6)
107
+ # GIGA = auto(9)
108
+ # end
109
+ #
110
+ # MetricPrefix::DECI # => 0.1e0
111
+ # MetricPrefix::CENTI # => 0.1e-1
112
+ # MetricPrefix::MILLI # => 0.1e-2
113
+ # MetricPrefix::MICRO # => 0.1e-5
114
+ # MetricPrefix::NANO # => 0.1e-8
115
+ #
116
+ # MetricPrefix::DECA # => 0.1e2
117
+ # MetricPrefix::HECA # => 0.1e3
118
+ # MetricPrefix::KILO # => 0.1e4
119
+ # MetricPrefix::MEGA # => 0.1e7
120
+ # MetricPrefix::GIGA # => 0.1e10
121
+ def auto(initial = nil, &block)
122
+ if block_given?
123
+ self.nummy_auto_proc = block
124
+ self.nummy_auto_value = initial || 0
125
+ elsif initial
126
+ self.nummy_auto_value = initial
127
+ end
128
+
129
+ value = nummy_auto_proc.call(nummy_auto_value)
130
+ nummy_auto_succ!
131
+
132
+ value
133
+ end
134
+
135
+ private
136
+
137
+ attr_writer :nummy_auto_proc
138
+
139
+ def nummy_auto_proc
140
+ @nummy_auto_proc ||= ->(value) { value }
141
+ end
142
+
143
+ attr_writer :nummy_auto_value
144
+
145
+ def nummy_auto_value
146
+ @nummy_auto_value ||= 0
147
+ end
148
+
149
+ def nummy_auto_succ!
150
+ curr_value = nummy_auto_value
151
+
152
+ self.nummy_auto_value =
153
+ if curr_value.respond_to?(:next)
154
+ curr_value.next
155
+ elsif curr_value.respond_to?(:succ)
156
+ curr_value.succ
157
+ else
158
+ raise TypeError,
159
+ "cannot increment #{curr_value.inspect}: does not respond to #next or #succ"
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nummy
4
+ # A module that can be extended in order to enumerate over constants
5
+ # in a +Hash+-like manner. This can be used to build enum-like classes and
6
+ # modules that work nicely with static analysis.
7
+ #
8
+ # The enumeration order for methods defined by this module is *not* guaranteed to be stable
9
+ # across calls and methods. This is because those constants are
10
+ # looked up using +Module#constants+, and that method does not have any
11
+ # ordering guarantees.
12
+ #
13
+ # @note
14
+ # If stable ordering is important, you should use {OrderedConstEnumerable}.
15
+ #
16
+ # @see OrderedConstEnumerable
17
+ module ConstEnumerable
18
+ include Enumerable
19
+
20
+ # Iterates through the names of the constants in +self+.
21
+ #
22
+ # @overload each_const_name(&)
23
+ # Calls the given block with the name of each constant in +self+.
24
+ #
25
+ # @yieldparam [Symbol] name
26
+ # @return [self]
27
+ #
28
+ # @overload each_const_name
29
+ # Returns an +Enumerator+ that calls the given block with the name of each
30
+ # constant in +self+.
31
+ #
32
+ # @return [Enumerator<Symbol>]
33
+ def each_const_name(&)
34
+ return enum_for(__method__) unless block_given?
35
+
36
+ nummy_constants.each(&)
37
+ self
38
+ end
39
+
40
+ # Iterates through the values of the constants in +self+.
41
+ #
42
+ # @overload each_const_value(&)
43
+ # Calls the given block with the value of each constant in +self+.
44
+ #
45
+ # The order of the values is guaranteed to be the same as the insertion
46
+ # order of the constants.
47
+ #
48
+ # @yieldparam [Object] value
49
+ # @return [self]
50
+ #
51
+ # @overload each_const_value
52
+ # Returns an +Enumerator+ that calls the given block with the value of
53
+ # each constant in +self+.
54
+ #
55
+ # @return [Enumerator<Object>]
56
+ def each_const_value
57
+ return enum_for(__method__) unless block_given?
58
+
59
+ each_const_name { |name| yield nummy_const_get(name) }
60
+ self
61
+ end
62
+
63
+ # Iterates through the name-value pairs of the constants in +self+.
64
+ #
65
+ # @note
66
+ # This method is used as the basis for the enumeration methods provided
67
+ # by the +Enumerable+ module.
68
+ #
69
+ # @overload each_const_pair(&)
70
+ # Calls the given block with a two-item array containing the name and
71
+ # value of each constant in +self+.
72
+ #
73
+ # The order of the pairs is guaranteed to be the same as the insertion
74
+ # order of the constants.
75
+ #
76
+ # @yieldparam [Symbol] name
77
+ # @yieldparam [Object] value
78
+ # @return [self]
79
+ #
80
+ # @overload each_const_pair
81
+ # Returns an +Enumerator+ that iterates over with a two-item array
82
+ # containing the name and value of each constant in +self+.
83
+ # @return [Enumerator<Array<Symbol, Object>>]
84
+ def each_const_pair
85
+ return enum_for(__method__) unless block_given?
86
+
87
+ each_const_name { |name| yield name, nummy_const_get(name) }
88
+ self
89
+ end
90
+
91
+ # Use {each_const_pair} as the base for enumeration methods in +Enumerable+.
92
+ alias each each_const_pair
93
+
94
+ private
95
+
96
+ def nummy_const_get(name) = const_get(name, false)
97
+
98
+ def nummy_constants = constants(false)
99
+ end
100
+ end