functional-light-service 0.5.4 → 6.0.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.
- checksums.yaml +4 -4
- data/.github/workflows/project-build.yml +35 -11
- data/.rubocop.yml +101 -160
- data/AUDIT-functional-light-service.md +352 -0
- data/CHANGELOG.md +38 -0
- data/README.md +54 -2
- data/audit/bench.rb +99 -0
- data/audit/verify_findings.rb +172 -0
- data/functional-light-service.gemspec +15 -21
- data/lib/functional-light-service/action.rb +97 -101
- data/lib/functional-light-service/configuration.rb +26 -24
- data/lib/functional-light-service/context/key_verifier.rb +124 -118
- data/lib/functional-light-service/context.rb +63 -20
- data/lib/functional-light-service/deprecations.rb +26 -0
- data/lib/functional-light-service/errors.rb +8 -6
- data/lib/functional-light-service/functional/enum.rb +286 -250
- data/lib/functional-light-service/functional/maybe.rb +21 -15
- data/lib/functional-light-service/functional/monad.rb +77 -66
- data/lib/functional-light-service/functional/null.rb +88 -74
- data/lib/functional-light-service/functional/option.rb +100 -97
- data/lib/functional-light-service/functional/result.rb +129 -116
- data/lib/functional-light-service/localization_adapter.rb +48 -47
- data/lib/functional-light-service/organizer/execute.rb +16 -14
- data/lib/functional-light-service/organizer/iterate.rb +30 -25
- data/lib/functional-light-service/organizer/reduce_if.rb +19 -17
- data/lib/functional-light-service/organizer/reduce_until.rb +22 -20
- data/lib/functional-light-service/organizer/scoped_reducable.rb +15 -13
- data/lib/functional-light-service/organizer/with_callback.rb +28 -26
- data/lib/functional-light-service/organizer/with_reducer.rb +81 -77
- data/lib/functional-light-service/organizer/with_reducer_factory.rb +20 -18
- data/lib/functional-light-service/organizer/with_reducer_log_decorator.rb +110 -108
- data/lib/functional-light-service/organizer.rb +114 -114
- data/lib/functional-light-service/testing/context_factory.rb +48 -42
- data/lib/functional-light-service/testing.rb +3 -1
- data/lib/functional-light-service/version.rb +5 -3
- data/lib/functional-light-service.rb +30 -28
- data/spec/acceptance/after_actions_spec.rb +87 -71
- data/spec/acceptance/before_actions_spec.rb +115 -98
- data/spec/acceptance/custom_log_from_organizer_spec.rb +61 -60
- data/spec/acceptance/deprecation_warnings_spec.rb +82 -0
- data/spec/acceptance/fail_spec.rb +52 -50
- data/spec/acceptance/message_localization_spec.rb +119 -118
- data/spec/acceptance/organizer/context_failure_and_skipping_spec.rb +68 -65
- data/spec/acceptance/organizer/reduce_if_spec.rb +89 -89
- data/spec/acceptance/organizer/with_callback_spec.rb +113 -110
- data/spec/acceptance/{not_having_call_method_warning_spec.rb → organizer_entry_point_spec.rb} +10 -7
- data/spec/acceptance/rollback_spec.rb +183 -132
- data/spec/action_expects_and_promises_spec.rb +97 -93
- data/spec/action_promised_keys_spec.rb +126 -122
- data/spec/context_spec.rb +289 -197
- data/spec/examples/controller_spec.rb +63 -63
- data/spec/examples/validate_address_spec.rb +38 -37
- data/spec/lib/deterministic/currify_spec.rb +90 -88
- data/spec/lib/deterministic/null_spec.rb +6 -1
- data/spec/lib/deterministic/option_spec.rb +140 -137
- data/spec/lib/deterministic/result/result_map_spec.rb +155 -154
- data/spec/lib/deterministic/result/result_shared.rb +3 -2
- data/spec/lib/deterministic/result_spec.rb +2 -2
- data/spec/lib/edge_cases_spec.rb +156 -0
- data/spec/lib/enum_spec.rb +1 -1
- data/spec/lib/native_pattern_matching_spec.rb +74 -0
- data/spec/organizer_spec.rb +115 -114
- data/spec/readme_spec.rb +45 -47
- data/spec/sample/calculates_order_tax_action_spec.rb +16 -16
- data/spec/sample/calculates_tax_spec.rb +1 -1
- data/spec/sample/looks_up_tax_percentage_action_spec.rb +55 -55
- data/spec/sample/tax/calculates_order_tax_action.rb +10 -9
- data/spec/sample/tax/looks_up_tax_percentage_action.rb +28 -27
- data/spec/sample/tax/provides_free_shipping_action.rb +11 -10
- data/spec/spec_helper.rb +6 -0
- data/spec/test_doubles.rb +628 -599
- data/spec/testing/context_factory_spec.rb +21 -0
- metadata +45 -161
- data/lib/functional-light-service/organizer/verify_call_method_exists.rb +0 -29
- data/spec/acceptance/include_warning_spec.rb +0 -29
|
@@ -1,154 +1,155 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
describe FunctionalLightService::Result do
|
|
4
|
-
include FunctionalLightService::Prelude::Result
|
|
5
|
-
|
|
6
|
-
context ">> (map)" do
|
|
7
|
-
specify { expect(Success(0).map { |n| Success(n + 1) }).to eq Success(1) }
|
|
8
|
-
specify { expect(Failure(0).map { |n| Success(n + 1) }).to eq Failure(0) }
|
|
9
|
-
|
|
10
|
-
it "Failure stops execution" do
|
|
11
|
-
class ChainUnderTest
|
|
12
|
-
include FunctionalLightService::Prelude::Result
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
m(:
|
|
19
|
-
m(:
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
.map(&:
|
|
84
|
-
.map(&:
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
# #
|
|
108
|
-
# #
|
|
109
|
-
# #
|
|
110
|
-
# #
|
|
111
|
-
# #
|
|
112
|
-
# #
|
|
113
|
-
# #
|
|
114
|
-
# #
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
expect(test
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
expect(
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
end
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe FunctionalLightService::Result do
|
|
4
|
+
include FunctionalLightService::Prelude::Result
|
|
5
|
+
|
|
6
|
+
context ">> (map)" do
|
|
7
|
+
specify { expect(Success(0).map { |n| Success(n + 1) }).to eq Success(1) }
|
|
8
|
+
specify { expect(Failure(0).map { |n| Success(n + 1) }).to eq Failure(0) }
|
|
9
|
+
|
|
10
|
+
it "Failure stops execution" do
|
|
11
|
+
class ChainUnderTest
|
|
12
|
+
include FunctionalLightService::Prelude::Result
|
|
13
|
+
|
|
14
|
+
alias :m :method
|
|
15
|
+
|
|
16
|
+
def call
|
|
17
|
+
init >>
|
|
18
|
+
m(:validate) >>
|
|
19
|
+
m(:send) >>
|
|
20
|
+
m(:parse)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def init
|
|
24
|
+
Success(:step => 1)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def validate(i)
|
|
28
|
+
i[:step] = i[:step] + 1
|
|
29
|
+
Success(i)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def send(i)
|
|
33
|
+
i[:step] = i[:step] + 1
|
|
34
|
+
Failure("Error @ #{i.fetch(:step)}")
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def parse(i)
|
|
38
|
+
i[:step] = i[:step] + 1
|
|
39
|
+
Success(i)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
test = ChainUnderTest.new
|
|
44
|
+
|
|
45
|
+
expect(test.call).to eq Failure("Error @ 3")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "expects an Result" do
|
|
49
|
+
def returns_non_result(_i)
|
|
50
|
+
2
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
expect { Success(1) >> method(:returns_non_result) }.to \
|
|
54
|
+
raise_error(FunctionalLightService::Monad::NotMonadError)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it "works with a block" do
|
|
58
|
+
expect(
|
|
59
|
+
Success(1).map { |i| Success(i + 1) }
|
|
60
|
+
).to eq Success(2)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "works with a lambda" do
|
|
64
|
+
expect(
|
|
65
|
+
Success(1) >> ->(i) { Success(i + 1) }
|
|
66
|
+
).to eq Success(2)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it "does not catch exceptions" do
|
|
70
|
+
expect do
|
|
71
|
+
Success(1) >> ->(_i) { raise "error" }
|
|
72
|
+
end.to raise_error(RuntimeError)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
context "using self as the context for success" do
|
|
77
|
+
class SelfContextUnderTest
|
|
78
|
+
include FunctionalLightService::Prelude::Result
|
|
79
|
+
|
|
80
|
+
def call
|
|
81
|
+
@step = 0
|
|
82
|
+
Success(self)
|
|
83
|
+
.map(&:validate)
|
|
84
|
+
.map(&:build)
|
|
85
|
+
.map(&:send)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def validate
|
|
89
|
+
@step = 1
|
|
90
|
+
Success(self)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def build
|
|
94
|
+
@step = 2
|
|
95
|
+
Success(self)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def send
|
|
99
|
+
@step = 3
|
|
100
|
+
Success(self)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def inspect
|
|
104
|
+
"Step #{@step}"
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# # def self.procify(*meths)
|
|
108
|
+
# # meths.each do |m|
|
|
109
|
+
# # new_m = "__#{m}__procified".to_sym
|
|
110
|
+
# # alias new_m m
|
|
111
|
+
# # define_method new_m do |ctx|
|
|
112
|
+
# # method(m)
|
|
113
|
+
# # end
|
|
114
|
+
# # end
|
|
115
|
+
# # end
|
|
116
|
+
|
|
117
|
+
# procify :send
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
it "works" do
|
|
121
|
+
test = SelfContextUnderTest.new.call
|
|
122
|
+
expect(test).to be_a described_class::Success
|
|
123
|
+
expect(test.inspect).to eq "Success(Step 3)"
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
context "<< (pipe)" do
|
|
128
|
+
it "ignores the output of pipe" do
|
|
129
|
+
acc = "ctx: "
|
|
130
|
+
log = ->(ctx) { acc += ctx.inspect }
|
|
131
|
+
|
|
132
|
+
actual = Success(1).pipe(log).map { Success(2) }
|
|
133
|
+
expect(actual).to eq Success(2)
|
|
134
|
+
expect(acc).to eq "ctx: Success(1)"
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
it "works with <<" do
|
|
138
|
+
log = ->(n) { n.value + 1 }
|
|
139
|
+
foo = ->(n) { Success(n + 1) }
|
|
140
|
+
|
|
141
|
+
Success(1) << log >> foo
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
context ">= (try)" do
|
|
146
|
+
it "try (>=) catches errors and wraps them as Failure" do
|
|
147
|
+
def error(ctx)
|
|
148
|
+
raise "error #{ctx}"
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
actual = Success(1) >= method(:error)
|
|
152
|
+
expect(actual.inspect).to eq "Failure(#<RuntimeError: error 1>)"
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
@@ -13,12 +13,13 @@ shared_examples 'Result' do
|
|
|
13
13
|
|
|
14
14
|
it "#to_s" do
|
|
15
15
|
expect(result.new(1).to_s).to eq "1"
|
|
16
|
-
|
|
16
|
+
# il formato di Hash#to_s/inspect cambia tra Ruby 3.3 e 3.4: costruiamo l'atteso dinamicamente
|
|
17
|
+
expect(result.new(:a => 1).to_s).to eq({ :a => 1 }.to_s)
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
it "#inspect" do
|
|
20
21
|
expect(result.new(1).inspect).to eq "#{result_name}(1)"
|
|
21
|
-
expect(result.new(:a => 1).inspect).to eq "#{result_name}({:a=>1})"
|
|
22
|
+
expect(result.new(:a => 1).inspect).to eq "#{result_name}(#{{ :a => 1 }.inspect})"
|
|
22
23
|
end
|
|
23
24
|
end
|
|
24
25
|
|
|
@@ -4,8 +4,8 @@ describe FunctionalLightService::Result do
|
|
|
4
4
|
include FunctionalLightService::Prelude::Result
|
|
5
5
|
|
|
6
6
|
it "can't call Result#new directly" do
|
|
7
|
-
|
|
8
|
-
expect { described_class.new(1) }.to raise_error(NoMethodError,
|
|
7
|
+
# il formato del messaggio NoMethodError cambia tra le versioni di Ruby
|
|
8
|
+
expect { described_class.new(1) }.to raise_error(NoMethodError, /private method [`']new'/)
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
it "fmap" do
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'functional-light-service/functional/maybe'
|
|
3
|
+
|
|
4
|
+
# Copertura dei path d'errore e delle API minori emersi dall'audit
|
|
5
|
+
describe "edge cases and error paths" do
|
|
6
|
+
include FunctionalLightService::Prelude::Result
|
|
7
|
+
include FunctionalLightService::Prelude::Option
|
|
8
|
+
|
|
9
|
+
describe FunctionalLightService::Context do
|
|
10
|
+
it "#add_to_context merges values" do
|
|
11
|
+
ctx = FunctionalLightService::Context.make(:a => 1)
|
|
12
|
+
ctx.add_to_context(:b => 2)
|
|
13
|
+
|
|
14
|
+
expect(ctx[:b]).to eq(2)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
describe FunctionalLightService::Context::KeyVerifier do
|
|
19
|
+
it "the base verifier requires throw_error_predicate to be overridden" do
|
|
20
|
+
ctx = FunctionalLightService::Context.make
|
|
21
|
+
verifier = described_class.new(ctx, nil)
|
|
22
|
+
|
|
23
|
+
expect { verifier.throw_error_predicate([]) }.to raise_error(NotImplementedError)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
describe "Object monkey patch (opt-in via functional/maybe)" do
|
|
28
|
+
it "defines null? and some? on every object" do
|
|
29
|
+
expect("anything".null?).to be(false)
|
|
30
|
+
expect("anything".some?).to be(true)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
describe Null do
|
|
35
|
+
it "class-level unknown messages return the singleton" do
|
|
36
|
+
expect(Null.anything_at_all).to eq(Null.instance)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "class-level respond_to? is permissive" do
|
|
40
|
+
expect(Null.respond_to?(:anything_at_all)).to be(true)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "compares equal to another Null" do
|
|
44
|
+
expect(Null.instance == Null.instance).to be(true) # rubocop:disable Lint/BinaryOperatorWithIdenticalOperands
|
|
45
|
+
expect(Null.instance == "not null").to be(false)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "a mimic only responds to the mimicked interface" do
|
|
49
|
+
klass = Class.new { def foo; end }
|
|
50
|
+
mimic = Null.mimic(klass)
|
|
51
|
+
|
|
52
|
+
expect(mimic.respond_to?(:foo)).to be(true)
|
|
53
|
+
expect(mimic.respond_to?(:bar)).to be(false)
|
|
54
|
+
expect { mimic.bar }.to raise_error(NoMethodError)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
describe "enum error paths" do
|
|
59
|
+
it "raises when a variant is defined twice" do
|
|
60
|
+
expect do
|
|
61
|
+
FunctionalLightService.enum do
|
|
62
|
+
Dup()
|
|
63
|
+
Dup()
|
|
64
|
+
end
|
|
65
|
+
end.to raise_error(ArgumentError, /already defined/)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it "the enum builder responds to any variant name" do
|
|
69
|
+
builder = FunctionalLightService::EnumBuilder.new(Class.new)
|
|
70
|
+
|
|
71
|
+
expect(builder.respond_to?(:AnyVariantName)).to be(true)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it "raises MatchError when the match is not exhaustive" do
|
|
75
|
+
expect do
|
|
76
|
+
Some(1).match do
|
|
77
|
+
Some() { |s| s }
|
|
78
|
+
end
|
|
79
|
+
end.to raise_error(FunctionalLightService::Enum::MatchError, /non-exhaustive/)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it "raises MatchError when the block arity does not match the variant" do
|
|
83
|
+
expect do
|
|
84
|
+
Some(1).match do
|
|
85
|
+
Some() { |a, b| [a, b] }
|
|
86
|
+
None() { nil }
|
|
87
|
+
end
|
|
88
|
+
end.to raise_error(FunctionalLightService::Enum::MatchError, /must match/)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "raises MatchError when no guard matches" do
|
|
92
|
+
expect do
|
|
93
|
+
Some(1).match do
|
|
94
|
+
Some(where { s > 100 }) { |s| s }
|
|
95
|
+
None() { nil }
|
|
96
|
+
end
|
|
97
|
+
end.to raise_error(FunctionalLightService::Enum::MatchError, /No match could be made/)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it "rejects unnamed/rest block parameters" do
|
|
101
|
+
# su Ruby 3.1 |*| appare come [[:rest]] (ramo "Unnamed param"),
|
|
102
|
+
# da Ruby 3.2 come [[:rest, :*]] (ramo "Only :req & :opt")
|
|
103
|
+
expect do
|
|
104
|
+
Some(1).match do
|
|
105
|
+
Some() { |*| nil }
|
|
106
|
+
None() { nil }
|
|
107
|
+
end
|
|
108
|
+
end.to raise_error(ArgumentError, /Unnamed param|Only :req & :opt/)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
it "rejects keyword block parameters" do
|
|
112
|
+
expect do
|
|
113
|
+
Some(1).match do
|
|
114
|
+
Some() { |**opts| opts }
|
|
115
|
+
None() { nil }
|
|
116
|
+
end
|
|
117
|
+
end.to raise_error(ArgumentError, /Only :req & :opt params allowed/)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
it "None deconstructs to an empty hash and array" do
|
|
121
|
+
none = FunctionalLightService::Option::None.new
|
|
122
|
+
|
|
123
|
+
expect(none.deconstruct).to eq([])
|
|
124
|
+
expect(none.deconstruct_keys(nil)).to eq({})
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
describe "Option minor APIs" do
|
|
129
|
+
it "#value_to_a returns the raw value" do
|
|
130
|
+
expect(Some(1).value_to_a).to eq(1)
|
|
131
|
+
expect(FunctionalLightService::Option::None.new.value_to_a).to be_nil
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
it "Prelude None() builds the shared None" do
|
|
135
|
+
expect(None().none?).to be(true)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it "Prelude Option() returns the Option enum" do
|
|
139
|
+
expect(Option()).to eq(FunctionalLightService::Option)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
describe "Result minor APIs" do
|
|
144
|
+
it "#+ raises NotMonadError for non-Result operands" do
|
|
145
|
+
expect { Success(1) + "not a result" } # rubocop:disable Style/StringConcatenation
|
|
146
|
+
.to raise_error(FunctionalLightService::Monad::NotMonadError)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
it "Prelude try! wraps exceptions in Failure" do
|
|
150
|
+
result = try! { raise "boom" }
|
|
151
|
+
|
|
152
|
+
expect(result).to be_failure
|
|
153
|
+
expect(result.value).to be_a(RuntimeError)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
data/spec/lib/enum_spec.rb
CHANGED
|
@@ -20,7 +20,7 @@ describe FunctionalLightService::Enum do
|
|
|
20
20
|
|
|
21
21
|
it "can't instantiate parent" do
|
|
22
22
|
expect { MyEnym.new }.to \
|
|
23
|
-
raise_error NoMethodError,
|
|
23
|
+
raise_error NoMethodError, /private method [`']new'/
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
it "Nullary" do
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe "native Ruby pattern matching (case/in) support" do
|
|
4
|
+
include FunctionalLightService::Prelude::Result
|
|
5
|
+
include FunctionalLightService::Prelude::Option
|
|
6
|
+
|
|
7
|
+
Result = FunctionalLightService::Result
|
|
8
|
+
Option = FunctionalLightService::Option
|
|
9
|
+
|
|
10
|
+
it "deconstructs Success/Failure positionally" do
|
|
11
|
+
matched =
|
|
12
|
+
case Success(42)
|
|
13
|
+
in Result::Success[value] then "success: #{value}"
|
|
14
|
+
in Result::Failure[error] then "failure: #{error}"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
expect(matched).to eq("success: 42")
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "deconstructs Failure positionally" do
|
|
21
|
+
matched =
|
|
22
|
+
case Failure(:boom)
|
|
23
|
+
in Result::Success[value] then "success: #{value}"
|
|
24
|
+
in Result::Failure[error] then "failure: #{error}"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
expect(matched).to eq("failure: boom")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "deconstructs by keys using the variant's field names" do
|
|
31
|
+
matched =
|
|
32
|
+
case Success(42)
|
|
33
|
+
in Result::Success(s:) then s
|
|
34
|
+
in Result::Failure(f:) then f
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
expect(matched).to eq(42)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "deconstructs Some and None" do
|
|
41
|
+
some_result =
|
|
42
|
+
case Some(7)
|
|
43
|
+
in Option::Some[v] then v
|
|
44
|
+
in Option::None then :none
|
|
45
|
+
end
|
|
46
|
+
none_result =
|
|
47
|
+
case None()
|
|
48
|
+
in Option::Some[v] then v
|
|
49
|
+
in Option::None then :none
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
expect(some_result).to eq(7)
|
|
53
|
+
expect(none_result).to eq(:none)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
Shape = FunctionalLightService.enum do
|
|
57
|
+
Point(:x, :y)
|
|
58
|
+
Origin()
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "deconstructs multi-field (Binary) variants by keys" do
|
|
62
|
+
matched =
|
|
63
|
+
case Shape::Point.new(1, 2)
|
|
64
|
+
in Shape::Point(x:, y:) then [x, y]
|
|
65
|
+
in Shape::Origin then [0, 0]
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
expect(matched).to eq([1, 2])
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def None # rubocop:disable Naming/MethodName
|
|
72
|
+
FunctionalLightService::Option::None.new
|
|
73
|
+
end
|
|
74
|
+
end
|