immutable-ruby 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/immutable/core_ext/struct.rb +9 -0
- data/lib/immutable/enumerable.rb +9 -0
- data/lib/immutable/hash.rb +104 -4
- data/lib/immutable/list.rb +13 -13
- data/lib/immutable/nested.rb +3 -0
- data/lib/immutable/vector.rb +21 -11
- data/lib/immutable/version.rb +1 -1
- data/spec/lib/immutable/hash/dig_spec.rb +34 -0
- data/spec/lib/immutable/hash/fetch_values_spec.rb +22 -0
- data/spec/lib/immutable/hash/put_spec.rb +9 -0
- data/spec/lib/immutable/hash/subset_spec.rb +42 -0
- data/spec/lib/immutable/hash/superset_spec.rb +42 -0
- data/spec/lib/immutable/hash/to_proc_spec.rb +39 -0
- data/spec/lib/immutable/hash/values_at_spec.rb +26 -6
- data/spec/lib/immutable/list/all_spec.rb +1 -1
- data/spec/lib/immutable/list/any_spec.rb +1 -1
- data/spec/lib/immutable/list/at_spec.rb +1 -1
- data/spec/lib/immutable/list/construction_spec.rb +1 -1
- data/spec/lib/immutable/list/count_spec.rb +1 -1
- data/spec/lib/immutable/list/each_slice_spec.rb +1 -1
- data/spec/lib/immutable/list/each_spec.rb +1 -1
- data/spec/lib/immutable/list/empty_spec.rb +1 -1
- data/spec/lib/immutable/list/eql_spec.rb +1 -1
- data/spec/lib/immutable/list/find_index_spec.rb +1 -1
- data/spec/lib/immutable/list/find_spec.rb +1 -1
- data/spec/lib/immutable/list/group_by_spec.rb +1 -1
- data/spec/lib/immutable/list/hash_spec.rb +1 -1
- data/spec/lib/immutable/list/include_spec.rb +1 -1
- data/spec/lib/immutable/list/index_spec.rb +6 -2
- data/spec/lib/immutable/list/indices_spec.rb +1 -1
- data/spec/lib/immutable/list/inspect_spec.rb +1 -1
- data/spec/lib/immutable/list/join_spec.rb +1 -1
- data/spec/lib/immutable/list/last_spec.rb +1 -1
- data/spec/lib/immutable/list/maximum_spec.rb +1 -1
- data/spec/lib/immutable/list/minimum_spec.rb +1 -1
- data/spec/lib/immutable/list/multithreading_spec.rb +4 -4
- data/spec/lib/immutable/list/none_spec.rb +1 -1
- data/spec/lib/immutable/list/one_spec.rb +1 -1
- data/spec/lib/immutable/list/product_spec.rb +1 -1
- data/spec/lib/immutable/list/reduce_spec.rb +1 -1
- data/spec/lib/immutable/list/reverse_spec.rb +1 -1
- data/spec/lib/immutable/list/size_spec.rb +1 -1
- data/spec/lib/immutable/list/sum_spec.rb +1 -1
- data/spec/lib/immutable/list/tail_spec.rb +1 -1
- data/spec/lib/immutable/list/to_a_spec.rb +1 -1
- data/spec/lib/immutable/list/to_ary_spec.rb +1 -1
- data/spec/lib/immutable/nested/construction_spec.rb +11 -5
- data/spec/lib/immutable/set/add_spec.rb +4 -2
- data/spec/lib/immutable/set/grep_spec.rb +10 -10
- data/spec/lib/immutable/set/grep_v_spec.rb +59 -0
- data/spec/lib/immutable/vector/dig_spec.rb +30 -0
- data/spec/spec_helper.rb +4 -0
- metadata +323 -306
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a963e4f0565bc516534d1cf81f0206357b252f3a
|
4
|
+
data.tar.gz: 38a237bd2bdcef74cb66971375e1414ea36c67bd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '040888fc5816c86d1df3333bd1e7e922096c7775e196f5bf3c9e9dedc599dc55b888cf31c1b602bcc4bfda8c06e28b436a793d4e221fca54855e69b0b2d2e523'
|
7
|
+
data.tar.gz: fb6bffbec4a70ccc00058140a563007cbf7617c55af03328c2a8385772caaf4d6e0d22e9e10664541ec04ad3f1dfe43f5b3b327268539841d6edb9c7683af1dc
|
data/lib/immutable/enumerable.rb
CHANGED
@@ -30,6 +30,15 @@ module Immutable
|
|
30
30
|
result
|
31
31
|
end
|
32
32
|
|
33
|
+
# Search the collection for elements which are not `#===` to `item`. Yield
|
34
|
+
# them to the optional code block if provided, and return them as a new
|
35
|
+
# collection.
|
36
|
+
def grep_v(pattern, &block)
|
37
|
+
result = select { |item| !(pattern === item) }
|
38
|
+
result = result.map(&block) if block_given?
|
39
|
+
result
|
40
|
+
end
|
41
|
+
|
33
42
|
# Yield all integers from 0 up to, but not including, the number of items in
|
34
43
|
# this collection. For collections which provide indexed access, these are all
|
35
44
|
# the valid, non-negative indices into the collection.
|
data/lib/immutable/hash.rb
CHANGED
@@ -264,6 +264,12 @@ module Immutable
|
|
264
264
|
end
|
265
265
|
end
|
266
266
|
|
267
|
+
# @private
|
268
|
+
# @raise NoMethodError
|
269
|
+
def []=(*)
|
270
|
+
raise NoMethodError, "Immutable::Hash doesn't support `[]='; use `put' instead"
|
271
|
+
end
|
272
|
+
|
267
273
|
# Return a new `Hash` with a deeply nested value modified to the result of
|
268
274
|
# the given code block. When traversing the nested `Hash`es and `Vector`s,
|
269
275
|
# non-existing keys are created with empty `Hash` values.
|
@@ -570,20 +576,54 @@ module Immutable
|
|
570
576
|
end
|
571
577
|
|
572
578
|
# Return a {Vector} of the values which correspond to the `wanted` keys.
|
573
|
-
# If any of the `wanted` keys are not present in this `Hash`,
|
579
|
+
# If any of the `wanted` keys are not present in this `Hash`, `nil` will be
|
580
|
+
# placed instead, or the result of the default proc (if one is defined),
|
581
|
+
# similar to the behavior of {#get}.
|
574
582
|
#
|
575
583
|
# @example
|
576
584
|
# h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
|
577
|
-
# h.values_at("B", "A", "D") # => Immutable::Vector[2, 1]
|
585
|
+
# h.values_at("B", "A", "D") # => Immutable::Vector[2, 1, nil]
|
578
586
|
#
|
579
587
|
# @param wanted [Array] The keys to retrieve
|
580
588
|
# @return [Vector]
|
581
589
|
def values_at(*wanted)
|
582
|
-
|
583
|
-
|
590
|
+
Vector.new(wanted.map { |key| get(key) }.freeze)
|
591
|
+
end
|
592
|
+
|
593
|
+
# Return a {Vector} of the values which correspond to the `wanted` keys.
|
594
|
+
# If any of the `wanted` keys are not present in this `Hash`, raise `KeyError`
|
595
|
+
# exception.
|
596
|
+
#
|
597
|
+
# @example
|
598
|
+
# h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
|
599
|
+
# h.fetch_values("C", "A") # => Immutable::Vector[3, 1]
|
600
|
+
# h.fetch_values("C", "Z") # => KeyError: key not found: "Z"
|
601
|
+
#
|
602
|
+
# @param wanted [Array] The keys to retrieve
|
603
|
+
# @return [Vector]
|
604
|
+
def fetch_values(*wanted)
|
605
|
+
array = wanted.map { |key| fetch(key) }
|
584
606
|
Vector.new(array.freeze)
|
585
607
|
end
|
586
608
|
|
609
|
+
# Return the value of successively indexing into a nested collection.
|
610
|
+
# If any of the keys is not present, return `nil`.
|
611
|
+
#
|
612
|
+
# @example
|
613
|
+
# h = Immutable::Hash[a: 9, b: Immutable::Hash[c: 'a', d: 4], e: nil]
|
614
|
+
# h.dig(:b, :c) # => "a"
|
615
|
+
# h.dig(:b, :f) # => nil
|
616
|
+
#
|
617
|
+
# @return [Object]
|
618
|
+
def dig(key, *rest)
|
619
|
+
value = self[key]
|
620
|
+
if rest.empty? || value.nil?
|
621
|
+
value
|
622
|
+
else
|
623
|
+
value.dig(*rest)
|
624
|
+
end
|
625
|
+
end
|
626
|
+
|
587
627
|
# Return a new {Set} containing the keys from this `Hash`.
|
588
628
|
#
|
589
629
|
# @example
|
@@ -734,6 +774,50 @@ module Immutable
|
|
734
774
|
self.eql?(other) || (other.respond_to?(:to_hash) && to_hash.eql?(other.to_hash))
|
735
775
|
end
|
736
776
|
|
777
|
+
# Return true if this `Hash` is a proper superset of `other`, which means
|
778
|
+
# all `other`'s keys are contained in this `Hash` with identical
|
779
|
+
# values, and the two hashes are not identical.
|
780
|
+
#
|
781
|
+
# @param other [Immutable::Hash] The object to compare with
|
782
|
+
# @return [Boolean]
|
783
|
+
def >(other)
|
784
|
+
self != other && self >= other
|
785
|
+
end
|
786
|
+
|
787
|
+
# Return true if this `Hash` is a superset of `other`, which means all
|
788
|
+
# `other`'s keys are contained in this `Hash` with identical values.
|
789
|
+
#
|
790
|
+
# @param other [Immutable::Hash] The object to compare with
|
791
|
+
# @return [Boolean]
|
792
|
+
def >=(other)
|
793
|
+
other.each do |key, value|
|
794
|
+
if self[key] != value
|
795
|
+
return false
|
796
|
+
end
|
797
|
+
end
|
798
|
+
true
|
799
|
+
end
|
800
|
+
|
801
|
+
# Return true if this `Hash` is a proper subset of `other`, which means all
|
802
|
+
# its keys are contained in `other` with the identical values, and the two
|
803
|
+
# hashes are not identical.
|
804
|
+
#
|
805
|
+
# @param other [Immutable::Hash] The object to compare with
|
806
|
+
# @return [Boolean]
|
807
|
+
def <(other)
|
808
|
+
other > self
|
809
|
+
end
|
810
|
+
|
811
|
+
# Return true if this `Hash` is a subset of `other`, which means all its
|
812
|
+
# keys are contained in `other` with the identical values, and the two
|
813
|
+
# hashes are not identical.
|
814
|
+
#
|
815
|
+
# @param other [Immutable::Hash] The object to compare with
|
816
|
+
# @return [Boolean]
|
817
|
+
def <=(other)
|
818
|
+
other >= self
|
819
|
+
end
|
820
|
+
|
737
821
|
# See `Object#hash`.
|
738
822
|
# @return [Integer]
|
739
823
|
def hash
|
@@ -801,6 +885,22 @@ module Immutable
|
|
801
885
|
end
|
802
886
|
alias :to_h :to_hash
|
803
887
|
|
888
|
+
# Return a `Proc` which accepts a key as an argument and returns the value.
|
889
|
+
# The `Proc` behaves like {#get} (when the key is missing, it returns nil or
|
890
|
+
# the result of the default proc).
|
891
|
+
#
|
892
|
+
# @example
|
893
|
+
# h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
|
894
|
+
# h.to_proc.call("B")
|
895
|
+
# # => 2
|
896
|
+
# ["A", "C", "X"].map(&h) # The & is short for .to_proc in Ruby
|
897
|
+
# # => [1, 3, nil]
|
898
|
+
#
|
899
|
+
# @return [Proc]
|
900
|
+
def to_proc
|
901
|
+
lambda { |key| get(key) }
|
902
|
+
end
|
903
|
+
|
804
904
|
# @return [::Hash]
|
805
905
|
# @private
|
806
906
|
def marshal_dump
|
data/lib/immutable/list.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require "thread"
|
2
2
|
require "set"
|
3
|
-
require "concurrent
|
3
|
+
require "concurrent"
|
4
4
|
|
5
5
|
require "immutable/undefined"
|
6
6
|
require "immutable/enumerable"
|
@@ -1311,23 +1311,23 @@ module Immutable
|
|
1311
1311
|
def initialize(&block)
|
1312
1312
|
@head = block # doubles as storage for block while yet unrealized
|
1313
1313
|
@tail = nil
|
1314
|
-
@atomic = Concurrent::
|
1314
|
+
@atomic = Concurrent::Atom.new(0) # haven't yet run block
|
1315
1315
|
@size = nil
|
1316
1316
|
end
|
1317
1317
|
|
1318
1318
|
def head
|
1319
|
-
realize if @atomic.
|
1319
|
+
realize if @atomic.value != 2
|
1320
1320
|
@head
|
1321
1321
|
end
|
1322
1322
|
alias :first :head
|
1323
1323
|
|
1324
1324
|
def tail
|
1325
|
-
realize if @atomic.
|
1325
|
+
realize if @atomic.value != 2
|
1326
1326
|
@tail
|
1327
1327
|
end
|
1328
1328
|
|
1329
1329
|
def empty?
|
1330
|
-
realize if @atomic.
|
1330
|
+
realize if @atomic.value != 2
|
1331
1331
|
@size == 0
|
1332
1332
|
end
|
1333
1333
|
|
@@ -1348,7 +1348,7 @@ module Immutable
|
|
1348
1348
|
def realize
|
1349
1349
|
while true
|
1350
1350
|
# try to "claim" the right to run the block which realizes target
|
1351
|
-
if @atomic.
|
1351
|
+
if @atomic.compare_and_set(0,1) # full memory barrier here
|
1352
1352
|
begin
|
1353
1353
|
list = @head.call
|
1354
1354
|
if list.empty?
|
@@ -1357,22 +1357,22 @@ module Immutable
|
|
1357
1357
|
@head, @tail = list.head, list.tail
|
1358
1358
|
end
|
1359
1359
|
rescue
|
1360
|
-
@atomic.
|
1360
|
+
@atomic.reset(0)
|
1361
1361
|
MUTEX.synchronize { QUEUE.broadcast }
|
1362
1362
|
raise
|
1363
1363
|
end
|
1364
|
-
@atomic.
|
1364
|
+
@atomic.reset(2)
|
1365
1365
|
MUTEX.synchronize { QUEUE.broadcast }
|
1366
1366
|
return
|
1367
1367
|
end
|
1368
1368
|
# we failed to "claim" it, another thread must be running it
|
1369
|
-
if @atomic.
|
1369
|
+
if @atomic.value == 1 # another thread is running the block
|
1370
1370
|
MUTEX.synchronize do
|
1371
1371
|
# check value of @atomic again, in case another thread already changed it
|
1372
1372
|
# *and* went past the call to QUEUE.broadcast before we got here
|
1373
|
-
QUEUE.wait(MUTEX) if @atomic.
|
1373
|
+
QUEUE.wait(MUTEX) if @atomic.value == 1
|
1374
1374
|
end
|
1375
|
-
elsif @atomic.
|
1375
|
+
elsif @atomic.value == 2 # another thread finished the block
|
1376
1376
|
return
|
1377
1377
|
end
|
1378
1378
|
end
|
@@ -1591,5 +1591,5 @@ module Immutable
|
|
1591
1591
|
true
|
1592
1592
|
end
|
1593
1593
|
end
|
1594
|
-
end
|
1595
|
-
end
|
1594
|
+
end.freeze
|
1595
|
+
end
|
data/lib/immutable/nested.rb
CHANGED
@@ -5,6 +5,7 @@ require "immutable/vector"
|
|
5
5
|
require "immutable/sorted_set"
|
6
6
|
require "immutable/list"
|
7
7
|
require "immutable/deque"
|
8
|
+
require "immutable/core_ext/struct"
|
8
9
|
|
9
10
|
module Immutable
|
10
11
|
class << self
|
@@ -30,6 +31,8 @@ module Immutable
|
|
30
31
|
when ::Array
|
31
32
|
res = obj.map { |element| from(element) }
|
32
33
|
Immutable::Vector.new(res)
|
34
|
+
when ::Struct
|
35
|
+
from(obj.to_h)
|
33
36
|
when ::SortedSet
|
34
37
|
# This clause must go before ::Set clause, since ::SortedSet is a ::Set.
|
35
38
|
res = obj.map { |element| from(element) }
|
data/lib/immutable/vector.rb
CHANGED
@@ -279,6 +279,24 @@ module Immutable
|
|
279
279
|
end
|
280
280
|
end
|
281
281
|
|
282
|
+
# Return the value of successively indexing into a nested collection.
|
283
|
+
# If any of the keys is not present, return `nil`.
|
284
|
+
#
|
285
|
+
# @example
|
286
|
+
# v = Immutable::Vector[9, Immutable::Hash[c: 'a', d: 4]]
|
287
|
+
# v.dig(1, :c) # => "a"
|
288
|
+
# v.dig(1, :f) # => nil
|
289
|
+
#
|
290
|
+
# @return [Object]
|
291
|
+
def dig(key, *rest)
|
292
|
+
value = self[key]
|
293
|
+
if rest.empty? || value.nil?
|
294
|
+
value
|
295
|
+
else
|
296
|
+
value.dig(*rest)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
282
300
|
# Return specific objects from the `Vector`. All overloads return `nil` if
|
283
301
|
# the starting index is out of range.
|
284
302
|
#
|
@@ -536,17 +554,9 @@ module Immutable
|
|
536
554
|
# @return [Vector]
|
537
555
|
def uniq(&block)
|
538
556
|
array = self.to_a
|
539
|
-
if
|
540
|
-
|
541
|
-
|
542
|
-
elsif array.uniq!(&block) # returns nil if no changes were made
|
543
|
-
self.class.new(array.freeze)
|
544
|
-
else
|
545
|
-
self
|
546
|
-
end
|
547
|
-
elsif array.frozen?
|
548
|
-
self.class.new(array.uniq.freeze)
|
549
|
-
elsif array.uniq! # returns nil if no changes were made
|
557
|
+
if array.frozen?
|
558
|
+
self.class.new(array.uniq(&block).freeze)
|
559
|
+
elsif array.uniq!(&block) # returns nil if no changes were made
|
550
560
|
self.class.new(array.freeze)
|
551
561
|
else
|
552
562
|
self
|
data/lib/immutable/version.rb
CHANGED
@@ -0,0 +1,34 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "immutable/hash"
|
3
|
+
|
4
|
+
describe Immutable::Hash do
|
5
|
+
describe "#dig" do
|
6
|
+
let(:h) { H[:a => 9, :b => H[:c => 'a', :d => 4], :e => nil] }
|
7
|
+
it "returns the value with one argument to dig" do
|
8
|
+
expect(h.dig(:a)).to eq(9)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "returns the value in nested hashes" do
|
12
|
+
expect(h.dig(:b, :c)).to eq('a')
|
13
|
+
end
|
14
|
+
|
15
|
+
it "returns nil if the key is not present" do
|
16
|
+
expect(h.dig(:f, :foo)).to eq(nil)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "returns nil if you dig out the end of the hash" do
|
20
|
+
expect(h.dig(:f, :foo, :bar)).to eq(nil)
|
21
|
+
end
|
22
|
+
|
23
|
+
# This is a bit different from Ruby's Hash; it raises TypeError for
|
24
|
+
# objects which don't respond to #dig
|
25
|
+
it "raises a NoMethodError if a value does not support #dig" do
|
26
|
+
expect { h.dig(:a, :foo) }.to raise_error(NoMethodError)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "returns the correct value when there is a default proc" do
|
30
|
+
default_hash = H.new { |k, v| "#{k}-default" }
|
31
|
+
expect(default_hash.dig(:a)).to eq("a-default")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "immutable/hash"
|
3
|
+
|
4
|
+
describe Immutable::Hash do
|
5
|
+
describe "#fetch_values" do
|
6
|
+
context "when the all the requested keys exist" do
|
7
|
+
it "returns a vector of values for the given keys" do
|
8
|
+
h = H[:a => 9, :b => 'a', :c => -10, :d => nil]
|
9
|
+
h.fetch_values.should be_kind_of(Immutable::Vector)
|
10
|
+
h.fetch_values.should eql(V.empty)
|
11
|
+
h.fetch_values(:a, :d, :b).should be_kind_of(Immutable::Vector)
|
12
|
+
h.fetch_values(:a, :d, :b).should eql(V[9, nil, 'a'])
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context "when the key does not exist" do
|
17
|
+
it "raises a KeyError" do
|
18
|
+
-> { H["A" => "aye", "C" => "Cee"].fetch_values("A", "B") }.should raise_error(KeyError)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -1,6 +1,15 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
3
|
describe Immutable::Hash do
|
4
|
+
describe "#[]=" do
|
5
|
+
let(:hash) { H["A" => "aye", "B" => "bee", "C" => "see"] }
|
6
|
+
|
7
|
+
it 'raises error pointing to #put' do
|
8
|
+
expect { hash[:A] = 'aye' }
|
9
|
+
.to raise_error(NoMethodError, /Immutable::Hash.*`put'/)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
4
13
|
describe "#put" do
|
5
14
|
let(:hash) { H["A" => "aye", "B" => "bee", "C" => "see"] }
|
6
15
|
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "immutable/hash"
|
3
|
+
|
4
|
+
describe Immutable::Hash do
|
5
|
+
describe "#<=" do
|
6
|
+
[
|
7
|
+
[{}, {}, true],
|
8
|
+
[{"A" => 1}, {}, false],
|
9
|
+
[{}, {"A" => 1}, true],
|
10
|
+
[{"A" => 1}, {"A" => 1}, true],
|
11
|
+
[{"A" => 1}, {"A" => 2}, false],
|
12
|
+
[{"B" => 2}, {"A" => 1, "B" => 2, "C" => 3}, true],
|
13
|
+
[{"A" => 1, "B" => 2, "C" => 3}, {"B" => 2}, false],
|
14
|
+
[{"B" => 0}, {"A" => 1, "B" => 2, "C" => 3}, false],
|
15
|
+
].each do |a, b, expected|
|
16
|
+
describe "for #{a.inspect} and #{b.inspect}" do
|
17
|
+
it "returns #{expected}" do
|
18
|
+
expect(H[a] <= H[b]).to eq(expected)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#<" do
|
25
|
+
[
|
26
|
+
[{}, {}, false],
|
27
|
+
[{"A" => 1}, {}, false],
|
28
|
+
[{}, {"A" => 1}, true],
|
29
|
+
[{"A" => 1}, {"A" => 1}, false],
|
30
|
+
[{"A" => 1}, {"A" => 2}, false],
|
31
|
+
[{"B" => 2}, {"A" => 1, "B" => 2, "C" => 3}, true],
|
32
|
+
[{"A" => 1, "B" => 2, "C" => 3}, {"B" => 2}, false],
|
33
|
+
[{"B" => 0}, {"A" => 1, "B" => 2, "C" => 3}, false],
|
34
|
+
].each do |a, b, expected|
|
35
|
+
describe "for #{a.inspect} and #{b.inspect}" do
|
36
|
+
it "returns #{expected}" do
|
37
|
+
expect(H[a] < H[b]).to eq(expected)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|