type_is_enum 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0404f23335ab5f97a1afd40dfd03b867fb2b820a
4
+ data.tar.gz: 7f7cc6cf0103fb2e158813f2c638b9757d3075dd
5
+ SHA512:
6
+ metadata.gz: 99ab72b0c8fca5006bbe28e3f77b9615e3537ea7df28ffcaeb03df588b28368619394b5718545f373e1ac1cb3ccb53545df4cfb8564cfa69648b6e2565047fe0
7
+ data.tar.gz: b28d6949c5447d1135700a3e0acc1f9ac263023f79aa432155bf5afe787aebb58b333a93a8cc616b1aced7c8cf1dbb254a4f99baaa901089a6bde6b6e5a51571
@@ -0,0 +1,45 @@
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
+ # Build output
19
+ *.gem
20
+
21
+ # Database
22
+
23
+ db/*.sqlite3
24
+
25
+ # Logs
26
+
27
+ /log/
28
+
29
+ # IntellJ
30
+
31
+ *.iml
32
+ *.ipr
33
+ *.iws
34
+ *.ids
35
+ .rakeTasks
36
+
37
+ # Emacs
38
+
39
+ *~
40
+ \#*
41
+ .#*
42
+
43
+ # Mac OS
44
+
45
+ .DS_Store
@@ -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
@@ -0,0 +1 @@
1
+ 2.2.3
@@ -0,0 +1,2 @@
1
+ language: ruby
2
+
@@ -0,0 +1 @@
1
+ --markup markdown
@@ -0,0 +1,47 @@
1
+ ## 0.1.7 (28 April 2016)
2
+
3
+ - The default `to_s` for `TypesafeEnum::Enum` now includes the enum's class, key, value,
4
+ and ordinal, e.g.
5
+
6
+ Suit::DIAMONDS.to_s
7
+ # => "Suit::DIAMONDS [1] -> diamonds"
8
+
9
+ (Fixes [#5](https://github.com/dmolesUC3/typesafe_enum/issues/5).)
10
+ - `::find_by_value_str` now uses a hash lookup like the other `::find_by` methods.
11
+ - Improved method documentation.
12
+
13
+ ## 0.1.6 (15 Mar 2016)
14
+
15
+ - [#3](https://github.com/dmolesUC3/typesafe_enum/pull/3) - No need for `instance_eval`
16
+ when creating new enum instance methods - [@dblock](https://github.com/dblock).
17
+
18
+ ## 0.1.5 (27 Jan 2016)
19
+
20
+ - Include the original call site in the warning message for duplicate instances to help
21
+ with debugging.
22
+ - Modified gemspec to take into account SSH checkouts when determining the homepage URL.
23
+
24
+ ## 0.1.4 (18 Dec 2015))
25
+
26
+ - Exact duplicate instances (e.g. due to multiple `requires`) are now ignored with a warning,
27
+ instead of causing a `NameError`. Duplicate keys with different values, and duplicate values
28
+ with different keys, still raise a `NameError`.
29
+ - `NameErrors` due to invalid keys or values no longer cause the enum class to be undefined.
30
+ However, the invalid instances will still not be registered and no constants created for them.
31
+
32
+ ## 0.1.3 (17 Dec 2015)
33
+
34
+ - Fixed issue where invalid classes weren't properly removed after duplicate name declarations,
35
+ polluting the namespace and causing duplicate declration error messages to be lost.
36
+
37
+ ## 0.1.2 (19 Nov 2015)
38
+
39
+ - Fixed issue where `::find_by_value_str` failed to return `nil` for bad values
40
+
41
+ ## 0.1.1 (19 Nov 2015)
42
+
43
+ - Added `::find_by_value_str`
44
+
45
+ ## 0.1.0 (18 Nov 2015)
46
+
47
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 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.
@@ -0,0 +1,478 @@
1
+ # TypeIsEnum
2
+
3
+ [![Build Status](https://travis-ci.org/dmolesUC3/typesafe_enum.svg?branch=master)](https://travis-ci.org/dmolesUC3/typesafe_enum)
4
+ [![Code Climate](https://codeclimate.com/github/dmolesUC3/typesafe_enum.svg)](https://codeclimate.com/github/dmolesUC3/typesafe_enum)
5
+ [![Inline docs](http://inch-ci.org/github/dmolesUC3/typesafe_enum.svg)](http://inch-ci.org/github/dmolesUC3/typesafe_enum)
6
+ [![Gem Version](https://img.shields.io/gem/v/typesafe_enum.svg)](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
+ ## Table of contents
13
+
14
+ - [Basic usage](#basic-usage)
15
+ - [Ordering](#ordering)
16
+ - [String representations](#string-representations)
17
+ - [Convenience methods on enum classes](#convenience-methods-on-enum-classes)
18
+ - [::to\_a](#to_a)
19
+ - [::size](#size)
20
+ - [::each, ::each\_with\_index, and ::map](#each-each_with_index-and-map)
21
+ - [::find\_by\_key, ::find\_by\_value, ::find\_by\_ord](#find_by_key-find_by_value-find_by_ord)
22
+ - [::find\_by\_value\_str](#find_by_value_str)
23
+ - [Enum classes with methods](#enum-classes-with-methods)
24
+ - [Enum instances with methods](#enum-instances-with-methods)
25
+ - [How is this different from Ruby::Enum?](#how-is-this-different-from-rubyenum)
26
+ - [How is this different from java.lang.Enum?](#how-is-this-different-from-javalangenum)
27
+ - [Clunkier syntax](#clunkier-syntax)
28
+ - [No special switch/case support](#no-special-switchcase-support)
29
+ - [No serialization support](#no-serialization-support)
30
+ - [No support classes](#no-support-classes)
31
+ - [Enum classes are not closed](#enum-classes-are-not-closed)
32
+ - [Contributing](#contributing)
33
+
34
+ ## Basic usage
35
+
36
+ Create a new enum class and a set of instances:
37
+
38
+ ```ruby
39
+ require 'typesafe_enum'
40
+
41
+ class Suit < TypeIsEnum::Enum
42
+ new :CLUBS
43
+ new :DIAMONDS
44
+ new :HEARTS
45
+ new :SPADES
46
+ end
47
+ ```
48
+
49
+ A constant is declared for each instance, with an instance of the new
50
+ class as the value of that constant:
51
+
52
+ ```ruby
53
+ Suit::CLUBS
54
+ # => #<Suit:0x007fe9b3ba2698 @key=:CLUBS, @value="clubs", @ord=0>
55
+ ```
56
+
57
+ By default, the `value` of an instance is its `key` symbol, lowercased:
58
+
59
+ ```ruby
60
+ Suit::CLUBS.key
61
+ # => :CLUBS
62
+ Suit::CLUBS.value
63
+ # => 'clubs'
64
+ ```
65
+
66
+ But you can also declare an explicit `value`:
67
+
68
+ ```ruby
69
+ class Tarot < TypeIsEnum::ValueEnum
70
+ new :CUPS, 'Cups'
71
+ new :COINS, 'Coins'
72
+ new :WANDS, 'Wands'
73
+ new :SWORDS, 'Swords'
74
+ end
75
+
76
+ Tarot::CUPS.value
77
+ # => 'Cups'
78
+ ```
79
+
80
+ And `values` need not be strings:
81
+
82
+ ```ruby
83
+ class Scale < TypeIsEnum::ValueEnum
84
+ new :DECA, 10
85
+ new :HECTO, 100
86
+ new :KILO, 1_000
87
+ new :MEGA, 1_000_000
88
+ end
89
+
90
+ Scale::KILO.value
91
+ # => 1000
92
+ ```
93
+
94
+ Declaring two instances with the same key will produce an error:
95
+
96
+ ```ruby
97
+ class Suit < TypeIsEnum::Enum
98
+ new :CLUBS
99
+ new :DIAMONDS
100
+ new :HEARTS
101
+ new :SPADES
102
+ new :SPADES, '♠'
103
+ end
104
+ ```
105
+
106
+ ```
107
+ typesafe_enum/lib/typesafe_enum/base.rb:88:in `valid_key_and_value': Suit::SPADES already exists (NameError)
108
+ from /Users/dmoles/Work/Stash/typesafe_enum/lib/typesafe_enum/base.rb:98:in `register'
109
+ from /Users/dmoles/Work/Stash/typesafe_enum/lib/typesafe_enum/base.rb:138:in `block in initialize'
110
+ from /Users/dmoles/Work/Stash/typesafe_enum/lib/typesafe_enum/base.rb:137:in `class_exec'
111
+ from /Users/dmoles/Work/Stash/typesafe_enum/lib/typesafe_enum/base.rb:137:in `initialize'
112
+ from ./scratch.rb:11:in `new'
113
+ from ./scratch.rb:11:in `<class:Suit>'
114
+ from ./scratch.rb:6:in `<main>'
115
+ ```
116
+
117
+ Likewise two instances with the same value but different keys:
118
+
119
+ ```ruby
120
+ class Tarot < TypeIsEnum::ValueEnum
121
+ new :CUPS, 'Cups'
122
+ new :COINS, 'Coins'
123
+ new :WANDS, 'Wands'
124
+ new :SWORDS, 'Swords'
125
+ new :STAVES, 'Wands'
126
+ end
127
+ ```
128
+
129
+ ```
130
+ /typesafe_enum/lib/typesafe_enum/base.rb:92:in `valid_key_and_value': A Tarot instance with value 'Wands' already exists (NameError)
131
+ from /Users/dmoles/Work/Stash/typesafe_enum/lib/typesafe_enum/base.rb:98:in `register'
132
+ from /Users/dmoles/Work/Stash/typesafe_enum/lib/typesafe_enum/base.rb:138:in `block in initialize'
133
+ from /Users/dmoles/Work/Stash/typesafe_enum/lib/typesafe_enum/base.rb:137:in `class_exec'
134
+ from /Users/dmoles/Work/Stash/typesafe_enum/lib/typesafe_enum/base.rb:137:in `initialize'
135
+ from ./scratch.rb:11:in `new'
136
+ from ./scratch.rb:11:in `<class:Tarot>'
137
+ from ./scratch.rb:6:in `<main>'
138
+ ```
139
+
140
+ However, declaring an identical key/value pair will be ignored with a warning, to avoid unnecessary errors
141
+ when, e.g., a declaration file is accidentally loaded twice.
142
+
143
+ ```ruby
144
+ class Tarot < TypeIsEnum::ValueEnum
145
+ new :CUPS, 'Cups'
146
+ new :COINS, 'Coins'
147
+ new :WANDS, 'Wands'
148
+ new :SWORDS, 'Swords'
149
+ end
150
+
151
+ class Tarot < TypeIsEnum::ValueEnum
152
+ new :CUPS, 'Cups'
153
+ new :COINS, 'Coins'
154
+ new :WANDS, 'Wands'
155
+ new :SWORDS, 'Swords'
156
+ end
157
+
158
+ # => ignoring redeclaration of Tarot::CUPS with value Cups (source: /tmp/duplicate_enum.rb:13:in `new')
159
+ # => ignoring redeclaration of Tarot::COINS with value Coins (source: /tmp/duplicate_enum.rb:14:in `new')
160
+ # => ignoring redeclaration of Tarot::WANDS with value Wands (source: /tmp/duplicate_enum.rb:15:in `new')
161
+ # => ignoring redeclaration of Tarot::SWORDS with value Swords (source: /tmp/duplicate_enum.rb:16:in `new')
162
+ ```
163
+
164
+ **Note:** If do you see these warnings, it probably means there's something wrong with your `$LOAD_PATH` (e.g.,
165
+ the same directory present both via its real path and via a symlink). This can cause all sorts of problems,
166
+ and Ruby's `require` statement is [known to be not smart enough to deal with it](https://bugs.ruby-lang.org/issues/4403),
167
+ so it's worth tracking down and fixing the root cause.
168
+
169
+ ## Ordering
170
+
171
+ Enum instances have an ordinal value corresponding to their declaration
172
+ order:
173
+
174
+ ```ruby
175
+ Suit::SPADES.ord
176
+ # => 3
177
+ ```
178
+
179
+ And enum instances are comparable (within a type) based on that order:
180
+
181
+ ```ruby
182
+ Suit::SPADES.is_a?(Comparable)
183
+ # => true
184
+ Suit::SPADES > Suit::DIAMONDS
185
+ # => true
186
+ Suit::SPADES > Tarot::CUPS
187
+ # ArgumentError: comparison of Suit with Tarot failed
188
+ ```
189
+
190
+ ## String representations
191
+
192
+ The default `to_s` implementation provides the enum's class, key, value,
193
+ and ordinal, e.g.
194
+
195
+ ```ruby
196
+ Suit::DIAMONDS.to_s
197
+ # => "Suit::DIAMONDS [1] -> diamonds"
198
+ ```
199
+
200
+ It can of course be overridden.
201
+
202
+ ## Convenience methods on enum classes
203
+
204
+ ### `::to_a`
205
+
206
+ Returns an array of the enum instances in declaration order:
207
+
208
+ ```ruby
209
+ Tarot.to_a
210
+ # => [#<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>]
211
+ ```
212
+
213
+ ### `::size`
214
+
215
+ Returns the number of enum instances:
216
+
217
+ ```ruby
218
+ Suit.size
219
+ # => 4
220
+ ```
221
+
222
+ ### `::each`, `::each_with_index`, and `::map`
223
+
224
+ Iterate over the set of enum instances:
225
+
226
+ ```ruby
227
+ Suit.each { |s| puts s.value }
228
+ # clubs
229
+ # diamonds
230
+ # hearts
231
+ # spades
232
+
233
+ Suit.each_with_index { |s, i| puts "#{i}: #{s.key}" }
234
+ # 0: CLUBS
235
+ # 1: DIAMONDS
236
+ # 2: HEARTS
237
+ # 3: SPADES
238
+
239
+ Suit.map(&:value)
240
+ # => ["clubs", "diamonds", "hearts", "spades"]
241
+ ```
242
+
243
+ ### `::find_by_key`, `::find_by_value`, `::find_by_ord`
244
+
245
+ Look up an enum instance based on its key, value, or ordinal:
246
+
247
+ ```ruby
248
+ Tarot.find_by_key(:CUPS)
249
+ # => #<Tarot:0x007faab19fda40 @key=:CUPS, @value="Cups", @ord=0>
250
+ Tarot.find_by_value('Wands')
251
+ # => #<Tarot:0x007faab19fd8b0 @key=:WANDS, @value="Wands", @ord=2>
252
+ Tarot.find_by_ord(3)
253
+ # => #<Tarot:0x007faab19fd810 @key=:SWORDS, @value="Swords", @ord=3>
254
+ ```
255
+
256
+ ### `::find_by_value_str`
257
+
258
+ Look up an enum instance based on the string form of its value (as returned by `to_s`) --
259
+ useful for, e.g., XML or JSON mapping of enums with non-string values:
260
+
261
+ ```ruby
262
+ Scale.find_by_value_str('1000000')
263
+ # => #<Scale:0x007f8513a93810 @key=:MEGA, @value=1000000, @ord=3>
264
+ ```
265
+
266
+ ## Enum classes with methods
267
+
268
+ Enum classes are just classes. They can have methods, and other non-enum constants.
269
+ (The `:initialize` method for each class, though, is declared programmatically by
270
+ the base class. If you need to redefine it, be sure to alias and call the original.)
271
+
272
+ ```ruby
273
+ class Suit < TypeIsEnum::Enum
274
+ new :CLUBS
275
+ new :DIAMONDS
276
+ new :HEARTS
277
+ new :SPADES
278
+
279
+ ALL_PIPS = %w(♣ ♦ ♥ ♠)
280
+
281
+ def pip
282
+ ALL_PIPS[self.ord]
283
+ end
284
+ end
285
+
286
+ Suit::ALL_PIPS
287
+ # => ["♣", "♦", "♥", "♠"]
288
+
289
+ Suit::CLUBS.pip
290
+ # => "♣"
291
+
292
+ Suit.map(&:pip)
293
+ # => ["♣", "♦", "♥", "♠"]
294
+ ```
295
+
296
+ ## Enum instances with methods
297
+
298
+ Enum instances can declare their own methods:
299
+
300
+ ```ruby
301
+ class Operation < TypeIsEnum::Enum
302
+ new(:PLUS, '+') do
303
+ def eval(x, y)
304
+ x + y
305
+ end
306
+ end
307
+ new(:MINUS, '-') do
308
+ def eval(x, y)
309
+ x - y
310
+ end
311
+ end
312
+ end
313
+
314
+ Operation::PLUS.eval(11, 17)
315
+ # => 28
316
+
317
+ Operation::MINUS.eval(28, 11)
318
+ # => 17
319
+
320
+ Operation.map { |op| op.eval(39, 23) }
321
+ # => [62, 16]
322
+ ```
323
+
324
+ ## How is this different from [Ruby::Enum](https://github.com/dblock/ruby-enum)?
325
+
326
+ [Ruby::Enum](https://github.com/dblock/ruby-enum) is much closer to the classic
327
+ [C enumeration](https://www.gnu.org/software/gnu-c-manual/gnu-c-manual.html#Enumerations)
328
+ as seen in C, [C++](https://msdn.microsoft.com/en-us/library/2dzy4k6e.aspx),
329
+ [C#](https://msdn.microsoft.com/en-us/library/sbbt4032.aspx), and
330
+ [Objective-C](https://developer.apple.com/library/ios/releasenotes/ObjectiveC/ModernizationObjC/AdoptingModernObjective-C/AdoptingModernObjective-C.html#//apple_ref/doc/uid/TP40014150-CH1-SW6).
331
+ In C and most C-like languages, an `enum` is simply a named set of `int` values
332
+ (though C++ and others require an explicit cast to assign an `enum` value to
333
+ an `int` variable).
334
+
335
+ Similarly, a `Ruby::Enum` class is simply a named set of values of any type,
336
+ with convenience methods for iterating over the set. Usually the values are
337
+ strings, but they can be of any type.
338
+
339
+ ```ruby
340
+ # String enum
341
+ class Foo
342
+ include Ruby::Enum
343
+
344
+ new :BAR, 'bar'
345
+ new :BAZ, 'baz'
346
+ end
347
+
348
+ Foo::BAR
349
+ # => "bar"
350
+ Foo::BAR == 'bar'
351
+ # => true
352
+
353
+ # Integer enum
354
+ class Bar
355
+ include Ruby::Enum
356
+
357
+ new :BAR, 1
358
+ new :BAZ, 2
359
+ end
360
+
361
+ Bar::BAR
362
+ # => "bar"
363
+ Bar::BAR == 1
364
+ # => true
365
+ ```
366
+
367
+ Java introduced the concept of "typesafe enums", first as a
368
+ [design pattern](http://www.oracle.com/technetwork/java/page1-139488.html#replaceenums)
369
+ and later as a
370
+ [first-class language construct](https://docs.oracle.com/javase/1.5.0/docs/guide/language/enums.html).
371
+ In Java, an `Enum` class defines a closed, valued set of _instances of that class,_ rather than
372
+ of a primitive type such as an `int`, and those instances have all the features of other objects,
373
+ such as methods, fields, and type membership. Likewise, a `TypeIsEnum` class defines a valued set
374
+ of instances of that class, rather than of a set of some other type.
375
+
376
+ ```ruby
377
+ Suit::CLUBS.is_a?(Suit)
378
+ # => true
379
+ Tarot::CUPS == 'Cups'
380
+ # => false
381
+ ```
382
+
383
+ ## How is this different from `java.lang.Enum`?
384
+
385
+ ### Clunkier syntax
386
+
387
+ In Java 5+, you can define an enum in one line and instance-specific methods with a pair of braces.
388
+
389
+ ```java
390
+ enum CMYKColor {
391
+ CYAN, MAGENTA, YELLOW, BLACK
392
+ }
393
+
394
+ enum Suit {
395
+ CLUBS { char pip() { return '♣'; } },
396
+ DIAMONDS { char pip() { return '♦'; } },
397
+ HEARTS { char pip() { return '♥'; } },
398
+ SPADES { char pip() { return '♠'; } };
399
+
400
+ abstract char pip();
401
+ }
402
+ ```
403
+
404
+ With `TypeIsEnum`, instance-specific methods require extra parentheses,
405
+ as shown above, and about the best you can do even for simple enums is something like:
406
+
407
+ ```ruby
408
+ class CMYKColor < TypeIsEnum::Enum
409
+ [:CYAN, :MAGENTA, :YELLOW, :BLACK].each { |c| new c }
410
+ end
411
+ ```
412
+
413
+ ### No special `switch`/`case` support
414
+
415
+ The Java compiler will warn you if a `switch` statement doesn't include all instances of a Java enum.
416
+ Ruby doesn't care whether you cover all instances of a `TypeIsEnum`, and in fact it doesn't care if
417
+ your `when` statements include a mix of enum instances of different classes, or of enum instances and
418
+ other things. (In some respects this latter is a feature, of course.)
419
+
420
+ ### No serialization support
421
+
422
+ The Java `Enum` class has special code to ensure that enum instances are deserialized to the existing
423
+ singleton constants. This can be done with Ruby [`Marshal`](http://ruby-doc.org/core-2.2.3/Marshal.html)
424
+ (by defining `marshal_load`) but it didn't seem worth the trouble, so a deserialized `TypeIsEnum` will
425
+ not be identical to the original:
426
+
427
+ ```ruby
428
+ clubs2 = Marshal.load(Marshal.dump(Suit::CLUBS))
429
+ Suit::CLUBS.equal?(clubs2)
430
+ # => false
431
+ ```
432
+
433
+ However, `#==`, `#hash`, etc. are `Marshal`-safe:
434
+
435
+ ```ruby
436
+ Suit::CLUBS == clubs2
437
+ # => true
438
+ clubs2 == Suit::CLUBS
439
+ # => true
440
+ Suit::CLUBS.hash == clubs2.hash
441
+ # => true
442
+ ```
443
+
444
+ If this isn't enough, and the lack of object identity across marshalling is a problem, it could be added
445
+ in a later version. (Pull requests welcome!)
446
+
447
+ ### No support classes
448
+
449
+ Java has `Enum`-specific classes like
450
+ [`EnumSet`](http://docs.oracle.com/javase/8/docs/api/java/util/EnumSet.html) and
451
+ [`EnumMap`](http://docs.oracle.com/javase/8/docs/api/java/util/EnumMap.html) that provide special
452
+ high-performance, optimized versions of its collection interfaces. `TypeIsEnum` doesn't.
453
+
454
+ ### Enum classes are not closed
455
+
456
+ It's Ruby, so even though `:new` is private to each enum class, you
457
+ can work around that in various ways:
458
+
459
+ ```ruby
460
+ Suit.send(:new, :JOKERS)
461
+ # => #<Suit:0x007fc9e44e4778 @key=:JOKERS, @value="jokers", @ord=4>
462
+
463
+ class Tarot
464
+ new :MAJOR_ARCANA, 'Major Arcana'
465
+ end
466
+ # => #<Tarot:0x007f8513b39b20 @key=:MAJOR_ARCANA, @value="Major Arcana", @ord=4>
467
+
468
+ Suit.map(&:key)
469
+ # => [:CLUBS, :DIAMONDS, :HEARTS, :SPADES, :JOKERS]
470
+
471
+ Tarot.map(&:key)
472
+ # => [:CUPS, :COINS, :WANDS, :SWORDS, :MAJOR_ARCANA]
473
+ ```
474
+
475
+ ## Contributing
476
+
477
+ Pull requests are welcome, but please make sure the tests pass, the code has 100% coverage, and the
478
+ code style passes Rubocop. (The default rake task should check all of these for you.)