nummy 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/README.md +383 -0
- data/lib/nummy/auto_sequenceable.rb +163 -0
- data/lib/nummy/const_enumerable.rb +100 -0
- data/lib/nummy/enum.rb +391 -0
- data/lib/nummy/errors.rb +7 -0
- data/lib/nummy/member_enumerable.rb +87 -0
- data/lib/nummy/ordered_const_enumerable.rb +81 -0
- data/lib/nummy/version.rb +6 -0
- data/lib/nummy.rb +22 -0
- metadata +274 -0
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
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
|