fear 1.2.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 (94) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +27 -0
  3. data/.github/workflows/rubocop.yml +2 -2
  4. data/.github/workflows/spec.yml +1 -1
  5. data/CHANGELOG.md +13 -0
  6. data/Gemfile.lock +53 -56
  7. data/README.md +54 -186
  8. data/Rakefile +0 -21
  9. data/examples/pattern_extracting.rb +4 -4
  10. data/fear.gemspec +2 -4
  11. data/lib/fear/either.rb +8 -4
  12. data/lib/fear/either_api.rb +2 -0
  13. data/lib/fear/either_pattern_match.rb +7 -8
  14. data/lib/fear/failure.rb +0 -9
  15. data/lib/fear/failure_pattern_match.rb +2 -0
  16. data/lib/fear/for_api.rb +2 -0
  17. data/lib/fear/future.rb +12 -20
  18. data/lib/fear/future_api.rb +13 -2
  19. data/lib/fear/left.rb +0 -9
  20. data/lib/fear/none.rb +7 -9
  21. data/lib/fear/option.rb +5 -1
  22. data/lib/fear/option_api.rb +2 -0
  23. data/lib/fear/option_pattern_match.rb +6 -4
  24. data/lib/fear/partial_function/empty.rb +2 -0
  25. data/lib/fear/partial_function/guard.rb +4 -4
  26. data/lib/fear/partial_function/or_else.rb +2 -0
  27. data/lib/fear/partial_function.rb +9 -8
  28. data/lib/fear/pattern_match.rb +0 -10
  29. data/lib/fear/pattern_matching_api.rb +3 -28
  30. data/lib/fear/promise.rb +3 -9
  31. data/lib/fear/right.rb +0 -10
  32. data/lib/fear/right_biased.rb +1 -1
  33. data/lib/fear/right_pattern_match.rb +2 -0
  34. data/lib/fear/some.rb +7 -10
  35. data/lib/fear/struct.rb +3 -14
  36. data/lib/fear/success.rb +0 -9
  37. data/lib/fear/success_pattern_match.rb +2 -0
  38. data/lib/fear/try.rb +6 -2
  39. data/lib/fear/try_api.rb +2 -0
  40. data/lib/fear/try_pattern_match.rb +7 -8
  41. data/lib/fear/utils.rb +0 -3
  42. data/lib/fear/version.rb +1 -1
  43. data/lib/fear.rb +8 -42
  44. data/spec/fear/awaitable_spec.rb +2 -0
  45. data/spec/fear/either_spec.rb +26 -0
  46. data/spec/fear/failure_spec.rb +8 -23
  47. data/spec/fear/for/mixin_spec.rb +15 -0
  48. data/spec/fear/future_spec.rb +17 -2
  49. data/spec/fear/guard_spec.rb +110 -0
  50. data/spec/fear/left_spec.rb +7 -22
  51. data/spec/fear/none_spec.rb +11 -17
  52. data/spec/fear/option_spec.rb +15 -1
  53. data/spec/fear/partial_function/any_spec.rb +25 -0
  54. data/spec/fear/partial_function_spec.rb +2 -24
  55. data/spec/fear/pattern_match_spec.rb +0 -34
  56. data/spec/fear/promise_spec.rb +4 -6
  57. data/spec/fear/right_spec.rb +0 -22
  58. data/spec/fear/some_spec.rb +10 -22
  59. data/spec/fear/success_spec.rb +0 -22
  60. data/spec/fear/try/mixin_spec.rb +14 -0
  61. data/spec/fear/try_api_spec.rb +23 -0
  62. data/spec/struct_spec.rb +1 -33
  63. metadata +18 -80
  64. data/examples/pattern_extracting_ruby2.7.rb +0 -15
  65. data/lib/fear/extractor/anonymous_array_splat_matcher.rb +0 -10
  66. data/lib/fear/extractor/any_matcher.rb +0 -17
  67. data/lib/fear/extractor/array_head_matcher.rb +0 -36
  68. data/lib/fear/extractor/array_matcher.rb +0 -40
  69. data/lib/fear/extractor/array_splat_matcher.rb +0 -16
  70. data/lib/fear/extractor/empty_list_matcher.rb +0 -20
  71. data/lib/fear/extractor/extractor_matcher.rb +0 -44
  72. data/lib/fear/extractor/grammar.rb +0 -203
  73. data/lib/fear/extractor/grammar.treetop +0 -129
  74. data/lib/fear/extractor/identifier_matcher.rb +0 -18
  75. data/lib/fear/extractor/matcher/and.rb +0 -38
  76. data/lib/fear/extractor/matcher.rb +0 -53
  77. data/lib/fear/extractor/named_array_splat_matcher.rb +0 -17
  78. data/lib/fear/extractor/pattern.rb +0 -58
  79. data/lib/fear/extractor/typed_identifier_matcher.rb +0 -26
  80. data/lib/fear/extractor/value_matcher.rb +0 -19
  81. data/lib/fear/extractor.rb +0 -112
  82. data/lib/fear/extractor_api.rb +0 -35
  83. data/spec/fear/extractor/array_matcher_spec.rb +0 -230
  84. data/spec/fear/extractor/extractor_matcher_spec.rb +0 -153
  85. data/spec/fear/extractor/grammar_array_spec.rb +0 -25
  86. data/spec/fear/extractor/identified_matcher_spec.rb +0 -49
  87. data/spec/fear/extractor/identifier_matcher_spec.rb +0 -68
  88. data/spec/fear/extractor/pattern_spec.rb +0 -34
  89. data/spec/fear/extractor/typed_identifier_matcher_spec.rb +0 -64
  90. data/spec/fear/extractor/value_matcher_number_spec.rb +0 -79
  91. data/spec/fear/extractor/value_matcher_string_spec.rb +0 -88
  92. data/spec/fear/extractor/value_matcher_symbol_spec.rb +0 -71
  93. data/spec/fear/extractor_api_spec.rb +0 -115
  94. data/spec/fear/extractor_spec.rb +0 -61
