fear 1.0.0 → 2.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.
Files changed (143) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +27 -0
  3. data/.github/workflows/rubocop.yml +39 -0
  4. data/.github/workflows/spec.yml +42 -0
  5. data/.rubocop.yml +4 -60
  6. data/.simplecov +17 -0
  7. data/CHANGELOG.md +29 -1
  8. data/Gemfile +5 -5
  9. data/Gemfile.lock +86 -50
  10. data/README.md +240 -209
  11. data/Rakefile +72 -65
  12. data/examples/pattern_extracting.rb +10 -8
  13. data/examples/pattern_matching_binary_tree_set.rb +7 -2
  14. data/examples/pattern_matching_number_in_words.rb +48 -42
  15. data/fear.gemspec +33 -34
  16. data/lib/dry/types/fear/option.rb +125 -0
  17. data/lib/dry/types/fear.rb +8 -0
  18. data/lib/fear/await.rb +33 -0
  19. data/lib/fear/awaitable.rb +28 -0
  20. data/lib/fear/either.rb +15 -4
  21. data/lib/fear/either_api.rb +4 -0
  22. data/lib/fear/either_pattern_match.rb +9 -5
  23. data/lib/fear/empty_partial_function.rb +3 -1
  24. data/lib/fear/failure.rb +7 -7
  25. data/lib/fear/failure_pattern_match.rb +4 -0
  26. data/lib/fear/for.rb +4 -2
  27. data/lib/fear/for_api.rb +5 -1
  28. data/lib/fear/future.rb +157 -82
  29. data/lib/fear/future_api.rb +17 -4
  30. data/lib/fear/left.rb +3 -9
  31. data/lib/fear/left_pattern_match.rb +2 -0
  32. data/lib/fear/none.rb +28 -10
  33. data/lib/fear/none_pattern_match.rb +2 -0
  34. data/lib/fear/option.rb +30 -2
  35. data/lib/fear/option_api.rb +4 -0
  36. data/lib/fear/option_pattern_match.rb +8 -3
  37. data/lib/fear/partial_function/and_then.rb +4 -2
  38. data/lib/fear/partial_function/any.rb +2 -0
  39. data/lib/fear/partial_function/combined.rb +3 -1
  40. data/lib/fear/partial_function/empty.rb +6 -0
  41. data/lib/fear/partial_function/guard/and.rb +2 -0
  42. data/lib/fear/partial_function/guard/and3.rb +2 -0
  43. data/lib/fear/partial_function/guard/or.rb +2 -0
  44. data/lib/fear/partial_function/guard.rb +8 -6
  45. data/lib/fear/partial_function/lifted.rb +2 -0
  46. data/lib/fear/partial_function/or_else.rb +5 -1
  47. data/lib/fear/partial_function.rb +18 -9
  48. data/lib/fear/partial_function_class.rb +3 -1
  49. data/lib/fear/pattern_match.rb +3 -11
  50. data/lib/fear/pattern_matching_api.rb +6 -28
  51. data/lib/fear/promise.rb +7 -5
  52. data/lib/fear/right.rb +3 -9
  53. data/lib/fear/right_biased.rb +5 -3
  54. data/lib/fear/right_pattern_match.rb +4 -0
  55. data/lib/fear/some.rb +35 -8
  56. data/lib/fear/some_pattern_match.rb +2 -0
  57. data/lib/fear/struct.rb +237 -0
  58. data/lib/fear/success.rb +7 -8
  59. data/lib/fear/success_pattern_match.rb +4 -0
  60. data/lib/fear/try.rb +8 -2
  61. data/lib/fear/try_api.rb +4 -0
  62. data/lib/fear/try_pattern_match.rb +9 -5
  63. data/lib/fear/unit.rb +6 -2
  64. data/lib/fear/utils.rb +14 -2
  65. data/lib/fear/version.rb +4 -1
  66. data/lib/fear.rb +26 -44
  67. data/spec/dry/types/fear/option/constrained_spec.rb +22 -0
  68. data/spec/dry/types/fear/option/core_spec.rb +77 -0
  69. data/spec/dry/types/fear/option/default_spec.rb +21 -0
  70. data/spec/dry/types/fear/option/hash_spec.rb +58 -0
  71. data/spec/dry/types/fear/option/option_spec.rb +97 -0
  72. data/spec/fear/awaitable_spec.rb +19 -0
  73. data/spec/fear/done_spec.rb +7 -5
  74. data/spec/fear/either/mixin_spec.rb +4 -2
  75. data/spec/fear/either_pattern_match_spec.rb +10 -8
  76. data/spec/fear/either_pattern_matching_spec.rb +28 -0
  77. data/spec/fear/either_spec.rb +26 -0
  78. data/spec/fear/failure_spec.rb +57 -70
  79. data/spec/fear/for/mixin_spec.rb +15 -0
  80. data/spec/fear/for_spec.rb +19 -17
  81. data/spec/fear/future_spec.rb +477 -237
  82. data/spec/fear/guard_spec.rb +136 -24
  83. data/spec/fear/left_spec.rb +57 -70
  84. data/spec/fear/none_spec.rb +39 -43
  85. data/spec/fear/option/mixin_spec.rb +9 -7
  86. data/spec/fear/option_pattern_match_spec.rb +10 -8
  87. data/spec/fear/option_pattern_matching_spec.rb +34 -0
  88. data/spec/fear/option_spec.rb +142 -0
  89. data/spec/fear/partial_function/any_spec.rb +25 -0
  90. data/spec/fear/partial_function/empty_spec.rb +12 -10
  91. data/spec/fear/partial_function_and_then_spec.rb +39 -37
  92. data/spec/fear/partial_function_composition_spec.rb +46 -44
  93. data/spec/fear/partial_function_or_else_spec.rb +92 -90
  94. data/spec/fear/partial_function_spec.rb +91 -61
  95. data/spec/fear/pattern_match_spec.rb +19 -51
  96. data/spec/fear/pattern_matching_api_spec.rb +31 -0
  97. data/spec/fear/promise_spec.rb +23 -23
  98. data/spec/fear/right_biased/left.rb +28 -26
  99. data/spec/fear/right_biased/right.rb +51 -49
  100. data/spec/fear/right_spec.rb +48 -68
  101. data/spec/fear/some_spec.rb +30 -40
  102. data/spec/fear/success_spec.rb +40 -60
  103. data/spec/fear/try/mixin_spec.rb +19 -3
  104. data/spec/fear/try_api_spec.rb +23 -0
  105. data/spec/fear/try_pattern_match_spec.rb +10 -8
  106. data/spec/fear/try_pattern_matching_spec.rb +34 -0
  107. data/spec/fear/utils_spec.rb +16 -14
  108. data/spec/spec_helper.rb +13 -7
  109. data/spec/struct_pattern_matching_spec.rb +36 -0
  110. data/spec/struct_spec.rb +194 -0
  111. data/spec/support/dry_types.rb +6 -0
  112. metadata +128 -87
  113. data/.travis.yml +0 -13
  114. data/lib/fear/extractor/anonymous_array_splat_matcher.rb +0 -8
  115. data/lib/fear/extractor/any_matcher.rb +0 -15
  116. data/lib/fear/extractor/array_head_matcher.rb +0 -34
  117. data/lib/fear/extractor/array_matcher.rb +0 -38
  118. data/lib/fear/extractor/array_splat_matcher.rb +0 -14
  119. data/lib/fear/extractor/empty_list_matcher.rb +0 -18
  120. data/lib/fear/extractor/extractor_matcher.rb +0 -42
  121. data/lib/fear/extractor/grammar.rb +0 -201
  122. data/lib/fear/extractor/grammar.treetop +0 -129
  123. data/lib/fear/extractor/identifier_matcher.rb +0 -16
  124. data/lib/fear/extractor/matcher/and.rb +0 -36
  125. data/lib/fear/extractor/matcher.rb +0 -54
  126. data/lib/fear/extractor/named_array_splat_matcher.rb +0 -15
  127. data/lib/fear/extractor/pattern.rb +0 -55
  128. data/lib/fear/extractor/typed_identifier_matcher.rb +0 -24
  129. data/lib/fear/extractor/value_matcher.rb +0 -17
  130. data/lib/fear/extractor.rb +0 -108
  131. data/lib/fear/extractor_api.rb +0 -33
  132. data/spec/fear/extractor/array_matcher_spec.rb +0 -228
  133. data/spec/fear/extractor/extractor_matcher_spec.rb +0 -151
  134. data/spec/fear/extractor/grammar_array_spec.rb +0 -23
  135. data/spec/fear/extractor/identified_matcher_spec.rb +0 -47
  136. data/spec/fear/extractor/identifier_matcher_spec.rb +0 -66
  137. data/spec/fear/extractor/pattern_spec.rb +0 -32
  138. data/spec/fear/extractor/typed_identifier_matcher_spec.rb +0 -62
  139. data/spec/fear/extractor/value_matcher_number_spec.rb +0 -77
  140. data/spec/fear/extractor/value_matcher_string_spec.rb +0 -86
  141. data/spec/fear/extractor/value_matcher_symbol_spec.rb +0 -69
  142. data/spec/fear/extractor_api_spec.rb +0 -113
  143. data/spec/fear/extractor_spec.rb +0 -59
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Fear::Try do
4
+ describe "pattern matching" do
5
+ subject do
6
+ case value
7
+ in Fear::Success[Integer => int]
8
+ "success of #{int}"
9
+ in Fear::Failure[RuntimeError]
10
+ "runtime error"
11
+ else
12
+ "something else"
13
+ end
14
+ end
15
+
16
+ context "when value is success of integer" do
17
+ let(:value) { Fear.try { 42 } }
18
+
19
+ it { is_expected.to eq("success of 42") }
20
+ end
21
+
22
+ context "when value is failure runtime error" do
23
+ let(:value) { Fear.try { raise } }
24
+
25
+ it { is_expected.to eq("runtime error") }
26
+ end
27
+
28
+ context "when value is something else" do
29
+ let(:value) { Fear.try { raise StandardError } }
30
+
31
+ it { is_expected.to eq("something else") }
32
+ end
33
+ end
34
+ end
@@ -1,58 +1,60 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Fear::Utils do
2
- describe '.assert_arg_or_block!' do
4
+ describe ".assert_arg_or_block!" do
3
5
  def assert_arg_or_block!(*args, &block)
