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.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +105 -0
  3. data/.simplecov +2 -2
  4. data/.standard.yml +1 -0
  5. data/CHANGELOG.md +8 -0
  6. data/Gemfile +14 -2
  7. data/Gemfile.lock +84 -78
  8. data/LICENSE.txt +1 -1
  9. data/README.md +18 -70
  10. data/Rakefile +55 -142
  11. data/benchmarks/dry_do_vs_fear_for.txt +7 -6
  12. data/benchmarks/dry_some_fmap_vs_fear_some_map.txt +7 -6
  13. data/benchmarks/factorial.txt +7 -9
  14. data/benchmarks/fear_gaurd_and1_vs_new.txt +7 -6
  15. data/benchmarks/fear_gaurd_and2_vs_and.txt +8 -7
  16. data/benchmarks/fear_gaurd_and3_vs_and_and.txt +7 -6
  17. data/benchmarks/fear_pattern_matching_construction_vs_execution.txt +7 -6
  18. data/benchmarks/pattern_matching_dry_vs_qo_vs_fear_try.txt +8 -10
  19. data/examples/pattern_extracting.rb +2 -2
  20. data/examples/pattern_matching_number_in_words.rb +12 -12
  21. data/fear.gemspec +5 -21
  22. data/lib/fear/either/left_projection.rb +237 -0
  23. data/lib/fear/either/pattern_match.rb +49 -0
  24. data/lib/fear/either.rb +21 -12
  25. data/lib/fear/either_api.rb +2 -4
  26. data/lib/fear/empty_partial_function.rb +3 -3
  27. data/lib/fear/failure/pattern_match.rb +14 -0
  28. data/lib/fear/failure.rb +5 -5
  29. data/lib/fear/for.rb +1 -1
  30. data/lib/fear/for_api.rb +1 -3
  31. data/lib/fear/future.rb +6 -6
  32. data/lib/fear/future_api.rb +0 -5
  33. data/lib/fear/left/pattern_match.rb +15 -0
  34. data/lib/fear/left.rb +4 -4
  35. data/lib/fear/none.rb +0 -87
  36. data/lib/fear/none_class/pattern_match.rb +16 -0
  37. data/lib/fear/none_class.rb +85 -0
  38. data/lib/fear/option/pattern_match.rb +47 -0
  39. data/lib/fear/option.rb +2 -6
  40. data/lib/fear/option_api.rb +1 -3
  41. data/lib/fear/partial_function/and_then.rb +2 -2
  42. data/lib/fear/partial_function/combined.rb +3 -3
  43. data/lib/fear/partial_function/empty.rb +3 -5
  44. data/lib/fear/partial_function/guard.rb +0 -4
  45. data/lib/fear/partial_function/or_else.rb +2 -4
  46. data/lib/fear/partial_function.rb +0 -9
  47. data/lib/fear/partial_function_class.rb +2 -2
  48. data/lib/fear/pattern_match.rb +5 -4
  49. data/lib/fear/pattern_matching_api.rb +1 -4
  50. data/lib/fear/right/pattern_match.rb +15 -0
  51. data/lib/fear/right.rb +4 -4
  52. data/lib/fear/right_biased.rb +2 -0
  53. data/lib/fear/some/pattern_match.rb +15 -0
  54. data/lib/fear/some.rb +3 -3
  55. data/lib/fear/success/pattern_match.rb +16 -0
  56. data/lib/fear/success.rb +5 -5
  57. data/lib/fear/try/pattern_match.rb +29 -0
  58. data/lib/fear/try.rb +3 -7
  59. data/lib/fear/try_api.rb +1 -3
  60. data/lib/fear/utils.rb +1 -1
  61. data/lib/fear/version.rb +1 -1
  62. data/lib/fear.rb +3 -14
  63. data/spec/fear/awaitable_spec.rb +0 -2
  64. data/spec/fear/either/left_projection_spec.rb +289 -0
  65. data/spec/fear/{either_pattern_match_spec.rb → either/pattern_match_spec.rb} +7 -7
  66. data/spec/fear/either_spec.rb +1 -1
  67. data/spec/fear/left_spec.rb +1 -1
  68. data/spec/fear/{option_pattern_match_spec.rb → option/pattern_match_spec.rb} +6 -6
  69. data/spec/fear/option_spec.rb +2 -2
  70. data/spec/fear/partial_function/any_spec.rb +3 -3
  71. data/spec/fear/partial_function/empty_spec.rb +2 -2
  72. data/spec/fear/partial_function_and_then_spec.rb +5 -5
  73. data/spec/fear/partial_function_composition_spec.rb +6 -6
  74. data/spec/fear/partial_function_or_else_spec.rb +13 -13
  75. data/spec/fear/partial_function_spec.rb +7 -7
  76. data/spec/fear/pattern_match_spec.rb +5 -5
  77. data/spec/fear/right_spec.rb +1 -1
  78. data/spec/fear/{try_pattern_match_spec.rb → try/try_pattern_match_spec.rb} +7 -7
  79. data/spec/fear/try_api_spec.rb +2 -2
  80. data/spec/fear/utils_spec.rb +3 -3
  81. data/spec/support/.keep +0 -0
  82. metadata +27 -296
  83. data/.github/workflows/rubocop.yml +0 -39
  84. data/.github/workflows/spec.yml +0 -43
  85. data/.rubocop.yml +0 -7
  86. data/benchmarks/fear_pattern_extracting_with_vs_without_cache.txt +0 -11
  87. data/benchmarks/pattern_matching_qo_vs_fear_pattern_extraction.txt +0 -11
  88. data/benchmarks/pattern_matching_qo_vs_fear_try_execution.txt +0 -11
  89. data/lib/dry/types/fear/option.rb +0 -125
  90. data/lib/dry/types/fear.rb +0 -8
  91. data/lib/fear/either_pattern_match.rb +0 -52
  92. data/lib/fear/failure_pattern_match.rb +0 -12
  93. data/lib/fear/left_pattern_match.rb +0 -11
  94. data/lib/fear/none_pattern_match.rb +0 -14
  95. data/lib/fear/option_pattern_match.rb +0 -50
  96. data/lib/fear/right_pattern_match.rb +0 -13
  97. data/lib/fear/some_pattern_match.rb +0 -13
  98. data/lib/fear/struct.rb +0 -237
  99. data/lib/fear/success_pattern_match.rb +0 -14
  100. data/lib/fear/try_pattern_match.rb +0 -32
  101. data/spec/dry/types/fear/option/constrained_spec.rb +0 -22
  102. data/spec/dry/types/fear/option/core_spec.rb +0 -77
  103. data/spec/dry/types/fear/option/default_spec.rb +0 -21
  104. data/spec/dry/types/fear/option/hash_spec.rb +0 -58
  105. data/spec/dry/types/fear/option/option_spec.rb +0 -97
  106. data/spec/struct_pattern_matching_spec.rb +0 -36
  107. data/spec/struct_spec.rb +0 -194
  108. 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
@@ -1,6 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "dry/types/fear"
4
- require "dry/types/spec/types"
5
-
6
- Dry::Types.load_extensions(:fear_option)