@@ -1,79 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe Fear::Extractor::ValueMatcher, "Number" do
4
- let(:parser) { Fear::Extractor::GrammarParser.new }
5
- let(:matcher) { parser.parse(pattern).to_matcher }
6
-
7
- describe "#defined_at?" do
8
- subject { matcher }
9
-
10
- context "Integer" do
11
- let(:pattern) { "1" }
12
-
13
- it { is_expected.to be_defined_at(1) }
14
- it { is_expected.to be_defined_at(1.0) }
15
- it { is_expected.not_to be_defined_at(2) }
16
- it { is_expected.not_to be_defined_at("1") }
17
- end
18
-
19
- context "Float" do
20
- context "against float" do
21
- let(:pattern) { "1.2" }
22
-
23
- it { is_expected.to be_defined_at(1.2) }
24
- it { is_expected.not_to be_defined_at(1.3) }
25
- end
26
-
27
- context "against integer" do
28
- let(:pattern) { "1.0" }
29
-
30
- it { is_expected.to be_defined_at(1) }
31
- it { is_expected.not_to be_defined_at(2) }
32
- it { is_expected.not_to be_defined_at("1") }
33
- end
34
- end
35
- end
36
-
37
- describe "#call" do
38
- subject { matcher.(other) }
39
-
40
- let(:pattern) { "1.0" }
41
-
42
- context "defined" do
43
- let(:other) { 1 }
44
-
45
- it { is_expected.to eq({}) }
46
- end
47
- end
48
-
49
- describe "#failure_reason" do
50
- subject { matcher.failure_reason(other) }
51
-
52
- let(:pattern) { "1.0" }
53
-
54
- context "match integer" do
55
- let(:other) { 1 }
56
- let(:pattern) { "1" }
57
-
58
- it { is_expected.to eq(Fear.none) }
59
- end
60
-
61
- context "match float" do
62
- let(:other) { 1.0 }
63
- let(:pattern) { "1" }
64
-
65
- it { is_expected.to eq(Fear.none) }
66
- end
67
-
68
- context "does not match another integer" do
69
- let(:other) { 2 }
70
- let(:pattern) { "1" }
71
-
72
- it { is_expected.to eq(Fear.some(<<~ERROR.strip)) }
73
- Expected `2` to match:
74
- 1
75
- ^
76
- ERROR
77
- end
78
- end
79
- end
@@ -1,88 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe Fear::Extractor::ValueMatcher, "String" do
4
- let(:parser) { Fear::Extractor::GrammarParser.new }
5
- let(:matcher) { parser.parse(pattern).to_matcher }
6
-
7
- describe "#defined_at?" do
8
- subject { matcher }
9
-
10
- context "double quotas" do
11
- let(:pattern) { %("foo") }
12
-
13
- it { is_expected.to be_defined_at("foo") }
14
- it { is_expected.not_to be_defined_at("boo") }
15
- it { is_expected.not_to be_defined_at(2) }
16
-
17
- context "single quotes inside" do
18
- let(:pattern) { %("f'o'o") }
19
-
20
- it { is_expected.to be_defined_at(%(f'o'o)) }
21
- it { is_expected.not_to be_defined_at(%(f"o"o)) }
22
- end
23
-
24
- context "escaped double quotes inside" do
25
- let(:pattern) { '"f\"oo"' }
26
-
27
- it { is_expected.to be_defined_at('f"oo') }
28
- it { is_expected.not_to be_defined_at("f'oo") }
29
- end
30
- end
31
-
32
- context "single quotas" do
33
- let(:pattern) { %('foo') }
34
-
35
- it { is_expected.to be_defined_at("foo") }
36
- it { is_expected.not_to be_defined_at("boo") }
37
- it { is_expected.not_to be_defined_at(2) }
38
-
39
- context "double quotes inside" do
40
- let(:pattern) { %('f"o"o') }
41
-
42
- it { is_expected.to be_defined_at(%(f"o"o)) }
43
- it { is_expected.not_to be_defined_at(%(f'o'o)) }
44
- end
45
-
46
- context "escaped single quotes inside" do
47
- let(:pattern) { "'f\\'oo'" }
48
-
49
- it { is_expected.to be_defined_at("f\\'oo") }
50
- it { is_expected.not_to be_defined_at("f'oo") }
51
- end
52
- end
53
- end
54
-
55
- describe "#call" do
56
- subject { matcher.(other) }
57
-
58
- let(:pattern) { '"foo"' }
59
-
60
- context "defined" do
61
- let(:other) { "foo" }
62
-
63
- it { is_expected.to eq({}) }
64
- end
65
- end
66
-
67
- describe "#failure_reason" do
68
- subject { matcher.failure_reason(other) }
69
-
70
- context "match" do
71
- let(:other) { "foo" }
72
- let(:pattern) { '"foo"' }
73
-
74
- it { is_expected.to eq(Fear.none) }
75
- end
76
-
77
- context "does not match" do
78
- let(:other) { "bar" }
79
- let(:pattern) { '"foo"' }
80
-
81
- it { is_expected.to eq(Fear.some(<<~ERROR.strip)) }
82
- Expected `"bar"` to match:
83
- "foo"
84
- ^
85
- ERROR
86
- end
87
- end
88
- end
@@ -1,71 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe Fear::Extractor::ValueMatcher, "Symbol" do
4
- let(:parser) { Fear::Extractor::GrammarParser.new }
5
- let(:matcher) { parser.parse(pattern).to_matcher }
6
-
7
- describe "#defined_at?" do
8
- subject { matcher }
9
-
10
- context "no quotas" do
11
- let(:pattern) { ":foo" }
12
-
13
- it { is_expected.to be_defined_at(:foo) }
14
- it { is_expected.not_to be_defined_at("foo") }
15
- it { is_expected.not_to be_defined_at(:boo) }
16
- it { is_expected.not_to be_defined_at(2) }
17
- end
18
-
19
- context "double quotas" do
20
- let(:pattern) { %(:"foo") }
21
-
22
- it { is_expected.to be_defined_at(:foo) }
23
- it { is_expected.not_to be_defined_at("foo") }
24
- it { is_expected.not_to be_defined_at(:boo) }
25
- it { is_expected.not_to be_defined_at(2) }
26
- end
27
-
28
- context "single quotas" do
29
- let(:pattern) { %(:'foo') }
30
-
31
- it { is_expected.to be_defined_at(:foo) }
32
- it { is_expected.not_to be_defined_at("foo") }
33
- it { is_expected.not_to be_defined_at(:boo) }
34
- it { is_expected.not_to be_defined_at(2) }
35
- end
36
- end
37
-
38
- describe "#call" do
39
- subject { matcher.(other) }
40
-
41
- let(:pattern) { ":foo" }
42
-
43
- context "defined" do
44
- let(:other) { :foo }
45
-
46
- it { is_expected.to eq({}) }
47
- end
48
- end
49
-
50
- describe "#failure_reason" do
51
- subject { matcher.failure_reason(other) }
52
-
53
- context "match" do
54
- let(:other) { :foo }
55
- let(:pattern) { ":foo" }
56
-
57
- it { is_expected.to eq(Fear.none) }
58
- end
59
-
60
- context "does not match" do
61
- let(:other) { :bar }
62
- let(:pattern) { ":foo" }
63
-
64
- it { is_expected.to eq(Fear.some(<<~ERROR.strip)) }
65
- Expected `:bar` to match:
66
- :foo
67
- ^
68
- ERROR
69
- end
70
- end
71
- end
@@ -1,115 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe Fear::ExtractorApi do
4
- def assert(value)
5
- expect(value).to eq(true)
6
- end
7
-
8
- def assert_not(value)
9
- expect(value).not_to eq(true)
10
- end
11
-
12
- def assert_invalid_syntax
13
- expect do
14
- yield
15
- end.to raise_error(Fear::PatternSyntaxError)
16
- end
17
-
18
- def assert_valid_syntax
19
- expect { yield }.not_to raise_error
20
- end
21
-
22
- specify "Array" do
23
- assert(Fear["[]"] === [])
24
- assert_not(Fear["[]"] === [1])
25
- assert(Fear["[1]"] === [1])
26
- assert_not(Fear["[1]"] === [1, 2])
27
- assert_not(Fear["[1]"] === [2])
28
- assert(Fear["[1, 2]"] === [1, 2])
29
- assert_not(Fear["[1, 2]"] === [1, 3])
30
- assert_not(Fear["[1, 2]"] === [1, 2, 4])
31
- assert_not(Fear["[1, 2]"] === [1])
32
- assert(Fear["[*]"] === [])
33
- assert(Fear["[*]"] === [1, 2])
34
- assert_not(Fear["[1, *]"] === [])
35
- assert(Fear["[1, *]"] === [1])
36
- assert(Fear["[1, *]"] === [1, 2])
37
- assert(Fear["[1, *]"] === [1, 2, 3])
38
- assert(Fear["[[1]]"] === [[1]])
39
- assert(Fear["[[1],2]"] === [[1], 2])
40
- assert(Fear["[[1],*]"] === [[1], 2])
41
- assert(Fear["[[*],*]"] === [[1], 2])
42
- assert_invalid_syntax { Fear["[*, 2]"] }
43
- assert_invalid_syntax { Fear["[*, ]"] }
44
- assert_invalid_syntax { Fear["[1, *, ]"] }
45
- assert_invalid_syntax { Fear["[1, *, 2]"] }
46
- assert_invalid_syntax { Fear["[1, *, *]"] }
47
- assert_invalid_syntax { Fear["[*, *]"] }
48
- assert(Fear["[a, b]"] === [1, 2])
49
- assert_not(Fear["[a, b, c]"] === [1, 2])
50
- assert(Fear["[a, b, _]"] === [1, 2, 3])
51
- assert(Fear["[a, b, *c]"] === [1, 2])
52
- assert_not(Fear["[a, b, c, *d]"] === [1, 2])
53
- end
54
-
55
- specify "String" do
56
- assert(Fear['"foo"'] === "foo")
57
- assert(Fear['"f\"oo"'] === 'f"oo')
58
- assert_not(Fear['"foo"'] === "bar")
59
- assert(Fear["'foo'"] === "foo")
60
- assert_not(Fear["'foo'"] === "bar")
61
- end
62
-
63
- specify "Symbol" do
64
- assert(Fear[':"foo"'] === :foo)
65
- assert(Fear[":'foo'"] === :foo)
66
- assert(Fear[":foo"] === :foo)
67
- assert_not(Fear[":foo"] === :bar)
68
- end
69
-
70
- specify "Boolean" do
71
- assert(Fear["true"] === true)
72
- assert(Fear["false"] === false)
73
- assert_not(Fear["true"] === false)
74
- assert_not(Fear["false"] === true)
75
- end
76
-
77
- specify "Nil" do
78
- assert(Fear["nil"] === nil) # rubocop:disable Style/NilComparison
79
- assert_not(Fear["nil"] === 42)
80
- end
81
-
82
- specify "_" do
83
- assert(Fear["_"] === nil) # rubocop:disable Style/NilComparison
84
- assert(Fear["_"] === true)
85
- assert(Fear["_"] === false)
86
- assert(Fear["_"] === 42)
87
- assert(Fear["_"] === "foo")
88
- assert(Fear["_"] === [42])
89
- end
90
-
91
- specify "type matching" do
92
- class Foo
93
- class Bar
94
- end
95
- end
96
-
97
- assert(Fear["Integer"] === 3)
98
- assert_not(Fear["Integer"] === "3")
99
- assert(Fear["Numeric"] === 3)
100
- assert(Fear["Foo::Bar"] === Foo::Bar.new)
101
- assert(Fear["var : Integer"] === 3)
102
- assert_not(Fear["var : Integer"] === "3")
103
- end
104
-
105
- specify "capture matcher" do
106
- assert(Fear["array @ [head : Integer, *tail]"] === [1, 2])
107
- assert_not(Fear["array @ [head : Integer, *tail]"] === ["1", 2])
108
- end
109
-
110
- specify "extractor" do
111
- assert_valid_syntax { Fear["Foo(a, b : Integer)"] }
112
- assert(Fear["Fear::Some(a : Integer)"] === Fear.some(42))
113
- assert_not(Fear["Fear::Some(a : Integer)"] === Fear.some("foo"))
114
- end
115
- end
@@ -1,61 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe Fear::Extractor do
4
- describe ".register_extractor" do
5
- Foo = ::Struct.new(:v1, :v2)
6
- let(:matcher) do
7
- Fear.matcher do |m|
8
- m.case(Fear["Foo(43, second : Integer)"]) { |second| "43 and #{second}" }
9
- m.case(Fear["Foo(42, second : Integer)"]) { |second| "42 and #{second}" }
10
- m.else { "no match" }
11
- end
12
- end
13
-
14
- let(:extractor) do
15
- Fear.case(Foo) { |foo| [foo.v1, foo.v2] }.lift
16
- end
17
-
18
- context "extractor not registered" do
19
- it "raise Fear::Extractor::ExtractorNotFound" do
20
- expect do
21
- described_class.find_extractor("UnknownExtractor")
22
- end.to raise_error(Fear::Extractor::ExtractorNotFound)
23
- end
24
- end
25
-
26
- context "register by name" do
27
- let(:extractor) { ->(*) { Fear.some("matched") } }
28
-
29
- before do
30
- described_class.register_extractor(
31
- "ExtractorRegisteredByName",
32
- "ExtractorRegisteredByName2",
33
- extractor,
34
- )
35
- end
36
-
37
- it "returns extractor" do
38
- expect(described_class.find_extractor("ExtractorRegisteredByName")).to eq(extractor)
39
- expect(described_class.find_extractor("ExtractorRegisteredByName2")).to eq(extractor)
40
- end
41
- end
42
-
43
- context "register by class" do
44
- let(:extractor) { ->(*) { Fear.some("matched") } }
45
- ExtractorRegisteredByClass = Class.new
46
-
47
- before do
48
- described_class.register_extractor(
49
- ExtractorRegisteredByClass,
50
- "ExtractorRegisteredByClass2",
51
- extractor,
52
- )
53
- end
54
-
55
- it "returns extractor" do
56
- expect(described_class.find_extractor("ExtractorRegisteredByClass")).to eq(extractor)
57
- expect(described_class.find_extractor("ExtractorRegisteredByClass2")).to eq(extractor)
58
- end
59
- end
60
- end
61
- end