4
6
  described_class.assert_arg_or_block!(:the_method, *args, &block)
5
7
  end
6
8
 
7
- context 'block given, argument does not given' do
9
+ context "block given, argument does not given" do
8
10
  subject { proc { assert_arg_or_block! {} } }
9
11
 
10
12
  it { is_expected.not_to raise_error }
11
13
  end
12
14
 
13
- context 'argument given, block does not given' do
15
+ context "argument given, block does not given" do
14
16
  subject { proc { assert_arg_or_block!(42) } }
15
17
 
16
18
  it { is_expected.not_to raise_error }
17
19
  end
18
20
 
19
- context 'argument and block given at the same time' do
21
+ context "argument and block given at the same time" do
20
22
  subject { proc { assert_arg_or_block!(42) {} } }
21
23
 
22
- it 'fails with argument error' do
24
+ it "fails with argument error" do
23
25
  is_expected.to raise_error(
24
26
  ArgumentError,
25
- '#the_method accepts either one argument or block',
27
+ "#the_method accepts either one argument or block",
26
28
  )
27
29
  end
28
30
  end
29
31
 
30
- context 'no argument and no block given' do
32
+ context "no argument and no block given" do
31
33
  subject { proc { assert_arg_or_block! } }
32
34
 
33
- it 'fails with argument error' do
35
+ it "fails with argument error" do
34
36
  is_expected.to raise_error(
35
37
  ArgumentError,
36
- '#the_method accepts either one argument or block',
38
+ "#the_method accepts either one argument or block",
37
39
  )
