fear 2.0.1 → 3.1.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/ci.yml +105 -0
- data/.simplecov +2 -2
- data/.standard.yml +1 -0
- data/CHANGELOG.md +8 -0
- data/Gemfile +14 -2
- data/Gemfile.lock +84 -78
- data/LICENSE.txt +1 -1
- data/README.md +18 -70
- data/Rakefile +55 -142
- data/benchmarks/dry_do_vs_fear_for.txt +7 -6
- data/benchmarks/dry_some_fmap_vs_fear_some_map.txt +7 -6
- data/benchmarks/factorial.txt +7 -9
- data/benchmarks/fear_gaurd_and1_vs_new.txt +7 -6
- data/benchmarks/fear_gaurd_and2_vs_and.txt +8 -7
- data/benchmarks/fear_gaurd_and3_vs_and_and.txt +7 -6
- data/benchmarks/fear_pattern_matching_construction_vs_execution.txt +7 -6
- data/benchmarks/pattern_matching_dry_vs_qo_vs_fear_try.txt +8 -10
- data/examples/pattern_extracting.rb +2 -2
- data/examples/pattern_matching_number_in_words.rb +12 -12
- data/fear.gemspec +5 -21
- data/lib/fear/either/left_projection.rb +237 -0
- data/lib/fear/either/pattern_match.rb +49 -0
- data/lib/fear/either.rb +21 -12
- data/lib/fear/either_api.rb +2 -4
- data/lib/fear/empty_partial_function.rb +3 -3
- data/lib/fear/failure/pattern_match.rb +14 -0
- data/lib/fear/failure.rb +5 -5
- data/lib/fear/for.rb +1 -1
- data/lib/fear/for_api.rb +1 -3
- data/lib/fear/future.rb +6 -6
- data/lib/fear/future_api.rb +0 -5
- data/lib/fear/left/pattern_match.rb +15 -0
- data/lib/fear/left.rb +4 -4
- data/lib/fear/none.rb +0 -87
- data/lib/fear/none_class/pattern_match.rb +16 -0
- data/lib/fear/none_class.rb +85 -0
- data/lib/fear/option/pattern_match.rb +47 -0
- data/lib/fear/option.rb +2 -6
- data/lib/fear/option_api.rb +1 -3
- data/lib/fear/partial_function/and_then.rb +2 -2
- data/lib/fear/partial_function/combined.rb +3 -3
- data/lib/fear/partial_function/empty.rb +3 -5
- data/lib/fear/partial_function/guard.rb +0 -4
- data/lib/fear/partial_function/or_else.rb +2 -4
- data/lib/fear/partial_function.rb +0 -9
- data/lib/fear/partial_function_class.rb +2 -2
- data/lib/fear/pattern_match.rb +5 -4
- data/lib/fear/pattern_matching_api.rb +1 -4
- data/lib/fear/right/pattern_match.rb +15 -0
- data/lib/fear/right.rb +4 -4
- data/lib/fear/right_biased.rb +2 -0
- data/lib/fear/some/pattern_match.rb +15 -0
- data/lib/fear/some.rb +3 -3
- data/lib/fear/success/pattern_match.rb +16 -0
- data/lib/fear/success.rb +5 -5
- data/lib/fear/try/pattern_match.rb +29 -0
- data/lib/fear/try.rb +3 -7
- data/lib/fear/try_api.rb +1 -3
- data/lib/fear/utils.rb +1 -1
- data/lib/fear/version.rb +1 -1
- data/lib/fear.rb +3 -14
- data/spec/fear/awaitable_spec.rb +0 -2
- data/spec/fear/either/left_projection_spec.rb +289 -0
- data/spec/fear/{either_pattern_match_spec.rb → either/pattern_match_spec.rb} +7 -7
- data/spec/fear/either_spec.rb +1 -1
- data/spec/fear/left_spec.rb +1 -1
- data/spec/fear/{option_pattern_match_spec.rb → option/pattern_match_spec.rb} +6 -6
- data/spec/fear/option_spec.rb +2 -2
- data/spec/fear/partial_function/any_spec.rb +3 -3
- data/spec/fear/partial_function/empty_spec.rb +2 -2
- data/spec/fear/partial_function_and_then_spec.rb +5 -5
- data/spec/fear/partial_function_composition_spec.rb +6 -6
- data/spec/fear/partial_function_or_else_spec.rb +13 -13
- data/spec/fear/partial_function_spec.rb +7 -7
- data/spec/fear/pattern_match_spec.rb +5 -5
- data/spec/fear/right_spec.rb +1 -1
- data/spec/fear/{try_pattern_match_spec.rb → try/try_pattern_match_spec.rb} +7 -7
- data/spec/fear/try_api_spec.rb +2 -2
- data/spec/fear/utils_spec.rb +3 -3
- data/spec/support/.keep +0 -0
- metadata +27 -296
- data/.github/workflows/rubocop.yml +0 -39
- data/.github/workflows/spec.yml +0 -43
- data/.rubocop.yml +0 -7
- data/benchmarks/fear_pattern_extracting_with_vs_without_cache.txt +0 -11
- data/benchmarks/pattern_matching_qo_vs_fear_pattern_extraction.txt +0 -11
- data/benchmarks/pattern_matching_qo_vs_fear_try_execution.txt +0 -11
- data/lib/dry/types/fear/option.rb +0 -125
- data/lib/dry/types/fear.rb +0 -8
- data/lib/fear/either_pattern_match.rb +0 -52
- data/lib/fear/failure_pattern_match.rb +0 -12
- data/lib/fear/left_pattern_match.rb +0 -11
- data/lib/fear/none_pattern_match.rb +0 -14
- data/lib/fear/option_pattern_match.rb +0 -50
- data/lib/fear/right_pattern_match.rb +0 -13
- data/lib/fear/some_pattern_match.rb +0 -13
- data/lib/fear/struct.rb +0 -237
- data/lib/fear/success_pattern_match.rb +0 -14
- data/lib/fear/try_pattern_match.rb +0 -32
- data/spec/dry/types/fear/option/constrained_spec.rb +0 -22
- data/spec/dry/types/fear/option/core_spec.rb +0 -77
- data/spec/dry/types/fear/option/default_spec.rb +0 -21
- data/spec/dry/types/fear/option/hash_spec.rb +0 -58
- data/spec/dry/types/fear/option/option_spec.rb +0 -97
- data/spec/struct_pattern_matching_spec.rb +0 -36
- data/spec/struct_spec.rb +0 -194
- data/spec/support/dry_types.rb +0 -6
@@ -1,21 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "support/dry_types"
|
4
|
-
|
5
|
-
RSpec.describe Dry::Types::Nominal, "#default", :option do
|
6
|
-
context "with a maybe" do
|
7
|
-
subject(:type) { Dry::Types["strict.integer"].option }
|
8
|
-
|
9
|
-
it_behaves_like "Dry::Types::Nominal without primitive" do
|
10
|
-
let(:type) { Dry::Types["strict.integer"].option.default(0) }
|
11
|
-
end
|
12
|
-
|
13
|
-
it "does not allow nil" do
|
14
|
-
expect { type.default(nil) }.to raise_error(ArgumentError, /nil/)
|
15
|
-
end
|
16
|
-
|
17
|
-
it "accepts a non-nil value" do
|
18
|
-
expect(type.default(0)[0]).to be_some_of(0)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
@@ -1,58 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "support/dry_types"
|
4
|
-
|
5
|
-
RSpec.describe Dry::Types::Hash, :option do
|
6
|
-
let(:email) { Dry::Types["option.strict.string"] }
|
7
|
-
|
8
|
-
context "Symbolized constructor" do
|
9
|
-
subject(:hash) do
|
10
|
-
Dry::Types["nominal.hash"].schema(
|
11
|
-
name: "string",
|
12
|
-
email: email,
|
13
|
-
).with_key_transform(&:to_sym)
|
14
|
-
end
|
15
|
-
|
16
|
-
describe "#[]" do
|
17
|
-
it "sets None as a default value for option" do
|
18
|
-
result = hash["name" => "Jane"]
|
19
|
-
|
20
|
-
expect(result[:email]).to be_none
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
context "Schema constructor" do
|
26
|
-
subject(:hash) do
|
27
|
-
Dry::Types["nominal.hash"].schema(
|
28
|
-
name: "string",
|
29
|
-
email: email,
|
30
|
-
)
|
31
|
-
end
|
32
|
-
|
33
|
-
describe "#[]" do
|
34
|
-
it "sets None as a default value for option types" do
|
35
|
-
result = hash[name: "Jane"]
|
36
|
-
|
37
|
-
expect(result[:email]).to be_none
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
context "Strict with defaults" do
|
43
|
-
subject(:hash) do
|
44
|
-
Dry::Types["nominal.hash"].schema(
|
45
|
-
name: "string",
|
46
|
-
email: email,
|
47
|
-
)
|
48
|
-
end
|
49
|
-
|
50
|
-
describe "#[]" do
|
51
|
-
it "sets None as a default value for option types" do
|
52
|
-
result = hash[name: "Jane"]
|
53
|
-
|
54
|
-
expect(result[:email]).to be_none
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
@@ -1,97 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "support/dry_types"
|
4
|
-
|
5
|
-
RSpec.describe Dry::Types::Nominal, "#option", :option do
|
6
|
-
context "with a nominal" do
|
7
|
-
subject(:type) { Dry::Types["nominal.string"].option }
|
8
|
-
|
9
|
-
it_behaves_like "Dry::Types::Nominal without primitive"
|
10
|
-
|
11
|
-
it "returns None when value is nil" do
|
12
|
-
expect(type[nil]).to be_none
|
13
|
-
end
|
14
|
-
|
15
|
-
it "returns Some when value exists" do
|
16
|
-
expect(type["hello"]).to be_some_of("hello")
|
17
|
-
end
|
18
|
-
|
19
|
-
it "returns original if input is already a option" do
|
20
|
-
expect(type[Fear.some("hello")]).to be_some_of("hello")
|
21
|
-
end
|
22
|
-
|
23
|
-
it "aliases #[] as #call" do
|
24
|
-
expect(type.("hello")).to be_some_of("hello")
|
25
|
-
end
|
26
|
-
|
27
|
-
it "does not have primitive" do
|
28
|
-
expect(type).to_not respond_to(:primitive)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
context "with a strict type" do
|
33
|
-
subject(:type) { Dry::Types["strict.integer"].option }
|
34
|
-
|
35
|
-
it_behaves_like "Dry::Types::Nominal without primitive"
|
36
|
-
|
37
|
-
it "returns None when value is nil" do
|
38
|
-
expect(type[nil]).to be_none
|
39
|
-
end
|
40
|
-
|
41
|
-
it "returns Some when value exists" do
|
42
|
-
expect(type[231]).to be_some_of(231)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
context "with a sum" do
|
47
|
-
subject(:type) { Dry::Types["nominal.bool"].option }
|
48
|
-
|
49
|
-
it_behaves_like "Dry::Types::Nominal without primitive"
|
50
|
-
|
51
|
-
it "returns None when value is nil" do
|
52
|
-
expect(type[nil]).to be_none
|
53
|
-
end
|
54
|
-
|
55
|
-
it "returns Some when value exists" do
|
56
|
-
expect(type[true]).to be_some_of(true)
|
57
|
-
expect(type[false]).to be_some_of(false)
|
58
|
-
end
|
59
|
-
|
60
|
-
it "does not have primitive" do
|
61
|
-
expect(type).to_not respond_to(:primitive)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
context "with keys" do
|
66
|
-
subject(:type) do
|
67
|
-
Dry::Types["hash"].schema(foo: Dry::Types["integer"]).key(:foo)
|
68
|
-
end
|
69
|
-
|
70
|
-
it "gets wrapped by key type" do
|
71
|
-
expect(type.option).to be_a(Dry::Types::Schema::Key)
|
72
|
-
expect(type.option[nil]).to be_none
|
73
|
-
expect(type.option[1]).to be_some_of(1)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
describe "#try" do
|
78
|
-
subject(:type) { Dry::Types["coercible.integer"].option }
|
79
|
-
|
80
|
-
it "maps successful result" do
|
81
|
-
expect(type.try("1")).to eq(Dry::Types::Result::Success.new(Fear.some(1)))
|
82
|
-
expect(type.try(nil)).to eq(Dry::Types::Result::Success.new(Fear.none))
|
83
|
-
expect(type.try("a")).to be_a(Dry::Types::Result::Failure)
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
describe "#call" do
|
88
|
-
describe "safe calls" do
|
89
|
-
subject(:type) { Dry::Types["coercible.integer"].option }
|
90
|
-
|
91
|
-
specify do
|
92
|
-
expect(type.("a") { :fallback }).to be(:fallback)
|
93
|
-
expect(type.(Fear.some(1)) { :fallback }).to eq(Fear.some(1))
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
@@ -1,36 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe Fear::Struct do
|
4
|
-
describe "pattern matching" do
|
5
|
-
subject do
|
6
|
-
case struct
|
7
|
-
in Fear::Struct(a: 42)
|
8
|
-
"a = 42"
|
9
|
-
in Fear::Struct(a: 43, **rest)
|
10
|
-
"a = 43, #{rest}"
|
11
|
-
in Fear::Struct(a:)
|
12
|
-
"a = #{a}"
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
let(:struct_class) { described_class.with_attributes(:a, :b) }
|
17
|
-
|
18
|
-
context "when match single value" do
|
19
|
-
let(:struct) { struct_class.new(b: 43, a: 42) }
|
20
|
-
|
21
|
-
it { is_expected.to eq("a = 42") }
|
22
|
-
end
|
23
|
-
|
24
|
-
context "when match single value and capture the rest" do
|
25
|
-
let(:struct) { struct_class.new(b: 42, a: 43) }
|
26
|
-
|
27
|
-
it { is_expected.to eq("a = 43, {:b=>42}") }
|
28
|
-
end
|
29
|
-
|
30
|
-
context "when capture a value" do
|
31
|
-
let(:struct) { struct_class.new(b: 45, a: 44) }
|
32
|
-
|
33
|
-
it { is_expected.to eq("a = 44") }
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
data/spec/struct_spec.rb
DELETED
@@ -1,194 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe Fear::Struct do
|
4
|
-
describe ".with_attributes" do
|
5
|
-
context "same arguments" do
|
6
|
-
subject { struct_class.new(a: 42, b: 43) }
|
7
|
-
|
8
|
-
let(:struct_class) { described_class.with_attributes(:a, :b) }
|
9
|
-
|
10
|
-
it { is_expected.to have_attributes(a: 42, b: 43) }
|
11
|
-
end
|
12
|
-
|
13
|
-
context "string arguments" do
|
14
|
-
subject { -> { struct_class.new("a" => 42, "b" => 43) } }
|
15
|
-
|
16
|
-
let(:struct_class) { described_class.with_attributes(:a, :b) }
|
17
|
-
|
18
|
-
it { is_expected.to raise_error(ArgumentError) }
|
19
|
-
end
|
20
|
-
|
21
|
-
context "extra argument" do
|
22
|
-
subject { -> { struct_class.new(a: 42, b: 41, c: 43, d: 44) } }
|
23
|
-
|
24
|
-
let(:struct_class) { described_class.with_attributes(:a, :b) }
|
25
|
-
|
26
|
-
it { is_expected.to raise_error(ArgumentError, "unknown keywords: c, d") }
|
27
|
-
end
|
28
|
-
|
29
|
-
context "missing argument" do
|
30
|
-
subject { -> { struct_class.new } }
|
31
|
-
|
32
|
-
let(:struct_class) { described_class.with_attributes(:a, :b) }
|
33
|
-
|
34
|
-
it { is_expected.to raise_error(ArgumentError, "missing keywords: a, b") }
|
35
|
-
end
|
36
|
-
|
37
|
-
context "inheritance" do
|
38
|
-
let(:parent_struct) { described_class.with_attributes(:a, :b) }
|
39
|
-
|
40
|
-
it "does not change parent attributes" do
|
41
|
-
expect do
|
42
|
-
parent_struct.with_attributes(:c)
|
43
|
-
end.not_to change { parent_struct.attributes }.from([:a, :b])
|
44
|
-
end
|
45
|
-
|
46
|
-
it "extends parent attributes" do
|
47
|
-
child_struct = parent_struct.with_attributes(:c)
|
48
|
-
expect(child_struct.attributes).to eq([:a, :b, :c])
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
context "with block" do
|
53
|
-
subject { struct_class.new(a: 42, b: 43).a_plus_b }
|
54
|
-
|
55
|
-
let(:struct_class) do
|
56
|
-
described_class.with_attributes(:a, :b) do
|
57
|
-
def a_plus_b
|
58
|
-
a + b
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
it "evaluates block in context of struct" do
|
64
|
-
is_expected.to eq(85)
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
describe "#==" do
|
70
|
-
context "with members" do
|
71
|
-
let(:struct_class) { described_class.with_attributes(:a, :b) }
|
72
|
-
|
73
|
-
context "same class and members" do
|
74
|
-
subject { struct_class.new(a: 42, b: 43) == struct_class.new(a: 42, b: 43) } # rubocop: disable Lint/BinaryOperatorWithIdenticalOperands
|
75
|
-
|
76
|
-
it { is_expected.to eq(true) }
|
77
|
-
end
|
78
|
-
|
79
|
-
context "same class and different members" do
|
80
|
-
subject { struct_class.new(a: 42, b: 43) == struct_class.new(a: 42, b: 0) }
|
81
|
-
|
82
|
-
it { is_expected.to eq(false) }
|
83
|
-
end
|
84
|
-
|
85
|
-
context "different class and same members" do
|
86
|
-
subject { struct_class.new(a: 42, b: 43) == struct_class_1.new(a: 42, b: 43) }
|
87
|
-
|
88
|
-
let(:struct_class_1) { described_class.with_attributes(:a, :b) }
|
89
|
-
|
90
|
-
it { is_expected.to eq(true) }
|
91
|
-
end
|
92
|
-
|
93
|
-
context "different class and different members" do
|
94
|
-
subject { struct_class.new(a: 42, b: 43) == struct_class.new(a: 42, b: 0) }
|
95
|
-
|
96
|
-
let(:struct_class_1) { described_class.with_attributes(:a, :b) }
|
97
|
-
|
98
|
-
it { is_expected.to eq(false) }
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
describe "#members" do
|
104
|
-
let(:struct) { struct_class.new(b: 43, a: 42) }
|
105
|
-
let(:struct_class) { described_class.with_attributes(:a, :b) }
|
106
|
-
|
107
|
-
it "returns members in the order they defined" do
|
108
|
-
expect(struct.members).to eq([:a, :b])
|
109
|
-
end
|
110
|
-
|
111
|
-
it "is immutable" do
|
112
|
-
expect { struct.members << :c }.not_to change { struct.members }.from([:a, :b])
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
describe "#to_a" do
|
117
|
-
let(:struct) { struct_class.new(b: 43, a: 42) }
|
118
|
-
let(:struct_class) { described_class.with_attributes(:a, :b) }
|
119
|
-
|
120
|
-
it "returns members values in the order they defined" do
|
121
|
-
expect(struct.to_a).to eq([42, 43])
|
122
|
-
end
|
123
|
-
|
124
|
-
it "is immutable" do
|
125
|
-
expect { struct.to_a << 44 }.not_to change { struct.to_a }.from([42, 43])
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
describe "#to_h" do
|
130
|
-
let(:struct_class) { described_class.with_attributes(:a, :b) }
|
131
|
-
|
132
|
-
context "without block" do
|
133
|
-
let(:struct) { struct_class.new(b: 43, a: 42) }
|
134
|
-
|
135
|
-
it "returns a Hash containing the names and values for the structs members" do
|
136
|
-
expect(struct.to_h).to eq(a: 42, b: 43)
|
137
|
-
end
|
138
|
-
|
139
|
-
it "is immutable" do
|
140
|
-
expect { struct.to_h.merge(c: 44) }.not_to change { struct.to_h }.from(a: 42, b: 43)
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
context "with block" do
|
145
|
-
subject do
|
146
|
-
struct.to_h do |key, value|
|
147
|
-
[key.upcase, value / 2]
|
148
|
-
end
|
149
|
-
end
|
150
|
-
let(:struct) { struct_class.new(b: 2, a: 4) }
|
151
|
-
|
152
|
-
it "returns a Hash containing the names and values for the structs members" do
|
153
|
-
is_expected.to eq(A: 2, B: 1)
|
154
|
-
end
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
describe "#copy" do
|
159
|
-
let(:struct_class) { described_class.with_attributes(:a, :b) }
|
160
|
-
let(:struct) { struct_class.new(b: 43, a: 42) }
|
161
|
-
|
162
|
-
context "attributes given" do
|
163
|
-
subject { struct.copy(b: 44) }
|
164
|
-
|
165
|
-
it { is_expected.to eq(struct_class.new(a: 42, b: 44)) }
|
166
|
-
end
|
167
|
-
|
168
|
-
context "string attributes" do
|
169
|
-
subject { -> { struct.copy("a" => 44) } }
|
170
|
-
|
171
|
-
it { is_expected.to raise_error(ArgumentError) }
|
172
|
-
end
|
173
|
-
|
174
|
-
context "no attributes given" do
|
175
|
-
subject { struct.copy == struct }
|
176
|
-
|
177
|
-
it { is_expected.to eq(true) }
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
describe "#inspect" do
|
182
|
-
subject { StrInspect.new(a: 2, b: nil).inspect }
|
183
|
-
StrInspect = Fear::Struct.with_attributes(:a, :b)
|
184
|
-
|
185
|
-
it { is_expected.to eq("<#Fear::Struct StrInspect a=2, b=nil>") }
|
186
|
-
end
|
187
|
-
|
188
|
-
describe "#inspect" do
|
189
|
-
subject { StrToS.new(a: 2, b: nil).inspect }
|
190
|
-
StrToS = Fear::Struct.with_attributes(:a, :b)
|
191
|
-
|
192
|
-
it { is_expected.to eq("<#Fear::Struct StrToS a=2, b=nil>") }
|
193
|
-
end
|
194
|
-
end
|