typesafe_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/.gitignore +42 -0
- data/.rubocop.yml +24 -0
- data/.ruby-version +1 -0
- data/.travis.yml +2 -0
- data/.yardopts +1 -0
- data/CHANGES.md +3 -0
- data/Gemfile +3 -0
- data/LICENSE.md +22 -0
- data/README.md +291 -0
- data/Rakefile +49 -0
- data/lib/typesafe_enum/base.rb +133 -0
- data/lib/typesafe_enum/module_info.rb +10 -0
- data/lib/typesafe_enum.rb +3 -0
- data/spec/.rubocop.yml +7 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/unit/typesafe_enum/base_spec.rb +370 -0
- data/typesafe_enum.gemspec +33 -0
- metadata +163 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6e6e6d0a15330f5ec2dd13061e2da603ec354705
|
4
|
+
data.tar.gz: cfaca6a2bc5a2dd50dd2442abc23a6fe5775e8dd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0808cf4b53fc63cd69d330cec45fe2124f73380070e6db5d7c6c03e389f8dbf32d1b9c36ea54147cbe6f2abfcc458b35416371f1242c17d191208e91342033ad
|
7
|
+
data.tar.gz: 604dfd70e95d123a72045757aba249b474c3cb8869282d4eac3d38dd37663a56e5f7724439de1dafc20532b8d2eb79bb813fff60fad22064ac5229dd050df7c7
|
data/.gitignore
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# Ruby defaults
|
2
|
+
|
3
|
+
/.bundle/
|
4
|
+
/.yardoc
|
5
|
+
/Gemfile.lock
|
6
|
+
/_yardoc/
|
7
|
+
/coverage/
|
8
|
+
/doc/
|
9
|
+
/pkg/
|
10
|
+
/spec/reports/
|
11
|
+
/tmp/
|
12
|
+
*.bundle
|
13
|
+
*.so
|
14
|
+
*.o
|
15
|
+
*.a
|
16
|
+
mkmf.log
|
17
|
+
|
18
|
+
# Database
|
19
|
+
|
20
|
+
db/*.sqlite3
|
21
|
+
|
22
|
+
# Logs
|
23
|
+
|
24
|
+
/log/
|
25
|
+
|
26
|
+
# IntellJ
|
27
|
+
|
28
|
+
*.iml
|
29
|
+
*.ipr
|
30
|
+
*.iws
|
31
|
+
*.ids
|
32
|
+
.rakeTasks
|
33
|
+
|
34
|
+
# Emacs
|
35
|
+
|
36
|
+
*~
|
37
|
+
\#*
|
38
|
+
.#*
|
39
|
+
|
40
|
+
# Mac OS
|
41
|
+
|
42
|
+
.DS_Store
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# Disable compact style check for example.rb
|
2
|
+
Style/ClassAndModuleChildren:
|
3
|
+
Exclude:
|
4
|
+
- 'example.rb'
|
5
|
+
|
6
|
+
# Disable line-length check; it's too easy for the cure to be worse than the disease
|
7
|
+
Metrics/LineLength:
|
8
|
+
Enabled: False
|
9
|
+
|
10
|
+
# Disable problematic module documentation check (see https://github.com/bbatsov/rubocop/issues/947)
|
11
|
+
Style/Documentation:
|
12
|
+
Enabled: false
|
13
|
+
|
14
|
+
# Allow one line around class body (Style/EmptyLines will still disallow two or more)
|
15
|
+
Style/EmptyLinesAroundClassBody:
|
16
|
+
Enabled: false
|
17
|
+
|
18
|
+
# Allow one line around module body (Style/EmptyLines will still disallow two or more)
|
19
|
+
Style/EmptyLinesAroundModuleBody:
|
20
|
+
Enabled: false
|
21
|
+
|
22
|
+
# Allow one line around block body (Style/EmptyLines will still disallow two or more)
|
23
|
+
Style/EmptyLinesAroundBlockBody:
|
24
|
+
Enabled: false
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.2.3
|
data/.travis.yml
ADDED
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--markup markdown
|
data/CHANGES.md
ADDED
data/Gemfile
ADDED
data/LICENSE.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 The Regents of the University of California
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,291 @@
|
|
1
|
+
# typesafe_enum
|
2
|
+
|
3
|
+
[](https://travis-ci.org/dmolesUC3/typesafe_enum)
|
4
|
+
[](https://codeclimate.com/github/dmolesUC3/typesafe_enum)
|
5
|
+
[](http://inch-ci.org/github/dmolesUC3/typesafe_enum)
|
6
|
+
[](https://github.com/dmolesUC3/typesafe_enum/releases)
|
7
|
+
|
8
|
+
A Ruby implementation of Joshua Bloch's
|
9
|
+
[typesafe enum pattern](http://www.oracle.com/technetwork/java/page1-139488.html#replaceenums),
|
10
|
+
with syntax loosely inspired by [Ruby::Enum](https://github.com/dblock/ruby-enum).
|
11
|
+
|
12
|
+
## Basic usage
|
13
|
+
|
14
|
+
Create a new enum class and a set of instances:
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
require 'typesafe_enum'
|
18
|
+
|
19
|
+
class Suit < TypesafeEnum::Base
|
20
|
+
new :CLUBS
|
21
|
+
new :DIAMONDS
|
22
|
+
new :HEARTS
|
23
|
+
new :SPADES
|
24
|
+
end
|
25
|
+
```
|
26
|
+
|
27
|
+
A constant is declared for each instance, with the key symbol as its
|
28
|
+
value:
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
Suit::CLUBS
|
32
|
+
# => #<Suit:0x007fe9b3ba2698 @key=:CLUBS, @value="clubs", @ord=0>
|
33
|
+
```
|
34
|
+
|
35
|
+
By default, the `value` of an instance is its `key` symbol, lowercased:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
Suit::CLUBS.key
|
39
|
+
# => :CLUBS
|
40
|
+
Suit::CLUBS.value
|
41
|
+
# => 'clubs'
|
42
|
+
```
|
43
|
+
|
44
|
+
But you can also declare an explicit `value`:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
class Tarot < TypesafeEnum::Base
|
48
|
+
new :CUPS, 'Cups'
|
49
|
+
new :COINS, 'Coins'
|
50
|
+
new :WANDS, 'Wands'
|
51
|
+
new :SWORDS, 'Swords'
|
52
|
+
end
|
53
|
+
|
54
|
+
Tarot::CUPS.value
|
55
|
+
# => 'Cups'
|
56
|
+
```
|
57
|
+
|
58
|
+
And `values` need not be strings:
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
class Scale < TypesafeEnum::Base
|
62
|
+
new :DECA, 10
|
63
|
+
new :HECTO, 100
|
64
|
+
new :KILO, 1_000
|
65
|
+
new :MEGA, 1_000_000
|
66
|
+
end
|
67
|
+
|
68
|
+
Scale::KILO.value
|
69
|
+
# => 1000
|
70
|
+
```
|
71
|
+
|
72
|
+
## Ordering
|
73
|
+
|
74
|
+
Enum instances have an ordinal value corresponding to their declaration
|
75
|
+
order:
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
Suit::SPADES.ord
|
79
|
+
# => 3
|
80
|
+
```
|
81
|
+
|
82
|
+
And enum instances are comparable (within a type) based on that order:
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
Suit::SPADES.is_a?(Comparable)
|
86
|
+
# => true
|
87
|
+
Suit::SPADES > Suit::DIAMONDS
|
88
|
+
# => true
|
89
|
+
Suit::SPADES > Tarot::CUPS
|
90
|
+
# ArgumentError: comparison of Suit with Tarot failed
|
91
|
+
```
|
92
|
+
|
93
|
+
## Convenience methods on enum classes
|
94
|
+
|
95
|
+
### `::to_a`
|
96
|
+
|
97
|
+
Returns an array of the enum instances in declaration order:
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
Tarot.to_a
|
101
|
+
# => [#<Tarot:0x007fd4db30eca8 @key=:CUPS, @value="Cups", @ord=0>, #<Tarot:0x007fd4db30ebe0 @key=:COINS, @value="Coins", @ord=1>, #<Tarot:0x007fd4db30eaf0 @key=:WANDS, @value="Wands", @ord=2>, #<Tarot:0x007fd4db30e9b0 @key=:SWORDS, @value="Swords", @ord=3>]
|
102
|
+
```
|
103
|
+
|
104
|
+
### `::size`
|
105
|
+
|
106
|
+
Returns the number of enum instances:
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
Suit.size
|
110
|
+
# => 4
|
111
|
+
```
|
112
|
+
|
113
|
+
### `::each`, `::each_with_index`, and `::map`
|
114
|
+
|
115
|
+
Iterate over the set of enum instances:
|
116
|
+
|
117
|
+
```ruby
|
118
|
+
Suit.each { |s| puts s.value }
|
119
|
+
# clubs
|
120
|
+
# diamonds
|
121
|
+
# hearts
|
122
|
+
# spades
|
123
|
+
|
124
|
+
Suit.each_with_index { |s, i| puts "#{i}: #{s.key}" }
|
125
|
+
# 0: CLUBS
|
126
|
+
# 1: DIAMONDS
|
127
|
+
# 2: HEARTS
|
128
|
+
# 3: SPADES
|
129
|
+
|
130
|
+
Suit.map(&:value)
|
131
|
+
# => ["clubs", "diamonds", "hearts", "spades"]
|
132
|
+
```
|
133
|
+
|
134
|
+
### `::find_by_key`, `::find_by_value`, `::find_by_ord`
|
135
|
+
|
136
|
+
Look up an enum instance based on its key, value, or ordinal:
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
Tarot.find_by_key(:CUPS)
|
140
|
+
# => #<Tarot:0x007faab19fda40 @key=:CUPS, @value="Cups", @ord=0>
|
141
|
+
Tarot.find_by_value('Wands')
|
142
|
+
# => #<Tarot:0x007faab19fd8b0 @key=:WANDS, @value="Wands", @ord=2>
|
143
|
+
Tarot.find_by_ord(3)
|
144
|
+
# => #<Tarot:0x007faab19fd810 @key=:SWORDS, @value="Swords", @ord=3>
|
145
|
+
```
|
146
|
+
|
147
|
+
## Enum classes with methods
|
148
|
+
|
149
|
+
Enum classes can have methods, and other non-enum constants:
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
class Suit < TypesafeEnum::Base
|
153
|
+
new :CLUBS
|
154
|
+
new :DIAMONDS
|
155
|
+
new :HEARTS
|
156
|
+
new :SPADES
|
157
|
+
|
158
|
+
ALL_PIPS = %w(♣ ♦ ♥ ♠)
|
159
|
+
|
160
|
+
def pip
|
161
|
+
ALL_PIPS[self.ord]
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
Suit::ALL_PIPS
|
166
|
+
# => ["♣", "♦", "♥", "♠"]
|
167
|
+
|
168
|
+
Suit::CLUBS.pip
|
169
|
+
# => "♣"
|
170
|
+
|
171
|
+
Suit.map(&:pip)
|
172
|
+
# => ["♣", "♦", "♥", "♠"]
|
173
|
+
```
|
174
|
+
|
175
|
+
## Enum instances with methods
|
176
|
+
|
177
|
+
Enum instances can declare their own methods via `instance_eval`:
|
178
|
+
|
179
|
+
```ruby
|
180
|
+
class Operation < TypesafeEnum::Base
|
181
|
+
new(:PLUS, '+').instance_eval do
|
182
|
+
def eval(x, y)
|
183
|
+
x + y
|
184
|
+
end
|
185
|
+
end
|
186
|
+
new(:MINUS, '-').instance_eval do
|
187
|
+
def eval(x, y)
|
188
|
+
x - y
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
Operation::PLUS.eval(11, 17)
|
194
|
+
# => 28
|
195
|
+
|
196
|
+
Operation::MINUS.eval(28, 11)
|
197
|
+
# => 17
|
198
|
+
|
199
|
+
Operation.map { |op| op.eval(39, 23) }
|
200
|
+
# => [62, 16]
|
201
|
+
```
|
202
|
+
|
203
|
+
## How is this different from [Ruby::Enum](https://github.com/dblock/ruby-enum)?
|
204
|
+
|
205
|
+
[Ruby::Enum](https://github.com/dblock/ruby-enum) is much closer to the classic
|
206
|
+
[C enumeration](https://www.gnu.org/software/gnu-c-manual/gnu-c-manual.html#Enumerations)
|
207
|
+
as seen in C, [C++](https://msdn.microsoft.com/en-us/library/2dzy4k6e.aspx),
|
208
|
+
[C#](https://msdn.microsoft.com/en-us/library/sbbt4032.aspx), and
|
209
|
+
[Objective-C](https://developer.apple.com/library/ios/releasenotes/ObjectiveC/ModernizationObjC/AdoptingModernObjective-C/AdoptingModernObjective-C.html#//apple_ref/doc/uid/TP40014150-CH1-SW6).
|
210
|
+
In C and most C-like languages, an `enum` is simply a valued set of `int` values
|
211
|
+
(though C++ and others require an explicit cast to assign an `enum` value to
|
212
|
+
an `int` variable).
|
213
|
+
|
214
|
+
Similarly, a `Ruby::Enum` class is simply a valued set of values of any type,
|
215
|
+
with convenience methods for iterating over the set. Usually the values are
|
216
|
+
strings, but they can be of any type.
|
217
|
+
|
218
|
+
```ruby
|
219
|
+
# String enum
|
220
|
+
class Foo
|
221
|
+
include Ruby::Enum
|
222
|
+
|
223
|
+
new :BAR, 'bar'
|
224
|
+
new :BAZ, 'baz'
|
225
|
+
end
|
226
|
+
|
227
|
+
Foo::BAR
|
228
|
+
# => "bar"
|
229
|
+
Foo::BAR == 'bar'
|
230
|
+
# => true
|
231
|
+
|
232
|
+
# Integer enum
|
233
|
+
class Bar
|
234
|
+
include Ruby::Enum
|
235
|
+
|
236
|
+
new :BAR, 1
|
237
|
+
new :BAZ, 2
|
238
|
+
end
|
239
|
+
|
240
|
+
Bar::BAR
|
241
|
+
# => "bar"
|
242
|
+
Bar::BAR == 1
|
243
|
+
# => true
|
244
|
+
```
|
245
|
+
|
246
|
+
Java introduced the concept of "typesafe enums", first as a
|
247
|
+
[design pattern]((http://www.oracle.com/technetwork/java/page1-139488.html#replaceenums))
|
248
|
+
and later as a
|
249
|
+
[first-class language construct](https://docs.oracle.com/javase/1.5.0/docs/guide/language/enums.html).
|
250
|
+
In Java, an `Enum` class news a closed, valued set of _instances of that class,_ rather than
|
251
|
+
of a primitive type such as an `int`, and those instances have all the features of other objects,
|
252
|
+
such as methods, fields, and type membership. Likewise, a `TypesafeEnum` class news a valued set
|
253
|
+
of instances of that class, rather than of a set of some other type.
|
254
|
+
|
255
|
+
```ruby
|
256
|
+
Suit::CLUBS.is_a?(Suit)
|
257
|
+
# => true
|
258
|
+
Tarot::CUPS == 'Cups'
|
259
|
+
# => false
|
260
|
+
```
|
261
|
+
|
262
|
+
## How is this different from [java.lang.Enum](http://docs.oracle.com/javase/8/docs/api/java/lang/Enum.html)?
|
263
|
+
|
264
|
+
*[TODO: details]*
|
265
|
+
|
266
|
+
### Clunkier syntax
|
267
|
+
|
268
|
+
### No special `switch`/`case` support
|
269
|
+
|
270
|
+
### No serialization support
|
271
|
+
|
272
|
+
- but `#==`, `#hash` etc. are `Marshal`-safe
|
273
|
+
|
274
|
+
### No support classes like [`EnumSet`](http://docs.oracle.com/javase/8/docs/api/java/util/EnumSet.html) and
|
275
|
+
[`EnumMap`](http://docs.oracle.com/javase/8/docs/api/java/util/EnumMap.html)
|
276
|
+
|
277
|
+
### Enum classes are not closed
|
278
|
+
|
279
|
+
It's Ruby, so even though `:new` is private to each enum class, you
|
280
|
+
can always work around that:
|
281
|
+
|
282
|
+
```ruby
|
283
|
+
Suit.send(:new, :JOKERS)
|
284
|
+
# => #<Suit:0x007fc9e44e4778 @key=:JOKERS, @value="jokers", @ord=4>
|
285
|
+
Suit.map(&:key)
|
286
|
+
# => [:CLUBS, :DIAMONDS, :HEARTS, :SPADES, :JOKERS]
|
287
|
+
Suit.size
|
288
|
+
# => 5
|
289
|
+
```
|
290
|
+
|
291
|
+
### *[Other limitations...]*
|
data/Rakefile
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# ------------------------------------------------------------
|
2
|
+
# RSpec
|
3
|
+
|
4
|
+
require 'rspec/core'
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
|
7
|
+
namespace :spec do
|
8
|
+
|
9
|
+
desc 'Run all unit tests'
|
10
|
+
RSpec::Core::RakeTask.new(:unit) do |task|
|
11
|
+
task.rspec_opts = %w(--color --format documentation --order default)
|
12
|
+
task.pattern = 'unit/**/*_spec.rb'
|
13
|
+
end
|
14
|
+
|
15
|
+
task all: [:unit]
|
16
|
+
end
|
17
|
+
|
18
|
+
desc 'Run all tests'
|
19
|
+
task spec: 'spec:all'
|
20
|
+
|
21
|
+
# ------------------------------------------------------------
|
22
|
+
# Coverage
|
23
|
+
|
24
|
+
desc 'Run all unit tests with coverage'
|
25
|
+
task :coverage do
|
26
|
+
ENV['COVERAGE'] = 'true'
|
27
|
+
Rake::Task['spec:unit'].execute
|
28
|
+
end
|
29
|
+
|
30
|
+
# ------------------------------------------------------------
|
31
|
+
# RuboCop
|
32
|
+
|
33
|
+
require 'rubocop/rake_task'
|
34
|
+
RuboCop::RakeTask.new
|
35
|
+
|
36
|
+
# ------------------------------------------------------------
|
37
|
+
# TODOs
|
38
|
+
|
39
|
+
desc 'List TODOs (from spec/todo.rb)'
|
40
|
+
RSpec::Core::RakeTask.new(:todo) do |task|
|
41
|
+
task.rspec_opts = %w(--color --format documentation --order default)
|
42
|
+
task.pattern = 'todo.rb'
|
43
|
+
end
|
44
|
+
|
45
|
+
# ------------------------------------------------------------
|
46
|
+
# Defaults
|
47
|
+
|
48
|
+
desc 'Run unit tests, check test coverage, run acceptance tests, check code style'
|
49
|
+
task default: [:coverage, :rubocop]
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# A Ruby implementation of Joshua Bloch's
|
2
|
+
# [typesafe enum pattern](http://www.oracle.com/technetwork/java/page1-139488.html#replaceenums)
|
3
|
+
module TypesafeEnum
|
4
|
+
# Base class for typesafe enum classes.
|
5
|
+
class Base
|
6
|
+
include Comparable
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
# Returns an array of the enum instances in declaration order
|
11
|
+
def to_a
|
12
|
+
as_array.dup
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns the number of enum instances
|
16
|
+
def size
|
17
|
+
as_array ? as_array.length : 0
|
18
|
+
end
|
19
|
+
|
20
|
+
# Iterates over the set of enum instances
|
21
|
+
def each(&block)
|
22
|
+
to_a.each(&block)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Iterates over the set of enum instances
|
26
|
+
def each_with_index(&block)
|
27
|
+
to_a.each_with_index(&block)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Iterates over the set of enum instances
|
31
|
+
def map(&block)
|
32
|
+
to_a.map(&block)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Looks up an enum instance based on its key
|
36
|
+
def find_by_key(key)
|
37
|
+
by_key[key]
|
38
|
+
end
|
39
|
+
|
40
|
+
# Looks up an enum instance based on its value
|
41
|
+
def find_by_value(value)
|
42
|
+
by_value[value]
|
43
|
+
end
|
44
|
+
|
45
|
+
# Looks up an enum instance based on its ordinal
|
46
|
+
def find_by_ord(ord)
|
47
|
+
return nil if ord < 0 || ord > size
|
48
|
+
as_array[ord]
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
attr_accessor :by_key
|
54
|
+
attr_accessor :by_value
|
55
|
+
attr_accessor :as_array
|
56
|
+
|
57
|
+
def undefine_class
|
58
|
+
enclosing_module = Module.nesting.last
|
59
|
+
class_value = name.split('::').last || ''
|
60
|
+
enclosing_module.send(:remove_const, class_value)
|
61
|
+
end
|
62
|
+
|
63
|
+
def register(instance)
|
64
|
+
ensure_registries
|
65
|
+
key, value = valid_key_and_value(instance)
|
66
|
+
|
67
|
+
by_key[key] = instance
|
68
|
+
by_value[value] = instance
|
69
|
+
as_array << instance
|
70
|
+
const_set(key.to_s, instance)
|
71
|
+
end
|
72
|
+
|
73
|
+
def ensure_registries
|
74
|
+
self.by_key ||= {}
|
75
|
+
self.by_value ||= {}
|
76
|
+
self.as_array ||= []
|
77
|
+
end
|
78
|
+
|
79
|
+
def valid_key_and_value(instance)
|
80
|
+
key = instance.key
|
81
|
+
value = instance.value
|
82
|
+
|
83
|
+
begin
|
84
|
+
fail NameError, "#{name}::#{key} already exists" if find_by_key(key)
|
85
|
+
fail NameError, "A #{name} instance with value '#{value}' already exists" if find_by_value(value)
|
86
|
+
rescue NameError => e
|
87
|
+
undefine_class
|
88
|
+
raise e
|
89
|
+
end
|
90
|
+
|
91
|
+
[key, value]
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
# The symbol key for the enum instance
|
97
|
+
attr_reader :key
|
98
|
+
# The value encapsulated by the enum instance
|
99
|
+
attr_reader :value
|
100
|
+
# The ordinal of the enum instance, in declaration order
|
101
|
+
attr_reader :ord
|
102
|
+
|
103
|
+
# Compares two instances of the same enum class based on their declaration order
|
104
|
+
def <=>(other)
|
105
|
+
ord <=> other.ord if self.class == other.class
|
106
|
+
end
|
107
|
+
|
108
|
+
# Generates a Fixnum hash value for this enum instance
|
109
|
+
def hash
|
110
|
+
@hash ||= begin
|
111
|
+
result = 17
|
112
|
+
result = 31 * result + self.class.hash
|
113
|
+
result = 31 * result + ord
|
114
|
+
result.is_a?(Fixnum) ? result : result.hash
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
def initialize(key, value = nil)
|
121
|
+
fail TypeError, "#{key} is not a symbol" unless key.is_a?(Symbol)
|
122
|
+
@key = key
|
123
|
+
@value = value || key.to_s.downcase
|
124
|
+
@ord = self.class.size
|
125
|
+
self.class.class_exec(self) do |instance|
|
126
|
+
register(instance)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
private_class_method :new
|
131
|
+
|
132
|
+
end
|
133
|
+
end
|
data/spec/.rubocop.yml
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# ------------------------------------------------------------
|
2
|
+
# SimpleCov setup
|
3
|
+
|
4
|
+
if ENV['COVERAGE']
|
5
|
+
require 'simplecov'
|
6
|
+
require 'simplecov-console'
|
7
|
+
|
8
|
+
SimpleCov.minimum_coverage 100
|
9
|
+
SimpleCov.start do
|
10
|
+
add_filter '/spec/'
|
11
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
12
|
+
SimpleCov::Formatter::HTMLFormatter,
|
13
|
+
SimpleCov::Formatter::Console,
|
14
|
+
]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# ------------------------------------------------------------
|
19
|
+
# Rspec configuration
|
20
|
+
|
21
|
+
require 'rspec'
|
22
|
+
|
23
|
+
RSpec.configure do |config|
|
24
|
+
config.raise_errors_for_deprecations!
|
25
|
+
config.mock_with :rspec
|
26
|
+
end
|
27
|
+
|
28
|
+
# ------------------------------------------------------------
|
29
|
+
# TypesafeEnum
|
30
|
+
|
31
|
+
require 'typesafe_enum'
|
@@ -0,0 +1,370 @@
|
|
1
|
+
# coding: UTF-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
class Suit < TypesafeEnum::Base
|
5
|
+
new :CLUBS
|
6
|
+
new :DIAMONDS
|
7
|
+
new :HEARTS
|
8
|
+
new :SPADES
|
9
|
+
end
|
10
|
+
|
11
|
+
class Tarot < TypesafeEnum::Base
|
12
|
+
new :CUPS, 'Cups'
|
13
|
+
new :COINS, 'Coins'
|
14
|
+
new :WANDS, 'Wands'
|
15
|
+
new :SWORDS, 'Swords'
|
16
|
+
end
|
17
|
+
|
18
|
+
module TypesafeEnum
|
19
|
+
describe Base do
|
20
|
+
|
21
|
+
describe ':: new' do
|
22
|
+
it ' news a constant enum value' do
|
23
|
+
enum = Suit::CLUBS
|
24
|
+
expect(enum).to be_a(Suit)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'insists symbols be symbols' do
|
28
|
+
expect do
|
29
|
+
class Cheat < Base
|
30
|
+
new 'spades', 'spades'
|
31
|
+
end
|
32
|
+
end.to raise_error(TypeError)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'insists symbols be uppercase' do
|
36
|
+
expect do
|
37
|
+
class Cheat < Base
|
38
|
+
new :spades, 'spades'
|
39
|
+
end
|
40
|
+
end.to raise_error(NameError)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'disallows duplicate symbols' do
|
44
|
+
expect do
|
45
|
+
class Cheat < Base
|
46
|
+
new :SPADES, 'spades'
|
47
|
+
new :SPADES, 'more spades'
|
48
|
+
end
|
49
|
+
end.to raise_error(NameError)
|
50
|
+
|
51
|
+
expect { Cheat.class }.to raise_error(NameError)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'disallows duplicate values' do
|
55
|
+
expect do
|
56
|
+
class Cheat < Base
|
57
|
+
new :SPADES, 'spades'
|
58
|
+
new :ALSO_SPADES, 'spades'
|
59
|
+
end
|
60
|
+
end.to raise_error(NameError)
|
61
|
+
|
62
|
+
expect { Cheat.class }.to raise_error(NameError)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'defaults the value to a lower-cased version of the symbol' do
|
66
|
+
expect(Suit::CLUBS.value).to eq('clubs')
|
67
|
+
expect(Suit::DIAMONDS.value).to eq('diamonds')
|
68
|
+
expect(Suit::HEARTS.value).to eq('hearts')
|
69
|
+
expect(Suit::SPADES.value).to eq('spades')
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'is private' do
|
73
|
+
expect { Tarot.new(:PENTACLES) }.to raise_error(NoMethodError)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe '::to_a' do
|
78
|
+
it 'returns the values as an array' do
|
79
|
+
expect(Suit.to_a).to eq([Suit::CLUBS, Suit::DIAMONDS, Suit::HEARTS, Suit::SPADES])
|
80
|
+
end
|
81
|
+
it 'returns a copy' do
|
82
|
+
array = Suit.to_a
|
83
|
+
array.clear
|
84
|
+
expect(array.empty?).to eq(true)
|
85
|
+
expect(Suit.to_a).to eq([Suit::CLUBS, Suit::DIAMONDS, Suit::HEARTS, Suit::SPADES])
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe '::size' do
|
90
|
+
it 'returns the number of enum instnaces' do
|
91
|
+
expect(Suit.size).to eq(4)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe '::each' do
|
96
|
+
it 'iterates the enum values' do
|
97
|
+
expected = [Suit::CLUBS, Suit::DIAMONDS, Suit::HEARTS, Suit::SPADES]
|
98
|
+
index = 0
|
99
|
+
Suit.each do |s|
|
100
|
+
expect(s).to be(expected[index])
|
101
|
+
index += 1
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe '::each_with_index' do
|
107
|
+
it 'iterates the enum values with indices' do
|
108
|
+
expected = [Suit::CLUBS, Suit::DIAMONDS, Suit::HEARTS, Suit::SPADES]
|
109
|
+
Suit.each_with_index do |s, index|
|
110
|
+
expect(s).to be(expected[index])
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
describe '::map' do
|
116
|
+
it 'maps enum values' do
|
117
|
+
all_keys = Suit.map(&:key)
|
118
|
+
expect(all_keys).to eq([:CLUBS, :DIAMONDS, :HEARTS, :SPADES])
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe '#<=>' do
|
123
|
+
it 'orders enum instances' do
|
124
|
+
Suit.each_with_index do |s1, i1|
|
125
|
+
Suit.each_with_index do |s2, i2|
|
126
|
+
expect(s1 <=> s2).to eq(i1 <=> i2)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe '#==' do
|
133
|
+
it 'returns true for identical instances, false otherwise' do
|
134
|
+
Suit.each do |s1|
|
135
|
+
Suit.each do |s2|
|
136
|
+
identical = s1.equal?(s2)
|
137
|
+
expect(s1 == s2).to eq(identical)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'reports instances as unequal to nil and other objects' do
|
143
|
+
a_nil_value = nil
|
144
|
+
Suit.each do |s|
|
145
|
+
expect(s.nil?).to eq(false)
|
146
|
+
expect(s == a_nil_value).to eq(false)
|
147
|
+
Tarot.each do |t|
|
148
|
+
expect(s == t).to eq(false)
|
149
|
+
expect(t == s).to eq(false)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'survives marshalling' do
|
155
|
+
Suit.each do |s1|
|
156
|
+
dump = Marshal.dump(s1)
|
157
|
+
s2 = Marshal.load(dump)
|
158
|
+
expect(s1 == s2).to eq(true)
|
159
|
+
expect(s2 == s1).to eq(true)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
describe '#!=' do
|
165
|
+
it 'returns false for identical instances, true otherwise' do
|
166
|
+
Suit.each do |s1|
|
167
|
+
Suit.each do |s2|
|
168
|
+
different = !s1.equal?(s2)
|
169
|
+
expect(s1 != s2).to eq(different)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'reports instances as unequal to nil and other objects' do
|
175
|
+
a_nil_value = nil
|
176
|
+
Suit.each do |s|
|
177
|
+
expect(s != a_nil_value).to eq(true)
|
178
|
+
Tarot.each do |t|
|
179
|
+
expect(s != t).to eq(true)
|
180
|
+
expect(t != s).to eq(true)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
describe '#hash' do
|
187
|
+
it 'gives consistent values' do
|
188
|
+
Suit.each do |s1|
|
189
|
+
Suit.each do |s2|
|
190
|
+
expect(s1.hash == s2.hash).to eq(s1 == s2)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
it 'returns different values for different types' do
|
196
|
+
class Suit2 < Base
|
197
|
+
new :CLUBS, 'Clubs'
|
198
|
+
new :DIAMONDS, 'Diamonds'
|
199
|
+
new :HEARTS, 'Hearts'
|
200
|
+
new :SPADES, 'Spades'
|
201
|
+
end
|
202
|
+
|
203
|
+
Suit.each do |s1|
|
204
|
+
s2 = Suit2.find_by_key(s1.key)
|
205
|
+
expect(s1.hash == s2.hash).to eq(false)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
it 'survives marshalling' do
|
210
|
+
Suit.each do |s1|
|
211
|
+
dump = Marshal.dump(s1)
|
212
|
+
s2 = Marshal.load(dump)
|
213
|
+
expect(s2.hash).to eq(s1.hash)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
it 'always returns a Fixnum' do
|
218
|
+
Suit.each do |s1|
|
219
|
+
expect(s1.hash).to be_a(Fixnum)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
describe '#eql?' do
|
225
|
+
it 'is consistent with #hash' do
|
226
|
+
Suit.each do |s1|
|
227
|
+
Suit.each do |s2|
|
228
|
+
expect(s1.eql?(s2)).to eq(s1.hash == s2.hash)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
describe '#value' do
|
235
|
+
it 'returns the string value of the enum instance' do
|
236
|
+
expected = %w(clubs diamonds hearts spades)
|
237
|
+
Suit.each_with_index do |s, index|
|
238
|
+
expect(s.value).to eq(expected[index])
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
describe '#key' do
|
244
|
+
it 'returns the symbol key of the enum instance' do
|
245
|
+
expected = [:CLUBS, :DIAMONDS, :HEARTS, :SPADES]
|
246
|
+
Suit.each_with_index do |s, index|
|
247
|
+
expect(s.key).to eq(expected[index])
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
describe '#ord' do
|
253
|
+
it 'returns the ord value of the enum instance' do
|
254
|
+
Suit.each_with_index do |s, index|
|
255
|
+
expect(s.ord).to eq(index)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
describe '::find_by_key' do
|
261
|
+
it 'maps symbol keys to enum instances' do
|
262
|
+
keys = [:CLUBS, :DIAMONDS, :HEARTS, :SPADES]
|
263
|
+
expected = Suit.to_a
|
264
|
+
keys.each_with_index do |k, index|
|
265
|
+
expect(Suit.find_by_key(k)).to be(expected[index])
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
it 'returns nil for invalid keys' do
|
270
|
+
expect(Suit.find_by_key(:WANDS)).to be_nil
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
describe '::find_by_value' do
|
275
|
+
it 'maps values to enum instances' do
|
276
|
+
values = %w(clubs diamonds hearts spades)
|
277
|
+
expected = Suit.to_a
|
278
|
+
values.each_with_index do |n, index|
|
279
|
+
expect(Suit.find_by_value(n)).to be(expected[index])
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
it 'returns nil for invalid values' do
|
284
|
+
expect(Suit.find_by_value('wands')).to be_nil
|
285
|
+
end
|
286
|
+
|
287
|
+
it 'supports enums with symbol values' do
|
288
|
+
class RGBColors < Base
|
289
|
+
new :RED, :red
|
290
|
+
new :GREEN, :green
|
291
|
+
new :BLUE, :blue
|
292
|
+
end
|
293
|
+
|
294
|
+
RGBColors.each do |c|
|
295
|
+
expect(RGBColors.find_by_value(c.value)).to be(c)
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
it 'supports enums with integer values' do
|
300
|
+
class Scale < Base
|
301
|
+
new :DECA, 10
|
302
|
+
new :HECTO, 100
|
303
|
+
new :KILO, 1_000
|
304
|
+
new :MEGA, 1_000_000
|
305
|
+
end
|
306
|
+
|
307
|
+
Scale.each do |s|
|
308
|
+
expect(Scale.find_by_value(s.value)).to be(s)
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
describe '::find_by_ord' do
|
314
|
+
|
315
|
+
it 'maps ordinal indices to enum instances' do
|
316
|
+
Suit.each do |s|
|
317
|
+
expect(Suit.find_by_ord(s.ord)).to be(s)
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
it 'returns nil for negative indices' do
|
322
|
+
expect(Suit.find_by_ord(-1)).to be_nil
|
323
|
+
end
|
324
|
+
|
325
|
+
it 'returns nil for out-of-range indices' do
|
326
|
+
expect(Suit.find_by_ord(Suit.size)).to be_nil
|
327
|
+
end
|
328
|
+
|
329
|
+
it 'returns nil for invalid indices' do
|
330
|
+
expect(Suit.find_by_ord(100)).to be_nil
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
it 'supports case statements' do
|
335
|
+
def pip(suit)
|
336
|
+
case suit
|
337
|
+
when Suit::CLUBS
|
338
|
+
'♣'
|
339
|
+
when Suit::DIAMONDS
|
340
|
+
'♦'
|
341
|
+
when Suit::HEARTS
|
342
|
+
'♥'
|
343
|
+
when Suit::SPADES
|
344
|
+
'♠'
|
345
|
+
else
|
346
|
+
fail "unknown suit: #{self}"
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
expect(Suit.map { |s| pip(s) }).to eq(%w(♣ ♦ ♥ ♠))
|
351
|
+
end
|
352
|
+
|
353
|
+
it 'supports "inner class" methods via instance_eval' do
|
354
|
+
class Operation < Base
|
355
|
+
new(:PLUS, '+').instance_eval do
|
356
|
+
def eval(x, y)
|
357
|
+
x + y
|
358
|
+
end
|
359
|
+
end
|
360
|
+
new(:MINUS, '-').instance_eval do
|
361
|
+
def eval(x, y)
|
362
|
+
x - y
|
363
|
+
end
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
expect(Operation.map { |op| op.eval(39, 23) }).to eq([39 + 23, 39 - 23])
|
368
|
+
end
|
369
|
+
end
|
370
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
require 'uri'
|
6
|
+
require 'typesafe_enum/module_info'
|
7
|
+
|
8
|
+
Gem::Specification.new do |spec|
|
9
|
+
spec.name = TypesafeEnum::NAME
|
10
|
+
spec.version = TypesafeEnum::VERSION
|
11
|
+
spec.authors = ['David Moles']
|
12
|
+
spec.email = ['david.moles@ucop.edu']
|
13
|
+
spec.summary = 'Typesafe enum pattern for Ruby'
|
14
|
+
spec.description = 'A gem that implements the typesafe enum pattern in Ruby'
|
15
|
+
spec.license = 'MIT'
|
16
|
+
|
17
|
+
origin_uri = URI(`git config --get remote.origin.url`.chomp)
|
18
|
+
spec.homepage = URI::HTTP.build(host: origin_uri.host, path: origin_uri.path.chomp('.git')).to_s
|
19
|
+
|
20
|
+
spec.files = `git ls-files -z`.split("\x0")
|
21
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
22
|
+
|
23
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
24
|
+
spec.require_paths = ['lib']
|
25
|
+
|
26
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
27
|
+
spec.add_development_dependency 'rake', '~> 10.4'
|
28
|
+
spec.add_development_dependency 'rspec', '~> 3.2'
|
29
|
+
spec.add_development_dependency 'simplecov', '~> 0.9.2'
|
30
|
+
spec.add_development_dependency 'simplecov-console', '~> 0.2.0'
|
31
|
+
spec.add_development_dependency 'rubocop', '~> 0.32.1'
|
32
|
+
spec.add_development_dependency 'yard', '~> 0.8'
|
33
|
+
end
|
metadata
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: typesafe_enum
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- David Moles
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-11-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.4'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.4'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.2'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.2'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: simplecov
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.9.2
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.9.2
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: simplecov-console
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.2.0
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.2.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.32.1
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 0.32.1
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: yard
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0.8'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0.8'
|
111
|
+
description: A gem that implements the typesafe enum pattern in Ruby
|
112
|
+
email:
|
113
|
+
- david.moles@ucop.edu
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- ".gitignore"
|
119
|
+
- ".rubocop.yml"
|
120
|
+
- ".ruby-version"
|
121
|
+
- ".travis.yml"
|
122
|
+
- ".yardopts"
|
123
|
+
- CHANGES.md
|
124
|
+
- Gemfile
|
125
|
+
- LICENSE.md
|
126
|
+
- README.md
|
127
|
+
- Rakefile
|
128
|
+
- lib/typesafe_enum.rb
|
129
|
+
- lib/typesafe_enum/base.rb
|
130
|
+
- lib/typesafe_enum/module_info.rb
|
131
|
+
- spec/.rubocop.yml
|
132
|
+
- spec/spec_helper.rb
|
133
|
+
- spec/unit/typesafe_enum/base_spec.rb
|
134
|
+
- typesafe_enum.gemspec
|
135
|
+
homepage: http://github.com/dmolesUC3/typesafe_enum
|
136
|
+
licenses:
|
137
|
+
- MIT
|
138
|
+
metadata: {}
|
139
|
+
post_install_message:
|
140
|
+
rdoc_options: []
|
141
|
+
require_paths:
|
142
|
+
- lib
|
143
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
144
|
+
requirements:
|
145
|
+
- - ">="
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: '0'
|
148
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
requirements: []
|
154
|
+
rubyforge_project:
|
155
|
+
rubygems_version: 2.4.5.1
|
156
|
+
signing_key:
|
157
|
+
specification_version: 4
|
158
|
+
summary: Typesafe enum pattern for Ruby
|
159
|
+
test_files:
|
160
|
+
- spec/.rubocop.yml
|
161
|
+
- spec/spec_helper.rb
|
162
|
+
- spec/unit/typesafe_enum/base_spec.rb
|
163
|
+
has_rdoc:
|