38
40
  end
39
41
  end
40
42
  end
41
43
 
42
- describe 'assert_type!' do
43
- context 'value is of the given type' do
44
+ describe "assert_type!" do
45
+ context "value is of the given type" do
44
46
  subject { proc { described_class.assert_type!(24, Integer) } }
45
47
 
46
48
  it { is_expected.not_to raise_error }
47
49
  end
48
50
 
49
- context 'value is not of the given type' do
51
+ context "value is not of the given type" do
50
52
  subject { proc { described_class.assert_type!(24, String) } }
51
53
 
52
- it 'raises TypeError' do
54
+ it "raises TypeError" do
53
55
  is_expected.to raise_error(
54
56
  TypeError,
55
- 'expected `24` to be of String class',
57
+ "expected `24` to be of String class",
56
58
  )
57
59
  end
58
60
  end
data/spec/spec_helper.rb CHANGED
@@ -1,12 +1,18 @@
1
- # require 'codeclimate-test-reporter'
2
- # CodeClimate::TestReporter.start
1
+ # frozen_string_literal: true
3
2
 
4
- require 'fear'
5
- require File.expand_path('spec/fear/right_biased/right')
6
- require File.expand_path('spec/fear/right_biased/left')
7
- require 'date'
3
+ require "simplecov"
4
+
5
+ require "fear"
6
+ require File.expand_path("spec/fear/right_biased/right")
7
+ require File.expand_path("spec/fear/right_biased/left")
8
+ require "date"
9
+ require "fear/rspec"
8
10
 
9
11
  RSpec.configure do |config|
12
+ unless RUBY_VERSION >= "2.7"
13
+ config.exclude_pattern = "**/*pattern_matching_spec.rb"
14
+ end
15
+
10
16
  # rspec-expectations config goes here. You can use an alternate
11
17
  # assertion/expectation library such as wrong or the stdlib/minitest
12
18
  # assertions if you prefer.
@@ -56,7 +62,7 @@ RSpec.configure do |config|
56
62
  # Use the documentation formatter for detailed output,
57
63
  # unless a formatter has already been configured
58
64
  # (e.g. via a command-line flag).
59
- config.default_formatter = 'doc'
65
+ config.default_formatter = "doc"
60
66
  end
61
67
 
62
68
  # Print the 10 slowest examples and example groups at the
@@ -0,0 +1,36 @@
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
@@ -0,0 +1,194 @@
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
@@ -0,0 +1,6 @@
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)