Skip to content

Commit 12bd8d4

Browse files
committed
refactor: new way creating typed value
rename methods update rubocop config
1 parent def2b4c commit 12bd8d4

3 files changed

Lines changed: 115 additions & 95 deletions

File tree

.rubocop.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,12 @@ RSpec/ExampleLength:
1818

1919
RSpec/MissingExpectationTargetMethod:
2020
Enabled: false
21+
22+
Style/RaiseArgs:
23+
EnforcedStyle: compact
24+
25+
Lint/MissingSuper:
26+
Enabled: false
27+
28+
Naming/PredicatePrefix:
29+
Enabled: false

lib/rs/option.rb

Lines changed: 66 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -5,112 +5,111 @@ module Rs
55
# https://doc.rust-lang.org/std/option/index.html
66
# https://doc.rust-lang.org/std/option/enum.Option.html
77
# https://doc.rust-lang.org/src/core/option.rs.html
8-
module Option
9-
class ArgumentError < StandardError; end
8+
class Option
9+
class TypeError < StandardError
10+
def initialize(type)
11+
super("Expected #{type} to be a Class")
12+
end
13+
end
14+
15+
class TypeNilClass < StandardError; end
16+
class TypeMismatch < StandardError; end
17+
class WrapNil < StandardError; end
1018
class UnwrapNone < StandardError; end
11-
class TypeError < StandardError; end
1219

13-
def some?
14-
@some
20+
def is_some
21+
is_a?(Some)
1522
end
1623

17-
def some_and
18-
if !@some
19-
false
20-
elsif block_given?
21-
yield(@value)
22-
else
23-
@some
24-
end
24+
def is_some_and(&block)
25+
is_some ? block.call(@value) : false
2526
end
2627

27-
def none?
28-
!@some
28+
def is_none
29+
is_a?(None)
2930
end
3031

31-
def none_or
32-
if !@some
33-
true
34-
elsif block_given?
35-
yield(@value)
36-
else
37-
!@some
38-
end
32+
def is_none_or(&block)
33+
is_none ? true : block.call(@value)
3934
end
4035

4136
def expect(msg)
42-
@some ? @value : raise(UnwrapNone, msg)
37+
is_some ? @value : raise(UnwrapNone.new(msg))
4338
end
4439

4540
def unwrap
46-
@some ? @value : raise(UnwrapNone)
41+
is_some ? @value : raise(UnwrapNone.new("Called unwrap on None"))
4742
end
4843

49-
def unwrap_or(default = nil)
50-
if @some
51-
@value
52-
elsif block_given?
53-
yield
54-
else
55-
default
56-
end
44+
def unwrap_or(default)
45+
is_some ? @value : default
5746
end
5847

5948
def unwrap_or_else(&block)
60-
@some ? @value : block.call
49+
is_some ? @value : block.call
6150
end
6251
end
6352
end
6453

65-
class Some
66-
include Rs::Option
67-
68-
attr_reader :type
69-
attr_reader :some
70-
attr_reader :value
71-
72-
private :value
73-
74-
def initialize(value, type: nil)
75-
if value == nil
76-
raise(Rs::Option::ArgumentError, "Some value cannot be nil")
54+
class Some < Rs::Option
55+
def self.[](type, &block)
56+
if !type.is_a?(Class)
57+
raise TypeError.new(type)
7758
end
7859

79-
if type
80-
if value.class != type
81-
raise(Rs::Option::TypeError, "Value type #{value.class} does not match #{type}")
82-
end
60+
if type == NilClass
61+
raise TypeNilClass.new("Cannot create Some[T] with NilClass")
62+
end
8363

84-
@type = type
85-
else
86-
@type = value.class
64+
value = block.call
65+
if !value.is_a?(type)
66+
raise TypeMismatch.new("Expected 'Some[#{type}] { #{value.inspect} }' block.call to be #{type}, not #{value.class}")
8767
end
8868

89-
@some = true
90-
@value = value
69+
new(value)
70+
end
71+
72+
def inspect
73+
"Some[#{@value.class}] { #{@value.inspect} }"
9174
end
9275

9376
def ==(other)
94-
other.is_a?(Some) && @type == other.type && @value == other.unwrap
77+
other.is_a?(Some) && @value == other.unwrap && @value.class == other.unwrap.class
9578
end
96-
end
9779

98-
class None
99-
include Rs::Option
80+
def initialize(value)
81+
if value == nil
82+
raise WrapNil.new("Cannot create Some[T](value) with nil")
83+
end
84+
85+
@value = value
86+
end
87+
end
10088

89+
class None < Rs::Option
10190
attr_reader :type
102-
attr_reader :some
103-
attr_reader :value
10491

105-
private :value
92+
def self.[](type)
93+
new(type)
94+
end
10695

107-
def initialize
108-
@type = NilClass
109-
@some = false
110-
@value = nil
96+
def inspect
97+
"None[#{@type}]"
11198
end
11299

