typesafe_enum 0.1.8 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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