typesafe_enum 0.1.8 → 0.3.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.
@@ -1 +1 @@
1
- 2.4.1
1
+ 2.6.6
@@ -1,2 +1,9 @@
1
1
  language: ruby
2
2
 
3
+ os:
4
+ - linux
5
+ - osx
6
+
7
+ rvm:
8
+ - 2.6
9
+ - 2.7
data/CHANGES.md CHANGED
@@ -1,3 +1,26 @@
1
+ ## 0.3.0
2
+
3
+ - Support explicit nil values
4
+ - Update author email in gemspec
5
+ - Update RuboCop to version 0.91 and pin version
6
+ - Set minimum required_ruby_version to 2.6.0
7
+ - Bump .ruby-version to 2.6.6
8
+
9
+ ## 0.2.2 (23 April 2020)
10
+
11
+ - Implement [`Enumerable`](https://ruby-doc.org/core-2.6.5/Enumerable.html)
12
+
13
+ ## 0.2.1 (12 March 2020)
14
+
15
+ - Update to Rake 12.3.3
16
+
17
+ ## 0.2.0 (12 March 2020)
18
+
19
+ - Update to Ruby 2.6.5
20
+ - Update to Rake 10.4
21
+ - Update to RSpec 3.9
22
+ - Update to RuboCop 0.80
23
+
1
24
  ## 0.1.8 (22 December 2017)
2
25
 
3
26
  - Update to Ruby 2.4.1
data/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # TypesafeEnum
2
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)
3
+ [![Build Status](https://travis-ci.org/dmolesUC/typesafe_enum.svg?branch=master)](https://travis-ci.org/dmolesUC/typesafe_enum)
4
+ [![Code Climate](https://codeclimate.com/github/dmolesUC/typesafe_enum.svg)](https://codeclimate.com/github/dmolesUC/typesafe_enum)
5
+ [![Inline docs](http://inch-ci.org/github/dmolesUC/typesafe_enum.svg)](http://inch-ci.org/github/dmolesUC/typesafe_enum)
6
+ [![Gem Version](https://img.shields.io/gem/v/typesafe_enum.svg)](https://github.com/dmolesUC/typesafe_enum/releases)
7
7
 
8
8
  A Ruby implementation of Joshua Bloch's
9
9
  [typesafe enum pattern](http://www.oracle.com/technetwork/java/page1-139488.html#replaceenums),
@@ -14,23 +14,25 @@ with syntax loosely inspired by [Ruby::Enum](https://github.com/dblock/ruby-enum
14
14
  - [Basic usage](#basic-usage)
15
15
  - [Ordering](#ordering)
16
16
  - [String representations](#string-representations)
17
+ - [Enumerable](#enumerable)
17
18
  - [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)
19
+ - [#to_a](#to_a)
20
+ - [#size](#size)
21
+ - [#each, <code>#each_with_index</code>, <code>#map</code> and <code>#flat_map</code>](#each-each_with_index-map-and-flat_map)
22
+ - [#find_by_key, <code>#find_by_value</code>, <code>#find_by_ord</code>](#find_by_key-find_by_value-find_by_ord)
23
+ - [#find_by_value_str](#find_by_value_str)
23
24
  - [Enum classes with methods](#enum-classes-with-methods)
24
25
  - [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 <a href="https://github.com/dblock/ruby-enum">Ruby::Enum</a>?](#how-is-this-different-from-rubyenum)
26
27
  - [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)
28
+ - [Clunkier syntax](#clunkier-syntax)
29
+ - [No special switch/<code>case</code> support](#no-special-switchcase-support)
30
+ - [No serialization support](#no-serialization-support)
31
+ - [No support classes](#no-support-classes)
32
+ - [Enum classes are not closed](#enum-classes-are-not-closed)
32
33
  - [Contributing](#contributing)
33
34
 
35
+
34
36
  ## Basic usage
35
37
 
36
38
  Create a new enum class and a set of instances:
@@ -91,6 +93,20 @@ Scale::KILO.value
91
93
  # => 1000
92
94
  ```
93
95
 
96
+ Even `nil` is a valid value (if set explicitly):
97
+
98
+ ```ruby
99
+ class Scheme < TypesafeEnum::Base
100
+ new :HTTP, 'http'
101
+ new :HTTPS, 'https'
102
+ new :EXAMPLE, 'example'
103
+ new :UNKNOWN, nil
104
+ end
105
+
106
+ Scheme::UNKNOWN.value
107
+ # => nil
108
+ ```
109
+
94
110
  Declaring two instances with the same key will produce an error:
95
111
 
96
112
  ```ruby
@@ -199,9 +215,19 @@ Suit::DIAMONDS.to_s
199
215
 
200
216
  It can of course be overridden.
201
217
 
218
+ ## `Enumerable`
219
+
220
+ As of version 0.2.2, `TypesafeEnum` classes implement
221
+ [`Enumerable`](https://ruby-doc.org/core-2.6.5/Enumerable.html),
222
+ so they support methods such as
223
+ [`#find`](https://ruby-doc.org/core-2.6.5/Enumerable.html#method-i-find),
224
+ [`#select`](https://ruby-doc.org/core-2.6.5/Enumerable.html#method-i-select),
225
+ and [`#reduce`](https://ruby-doc.org/core-2.6.5/Enumerable.html#method-i-reduce),
226
+ in addition to the convenience methods called out specifically below.
227
+
202
228
  ## Convenience methods on enum classes
203
229
 
204
- ### `::to_a`
230
+ ### `#to_a`
205
231
 
206
232
  Returns an array of the enum instances in declaration order:
207
233
 
@@ -210,7 +236,7 @@ Tarot.to_a
210
236
  # => [#<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
237
  ```
212
238
 
213
- ### `::size`
239
+ ### `#size`
214
240
 
215
241
  Returns the number of enum instances:
216
242
 
@@ -219,7 +245,7 @@ Suit.size
219
245
  # => 4
220
246
  ```
221
247
 
222
- ### `::each`, `::each_with_index`, and `::map`
248
+ ### `#each`, `#each_with_index`, `#map` and `#flat_map`
223
249
 
224
250
  Iterate over the set of enum instances:
225
251
 
@@ -238,9 +264,12 @@ Suit.each_with_index { |s, i| puts "#{i}: #{s.key}" }
238
264
 
239
265
  Suit.map(&:value)
240
266
  # => ["clubs", "diamonds", "hearts", "spades"]
267
+
268
+ Suit.flat_map { |s| [s.key, s.value] }
269
+ # => [:CLUBS, "clubs", :DIAMONDS, "diamonds", :HEARTS, "hearts", :SPADES, "spades"]
241
270
  ```
242
271
 
243
- ### `::find_by_key`, `::find_by_value`, `::find_by_ord`
272
+ ### `#find_by_key`, `#find_by_value`, `#find_by_ord`
244
273
 
245
274
  Look up an enum instance based on its key, value, or ordinal:
246
275
 
@@ -253,7 +282,7 @@ Tarot.find_by_ord(3)
253
282
  # => #<Tarot:0x007faab19fd810 @key=:SWORDS, @value="Swords", @ord=3>
254
283
  ```
255
284
 
256
- ### `::find_by_value_str`
285
+ ### `#find_by_value_str`
257
286
 
258
287
  Look up an enum instance based on the string form of its value (as returned by `to_s`) --
259
288
  useful for, e.g., XML or JSON mapping of enums with non-string values:
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TypesafeEnum
4
- Dir.glob(File.expand_path('../typesafe_enum/*.rb', __FILE__)).sort.each(&method(:require))
4
+ Dir.glob(File.expand_path('typesafe_enum/*.rb', __dir__)).sort.each(&method(:require))
5
5
  end
@@ -8,6 +8,7 @@ module TypesafeEnum
8
8
  include Comparable
9
9
 
10
10
  class << self
11
+ include Enumerable
11
12
 
12
13
  # Returns an array of the enum instances in declaration order
13
14
  # @return [Array<self>] All instances of this enum, in declaration order
@@ -18,32 +19,16 @@ module TypesafeEnum
18
19
  # Returns the number of enum instances
19
20
  # @return [Integer] the number of instances
20
21
  def size
21
- as_array ? as_array.length : 0
22
+ as_array.size
22
23
  end
23
24
 
24
25
  # Iterates over the set of enum instances
25
26
  # @yield [self] Each instance of this enum, in declaration order
26
- # @return [Array<self>] All instances of this enum, in declaration order
27
+ # @return [Enumerator<self>] All instances of this enum, in declaration order
27
28
  def each(&block)
28
29
  to_a.each(&block)
29
30
  end
30
31
 
31
- # Iterates over the set of enum instances
32
- # @yield [self, Integer] Each instance of this enum, in declaration order,
33
- # with its ordinal index
34
- # @return [Array<self>] All instances of this enum, in declaration order
35
- def each_with_index(&block)
36
- to_a.each_with_index(&block)
37
- end
38
-
39
- # Iterates over the set of enum instances
40
- # @yield [self] Each instance of this enum, in declaration order
41
- # @return [Array] An array containing the result of applying `&block`
42
- # to each instance of this enum, in instance declaration order
43
- def map(&block)
44
- to_a.map(&block)
45
- end
46
-
47
32
  # Looks up an enum instance based on its key
48
33
  # @param key [Symbol] the key to look up
49
34
  # @return [self, nil] the corresponding enum instance, or nil
@@ -71,6 +56,7 @@ module TypesafeEnum
71
56
  # @return [self, nil] the corresponding enum instance, or nil
72
57
  def find_by_ord(ord)
73
58
  return nil if ord > size || ord.negative?
59
+
74
60
  as_array[ord]
75
61
  end
76
62
 
@@ -93,21 +79,37 @@ module TypesafeEnum
93
79
  end
94
80
 
95
81
  def valid_key_and_value(instance)
82
+ return unless (key = valid_key(instance))
83
+
84
+ [key, valid_value(instance)]
85
+ end
86
+
87
+ def valid_key(instance)
96
88
  key = instance.key
89
+ return key unless (found = find_by_key(key))
90
+
97
91
  value = instance.value
98
- if (found = find_by_key(key))
99
- raise NameError, "#{name}::#{key} already exists" unless value == found.value
100
- warn("ignoring redeclaration of #{name}::#{key} with value #{value} (source: #{caller(5..5).first})")
101
- nil
102
- else
103
- raise NameError, "A #{name} instance with value '#{value}' already exists" if find_by_value(value)
104
- [key, value]
105
- end
92
+ raise NameError, "#{name}::#{key} already exists with value #{found.value.inspect}" unless value == found.value
93
+
94
+ warn("ignoring redeclaration of #{name}::#{key} with value #{value.inspect} (source: #{caller(6..6).first})")
95
+ end
96
+
97
+ def valid_value(instance)
98
+ value = instance.value
99
+ return value unless (found = find_by_value(value))
100
+
101
+ key = instance.key
102
+ raise NameError, "A #{name} instance with value #{value.inspect} already exists: #{found.key}" unless key == found.key
103
+
104
+ # valid_key() should already have warned us, and valid_key_and_value() should have exited early, but just in case
105
+ # :nocov:
106
+ warn("ignoring redeclaration of #{name}::#{key} with value #{value.inspect} (source: #{caller(6..6).first})")
107
+ # :nocov:
106
108
  end
107
109
 
108
110
  def register(instance)
109
111
  key, value = valid_key_and_value(instance)
110
- return unless key && value
112
+ return unless key
111
113
 
112
114
  const_set(key.to_s, instance)
113
115
  by_key[key] = instance
@@ -150,15 +152,19 @@ module TypesafeEnum
150
152
  end
151
153
 
152
154
  def to_s
153
- "#{self.class}::#{key} [#{ord}] -> #{value}"
155
+ "#{self.class}::#{key} [#{ord}] -> #{value.inspect}"
154
156
  end
155
157
 
156
158
  private
157
159
 
158
- def initialize(key, value = nil, &block)
160
+ IMPLICIT = Class.new.new
161
+ private_constant :IMPLICIT
162
+
163
+ def initialize(key, value = IMPLICIT, &block)
159
164
  raise TypeError, "#{key} is not a symbol" unless key.is_a?(Symbol)
165
+
160
166
  @key = key
161
- @value = value || key.to_s.downcase
167
+ @value = value == IMPLICIT ? key.to_s.downcase : value
162
168
  @ord = self.class.size
163
169
  self.class.class_exec(self) do |instance|
164
170
  register(instance)
@@ -5,8 +5,8 @@ module TypesafeEnum
5
5
  NAME = 'typesafe_enum'
6
6
 
7
7
  # The version of this gem
8
- VERSION = '0.1.8'
8
+ VERSION = '0.3.0'
9
9
 
10
10
  # The copyright notice for this gem
11
- COPYRIGHT = 'Copyright (c) 2017 The Regents of the University of California'
11
+ COPYRIGHT = 'Copyright (c) 2020 The Regents of the University of California'
12
12
  end
@@ -1,16 +1,19 @@
1
1
  inherit_from: ../.rubocop.yml
2
2
 
3
- Metrics/BlockLength:
3
+ Lint/ConstantDefinitionInBlock:
4
4
  Enabled: false
5
5
 
6
- Metrics/MethodLength:
6
+ Style/ClassAndModuleChildren:
7
7
  Enabled: false
8
8
 
9
- Metrics/ModuleLength:
9
+ Metrics/AbcSize:
10
10
  Enabled: false
11
11
 
12
- Style/ClassAndModuleChildren:
12
+ Metrics/BlockLength:
13
13
  Enabled: false
14
14
 
15
- Security/MarshalLoad:
15
+ Metrics/ModuleLength:
16
+ Enabled: false
17
+
18
+ Metrics/MethodLength:
16
19
  Enabled: false
@@ -1,4 +1,3 @@
1
-
2
1
  # frozen_string_literal: true
3
2
 
4
3
  require 'spec_helper'
@@ -30,15 +29,31 @@ class Scale < TypesafeEnum::Base
30
29
  new :MEGA, 1_000_000
31
30
  end
32
31
 
32
+ class Scheme < TypesafeEnum::Base
33
+ new :HTTP, 'http'
34
+ new :HTTPS, 'https'
35
+ new :EXAMPLE, 'example'
36
+ new :UNKNOWN, nil
37
+ end
38
+
33
39
  module TypesafeEnum
34
40
  describe Base do
35
41
 
36
- describe '::new' do
37
- it ' news a constant enum value' do
42
+ describe :new do
43
+ it 'news a constant enum value' do
38
44
  enum = Suit::CLUBS
39
45
  expect(enum).to be_a(Suit)
40
46
  end
41
47
 
48
+ it 'allows nil values' do
49
+ expect do
50
+ class ::NilValues < Base
51
+ new :NONE, nil
52
+ end
53
+ end.not_to raise_error
54
+ expect(::NilValues.to_a).not_to be_empty
55
+ end
56
+
42
57
  it 'insists symbols be symbols' do
43
58
  expect do
44
59
  class ::StringKeys < Base
@@ -81,6 +96,18 @@ module TypesafeEnum
81
96
  expect(::DuplicateValues.find_by_key(:ALSO_SPADES)).to be_nil
82
97
  end
83
98
 
99
+ it 'disallows duplicate nil values' do
100
+ expect do
101
+ class ::DuplicateNilValues < Base
102
+ new :NONE, nil
103
+ new :NULL, nil
104
+ end.to raise_error(NameError)
105
+ expect(::DuplicateNilValues.to_a).to eq([::DuplicateNilValues::NONE])
106
+ expect(::DuplicateNilValues::NONE.value).to be_nil
107
+ expect(::DuplicateNilValues.find_by_key(:NULL)).to be_nil
108
+ end
109
+ end
110
+
84
111
  it 'disallows nil keys' do
85
112
  expect do
86
113
  class ::NilKeys < Base
@@ -94,7 +121,8 @@ module TypesafeEnum
94
121
  class ::IdenticalInstances < Base
95
122
  new :SPADES, 'spades'
96
123
  end
97
- expect(::IdenticalInstances).to receive(:warn).with(a_string_matching(/ignoring redeclaration of IdenticalInstances::SPADES with value spades/))
124
+ expected_msg = /ignoring redeclaration of IdenticalInstances::SPADES with value "spades" \(source: .*base_spec.rb:[0-9]+:in `new'\)/
125
+ expect(::IdenticalInstances).to receive(:warn).once.with(expected_msg)
98
126
  class ::IdenticalInstances < Base
99
127
  new :SPADES, 'spades'
100
128
  end
@@ -113,7 +141,7 @@ module TypesafeEnum
113
141
  end
114
142
  end
115
143
 
116
- describe '::to_a' do
144
+ describe :to_a do
117
145
  it 'returns the values as an array' do
118
146
  expect(Suit.to_a).to eq([Suit::CLUBS, Suit::DIAMONDS, Suit::HEARTS, Suit::SPADES])
119
147
  end
@@ -125,13 +153,27 @@ module TypesafeEnum
125
153
  end
126
154
  end
127
155
 
128
- describe '::size' do
156
+ describe :size do
129
157
  it 'returns the number of enum instnaces' do
130
158
  expect(Suit.size).to eq(4)
131
159
  end
132
160
  end
133
161
 
134
- describe '::each' do
162
+ describe :count do
163
+ it 'returns the number of enum instnaces' do
164
+ expect(Suit.count).to eq(4)
165
+ end
166
+
167
+ it 'counts items' do
168
+ expect(Suit.count(Suit::SPADES)).to eq(1)
169
+ end
170
+
171
+ it 'counts items that match a predicate' do
172
+ expect(Suit.count { |s| s.value.length == 6 }).to eq(2)
173
+ end
174
+ end
175
+
176
+ describe :each do
135
177
  it 'iterates the enum values' do
136
178
  expected = [Suit::CLUBS, Suit::DIAMONDS, Suit::HEARTS, Suit::SPADES]
137
179
  index = 0
@@ -142,7 +184,7 @@ module TypesafeEnum
142
184
  end
143
185
  end
144
186
 
145
- describe '::each_with_index' do
187
+ describe :each_with_index do
146
188
  it 'iterates the enum values with indices' do
147
189
  expected = [Suit::CLUBS, Suit::DIAMONDS, Suit::HEARTS, Suit::SPADES]
148
190
  Suit.each_with_index do |s, index|
@@ -151,14 +193,21 @@ module TypesafeEnum
151
193
  end
152
194
  end
153
195
 
154
- describe '::map' do
196
+ describe :map do
155
197
  it 'maps enum values' do
156
198
  all_keys = Suit.map(&:key)
157
199
  expect(all_keys).to eq(%i[CLUBS DIAMONDS HEARTS SPADES])
158
200
  end
159
201
  end
160
202
 
161
- describe '#<=>' do
203
+ describe :flat_map do
204
+ it 'flatmaps enum values' do
205
+ result = Tarot.flat_map { |t| [t.key, t.value] }
206
+ expect(result).to eq([:CUPS, 'Cups', :COINS, 'Coins', :WANDS, 'Wands', :SWORDS, 'Swords'])
207
+ end
208
+ end
209
+
210
+ describe :<=> do
162
211
  it 'orders enum instances' do
163
212
  Suit.each_with_index do |s1, i1|
164
213
  Suit.each_with_index do |s2, i2|
@@ -168,7 +217,7 @@ module TypesafeEnum
168
217
  end
169
218
  end
170
219
 
171
- describe '#==' do
220
+ describe :== do
172
221
  it 'returns true for identical instances, false otherwise' do
173
222
  Suit.each do |s1|
174
223
  Suit.each do |s2|
@@ -190,6 +239,7 @@ module TypesafeEnum
190
239
  end
191
240
  end
192
241
 
242
+ # rubocop:disable Security/MarshalLoad
193
243
  it 'survives marshalling' do
194
244
  Suit.each do |s1|
195
245
  dump = Marshal.dump(s1)
@@ -198,9 +248,10 @@ module TypesafeEnum
198
248
  expect(s2 == s1).to eq(true)
199
249
  end
200
250
  end
251
+ # rubocop:enable Security/MarshalLoad
201
252
  end
202
253
 
203
- describe '#!=' do
254
+ describe :!= do
204
255
  it 'returns false for identical instances, true otherwise' do
205
256
  Suit.each do |s1|
206
257
  Suit.each do |s2|
@@ -222,7 +273,7 @@ module TypesafeEnum
222
273
  end
223
274
  end
224
275
 
225
- describe '#hash' do
276
+ describe :hash do
226
277
  it 'gives consistent values' do
227
278
  Suit.each do |s1|
228
279
  Suit.each do |s2|
@@ -245,6 +296,7 @@ module TypesafeEnum
245
296
  end
246
297
  end
247
298
 
299
+ # rubocop:disable Security/MarshalLoad
248
300
  it 'survives marshalling' do
249
301
  Suit.each do |s1|
250
302
  dump = Marshal.dump(s1)
@@ -252,6 +304,7 @@ module TypesafeEnum
252
304
  expect(s2.hash).to eq(s1.hash)
253
305
  end
254
306
  end
307
+ # rubocop:enable Security/MarshalLoad
255
308
 
256
309
  it 'always returns a Fixnum' do
257
310
  Suit.each do |s1|
@@ -260,7 +313,7 @@ module TypesafeEnum
260
313
  end
261
314
  end
262
315
 
263
- describe '#eql?' do
316
+ describe :eql? do
264
317
  it 'is consistent with #hash' do
265
318
  Suit.each do |s1|
266
319
  Suit.each do |s2|
@@ -270,16 +323,30 @@ module TypesafeEnum
270
323
  end
271
324
  end
272
325
 
273
- describe '#value' do
326
+ describe :value do
274
327
  it 'returns the string value of the enum instance' do
275
328
  expected = %w[clubs diamonds hearts spades]
276
329
  Suit.each_with_index do |s, index|
277
330
  expect(s.value).to eq(expected[index])
278
331
  end
279
332
  end
333
+
334
+ it 'returns an explicit value' do
335
+ expected = [10, 100, 1000, 1_000_000]
336
+ Scale.each_with_index do |s, index|
337
+ expect(s.value).to eq(expected[index])
338
+ end
339
+ end
340
+
341
+ it 'supports nil values' do
342
+ expected = ['http', 'https', 'example', nil]
343
+ Scheme.each_with_index do |s, index|
344
+ expect(s.value).to eq(expected[index])
345
+ end
346
+ end
280
347
  end
281
348
 
282
- describe '#key' do
349
+ describe :key do
283
350
  it 'returns the symbol key of the enum instance' do
284
351
  expected = %i[CLUBS DIAMONDS HEARTS SPADES]
285
352
  Suit.each_with_index do |s, index|
@@ -288,7 +355,7 @@ module TypesafeEnum
288
355
  end
289
356
  end
290
357
 
291
- describe '#ord' do
358
+ describe :ord do
292
359
  it 'returns the ord value of the enum instance' do
293
360
  Suit.each_with_index do |s, index|
294
361
  expect(s.ord).to eq(index)
@@ -296,13 +363,13 @@ module TypesafeEnum
296
363
  end
297
364
  end
298
365
 
299
- describe '#to_s' do
366
+ describe :to_s do
300
367
  it 'provides an informative string' do
301
368
  aggregate_failures 'informative string' do
302
- [Suit, Tarot, RGBColor, Scale].each do |ec|
369
+ [Suit, Tarot, RGBColor, Scale, Scheme].each do |ec|
303
370
  ec.each do |ev|
304
371
  result = ev.to_s
305
- [ec.to_s, ev.key, ev.ord, ev.value].each do |info|
372
+ [ec.to_s, ev.key, ev.ord, ev.value.inspect].each do |info|
306
373
  expect(result).to include(info.to_s)
307
374
  end
308
375
  end
@@ -311,7 +378,7 @@ module TypesafeEnum
311
378
  end
312
379
  end
313
380
 
314
- describe '::find_by_key' do
381
+ describe :find_by_key do
315
382
  it 'maps symbol keys to enum instances' do
316
383
  keys = %i[CLUBS DIAMONDS HEARTS SPADES]
317
384
  expected = Suit.to_a
@@ -325,7 +392,7 @@ module TypesafeEnum
325
392
  end
326
393
  end
327
394
 
328
- describe '::find_by_value' do
395
+ describe :find_by_value do
329
396
  it 'maps values to enum instances' do
330
397
  values = %w[clubs diamonds hearts spades]
331
398
  expected = Suit.to_a
@@ -349,9 +416,15 @@ module TypesafeEnum
349
416
  expect(Scale.find_by_value(s.value)).to be(s)
350
417
  end
351
418
  end
419
+
420
+ it 'supports enums with explicit nil values' do
421
+ Scheme.each do |s|
422
+ expect(Scheme.find_by_value(s.value)).to be(s)
423
+ end
424
+ end
352
425
  end
353
426
 
354
- describe '::find_by_value_str' do
427
+ describe :find_by_value_str do
355
428
  it 'maps string values to enum instances' do
356
429
  values = %w[clubs diamonds hearts spades]
357
430
  expected = Suit.to_a
@@ -375,9 +448,15 @@ module TypesafeEnum
375
448
  expect(Scale.find_by_value_str(s.value.to_s)).to be(s)
376
449
  end
377
450
  end
451
+
452
+ it 'supports enums with explicit nil values' do
453
+ Scheme.each do |s|
454
+ expect(Scheme.find_by_value(s.value)).to be(s)
455
+ end
456
+ end
378
457
  end
379
458
 
380
- describe '::find_by_ord' do
459
+ describe :find_by_ord do
381
460
  it 'maps ordinal indices to enum instances' do
382
461
  Suit.each do |s|
383
462
  expect(Suit.find_by_ord(s.ord)).to be(s)
@@ -432,5 +511,9 @@ module TypesafeEnum
432
511
 
433
512
  expect(Operation.map { |op| op.eval(39, 23) }).to eq([39 + 23, 39 - 23])
434
513
  end
514
+
515
+ it 'is an Enumerable' do
516
+ expect(Suit).to be_an(Enumerable)
517
+ end
435
518
  end
436
519
  end