113100
def ==(other)
114-
other.is_a? None
101+
other.is_a?(None) && @type == other.type
102+
end
103+
104+
def initialize(type = Class)
105+
if !type.is_a?(Class)
106+
raise TypeError.new(type)
107+
end
108+
109+
if type == NilClass
110+
raise TypeNilClass.new("Cannot create None[T] with NilClass")
111+
end
112+
113+
@type = type
115114
end
116115
end

spec/rs/option_spec.rb

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,53 +4,71 @@
44
# https://doc.rust-lang.org/std/option/enum.Option.html
55

66
it "new" do
7-
expect { Some.new(nil) }.to raise_error Rs::Option::ArgumentError, "Some value cannot be nil"
7+
expect { Some[nil] {} }.to raise_error Rs::Option::TypeError
8+
expect { Some[NilClass] { nil } }.to raise_error Rs::Option::TypeNilClass
9+
expect { Some[Integer] { nil } }.to raise_error Rs::Option::TypeMismatch
10+
expect { Some.new(nil) }.to raise_error Rs::Option::WrapNil
11+
12+
expect { None[nil] }.to raise_error Rs::Option::TypeError
13+
expect { None[NilClass] }.to raise_error Rs::Option::TypeNilClass
14+
expect { None.new(nil) }.to raise_error Rs::Option::TypeError
15+
expect { None.new(NilClass) }.to raise_error Rs::Option::TypeNilClass
16+
17+
x = Some.new(2)
18+
y = Some[Integer] { 2 }
19+
expect(x).to eq y
20+
21+
x = None.new(Integer)
22+
y = None[Integer]
23+
expect(x).to eq y
24+
25+
x = None.new
26+
y = None[Class]
27+
expect(x).to eq y
828
end
929

10-
it "some?" do
30+
it "is_some" do
1131
x = Some.new(2)
12-
expect(x.some?).to be true
32+
expect(x.is_some).to be true
1333

1434
x = None.new
15-
expect(x.some?).to be false
35+
expect(x.is_some).to be false
1636
end
1737

18-
it "some_and proc" do
38+
it "is_some_and proc" do
1939
x = Some.new(2)
20-
expect(x.some_and).to be true
21-
expect(x.some_and { |x| x > 1 }).to be true
40+
expect(x.is_some_and { |x| x > 1 }).to be true
2241

2342
x = Some.new(0)
24-
expect(x.some_and { |x| x > 1 }).to be false
43+
expect(x.is_some_and { |x| x > 1 }).to be false
2544

2645
x = None.new
27-
expect(x.some_and { |x| x.to_i > 1 }).to be false
46+
expect(x.is_some_and { |x| x.to_i > 1 }).to be false
2847

2948
x = Some.new("str")
30-
expect(x.some_and { |x| x.size > 1 }).to be true
49+
expect(x.is_some_and { |x| x.size > 1 }).to be true
3150
end
3251

33-
it "none?" do
52+
it "is_none" do
3453
x = Some.new(2)
35-
expect(x.none?).to be false
54+
expect(x.is_none).to be false
3655

3756
x = None.new
38-
expect(x.none?).to be true
57+
expect(x.is_none).to be true
3958
end
4059

41-
it "none_or proc" do
60+
it "is_none_or proc" do
4261
x = Some.new(2)
43-
expect(x.none_or).to be false
44-
expect(x.none_or { |x| x > 1 }).to be true
62+
expect(x.is_none_or { |x| x > 1 }).to be true
4563

4664
x = Some.new(0)
47-
expect(x.none_or { |x| x > 1 }).to be false
65+
expect(x.is_none_or { |x| x > 1 }).to be false
4866

4967
x = None.new
50-
expect(x.none_or { |x| x.to_i > 1 }).to be true
68+
expect(x.is_none_or { |x| x.to_i > 1 }).to be true
5169

5270
x = Some.new("str")
53-
expect(x.none_or { |x| x.size > 1 }).to be true
71+
expect(x.is_none_or { |x| x.size > 1 }).to be true
5472
end
5573

5674
it "unwrap" do
@@ -66,12 +84,6 @@
6684
expect(None.new.unwrap_or("bike")).to eq "bike"
6785
end
6886

69-
it "unwrap_or proc" do
70-
k = 10
71-
expect(Some.new(4).unwrap_or { 2 * k }).to eq 4
72-
expect(None.new.unwrap_or { 2 * k }).to eq 20
73-
end
74-
7587
it "unwrap_or_else proc" do
7688
k = 10
7789
expect(Some.new(4).unwrap_or_else { 2 * k }).to eq 4
@@ -82,8 +94,8 @@
8294
x = None.new
8395
y = Some.new(12)
8496

85-
expect(x.unwrap_or).to be_nil
86-
expect(y.unwrap_or).to eq 12
97+
expect(x.unwrap_or(0)).to eq 0
98+
expect(y.unwrap_or(0)).to eq 12
8799
end
88100

89101
it "pattern_matching" do
@@ -108,7 +120,7 @@ def divide(numerator, denominator)
108120
it "type ==" do
109121
a = Some.new(1)
110122
b = Some.new(1.0)
111-
c = Some.new(1.0, type: Float)
123+
c = Some[Float] { 1.0 }
112124
d = Some.new("a")
113125
e = None.new
114126

0 commit comments

Comments
 (0)