fear 0.11.0 → 1.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 (110) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +0 -1
  3. data/.rubocop.yml +18 -0
  4. data/.travis.yml +0 -3
  5. data/CHANGELOG.md +12 -1
  6. data/Gemfile +1 -0
  7. data/{gemfiles/dry_equalizer_0.2.1.gemfile.lock → Gemfile.lock} +21 -12
  8. data/README.md +594 -241
  9. data/Rakefile +166 -219
  10. data/benchmarks/README.md +1 -0
  11. data/benchmarks/dry_do_vs_fear_for.txt +11 -0
  12. data/benchmarks/dry_some_fmap_vs_fear_some_map.txt +11 -0
  13. data/benchmarks/factorial.txt +16 -0
  14. data/benchmarks/fear_gaurd_and1_vs_new.txt +13 -0
  15. data/benchmarks/fear_gaurd_and2_vs_and.txt +13 -0
  16. data/benchmarks/fear_gaurd_and3_vs_and_and.txt +13 -0
  17. data/benchmarks/fear_pattern_extracting_with_vs_without_cache.txt +11 -0
  18. data/benchmarks/fear_pattern_matching_construction_vs_execution.txt +13 -0
  19. data/benchmarks/pattern_matching_dry_vs_qo_vs_fear_try.txt +14 -0
  20. data/benchmarks/pattern_matching_qo_vs_fear_pattern_extraction.txt +11 -0
  21. data/benchmarks/pattern_matching_qo_vs_fear_try_execution.txt +11 -0
  22. data/examples/pattern_extracting.rb +15 -0
  23. data/examples/pattern_matching_binary_tree_set.rb +96 -0
  24. data/examples/pattern_matching_number_in_words.rb +54 -0
  25. data/fear.gemspec +4 -2
  26. data/lib/fear.rb +21 -4
  27. data/lib/fear/either.rb +77 -59
  28. data/lib/fear/either_api.rb +21 -0
  29. data/lib/fear/empty_partial_function.rb +1 -1
  30. data/lib/fear/extractor.rb +108 -0
  31. data/lib/fear/extractor/anonymous_array_splat_matcher.rb +8 -0
  32. data/lib/fear/extractor/any_matcher.rb +15 -0
  33. data/lib/fear/extractor/array_head_matcher.rb +34 -0
  34. data/lib/fear/extractor/array_matcher.rb +38 -0
  35. data/lib/fear/extractor/array_splat_matcher.rb +14 -0
  36. data/lib/fear/extractor/empty_list_matcher.rb +18 -0
  37. data/lib/fear/extractor/extractor_matcher.rb +42 -0
  38. data/lib/fear/extractor/grammar.rb +201 -0
  39. data/lib/fear/extractor/grammar.treetop +129 -0
  40. data/lib/fear/extractor/identifier_matcher.rb +16 -0
  41. data/lib/fear/extractor/matcher.rb +54 -0
  42. data/lib/fear/extractor/matcher/and.rb +36 -0
  43. data/lib/fear/extractor/named_array_splat_matcher.rb +15 -0
  44. data/lib/fear/extractor/pattern.rb +55 -0
  45. data/lib/fear/extractor/typed_identifier_matcher.rb +24 -0
  46. data/lib/fear/extractor/value_matcher.rb +17 -0
  47. data/lib/fear/extractor_api.rb +33 -0
  48. data/lib/fear/failure.rb +32 -10
  49. data/lib/fear/for.rb +14 -69
  50. data/lib/fear/for_api.rb +66 -0
  51. data/lib/fear/future.rb +414 -0
  52. data/lib/fear/future_api.rb +19 -0
  53. data/lib/fear/left.rb +8 -0
  54. data/lib/fear/none.rb +17 -8
  55. data/lib/fear/option.rb +55 -49
  56. data/lib/fear/option_api.rb +38 -0
  57. data/lib/fear/partial_function.rb +9 -12
  58. data/lib/fear/partial_function/empty.rb +1 -1
  59. data/lib/fear/partial_function/guard.rb +8 -20
  60. data/lib/fear/partial_function/lifted.rb +1 -0
  61. data/lib/fear/partial_function_class.rb +10 -0
  62. data/lib/fear/pattern_match.rb +10 -0
  63. data/lib/fear/pattern_matching_api.rb +35 -11
  64. data/lib/fear/promise.rb +87 -0
  65. data/lib/fear/right.rb +8 -0
  66. data/lib/fear/some.rb +22 -3
  67. data/lib/fear/success.rb +22 -1
  68. data/lib/fear/try.rb +82 -67
  69. data/lib/fear/try_api.rb +31 -0
  70. data/lib/fear/unit.rb +28 -0
  71. data/lib/fear/version.rb +1 -1
  72. data/spec/fear/done_spec.rb +3 -3
  73. data/spec/fear/either/mixin_spec.rb +15 -0
  74. data/spec/fear/either_pattern_match_spec.rb +10 -12
  75. data/spec/fear/extractor/array_matcher_spec.rb +228 -0
  76. data/spec/fear/extractor/extractor_matcher_spec.rb +151 -0
  77. data/spec/fear/extractor/grammar_array_spec.rb +23 -0
  78. data/spec/fear/extractor/identified_matcher_spec.rb +47 -0
  79. data/spec/fear/extractor/identifier_matcher_spec.rb +66 -0
  80. data/spec/fear/extractor/pattern_spec.rb +32 -0
  81. data/spec/fear/extractor/typed_identifier_matcher_spec.rb +62 -0
  82. data/spec/fear/extractor/value_matcher_number_spec.rb +77 -0
  83. data/spec/fear/extractor/value_matcher_string_spec.rb +86 -0
  84. data/spec/fear/extractor/value_matcher_symbol_spec.rb +69 -0
  85. data/spec/fear/extractor_api_spec.rb +113 -0
  86. data/spec/fear/extractor_spec.rb +59 -0
  87. data/spec/fear/failure_spec.rb +73 -13
  88. data/spec/fear/for_spec.rb +35 -35
  89. data/spec/fear/future_spec.rb +466 -0
  90. data/spec/fear/guard_spec.rb +4 -4
  91. data/spec/fear/left_spec.rb +40 -14
  92. data/spec/fear/none_spec.rb +28 -12
  93. data/spec/fear/option/mixin_spec.rb +37 -0
  94. data/spec/fear/option_pattern_match_spec.rb +7 -9
  95. data/spec/fear/partial_function_spec.rb +25 -3
  96. data/spec/fear/pattern_match_spec.rb +33 -1
  97. data/spec/fear/promise_spec.rb +94 -0
  98. data/spec/fear/right_spec.rb +37 -9
  99. data/spec/fear/some_spec.rb +32 -6
  100. data/spec/fear/success_spec.rb +32 -4
  101. data/spec/fear/try/mixin_spec.rb +17 -0
  102. data/spec/fear/try_pattern_match_spec.rb +8 -10
  103. data/spec/spec_helper.rb +1 -1
  104. metadata +115 -20
  105. data/Appraisals +0 -32
  106. data/gemfiles/dry_equalizer_0.1.0.gemfile +0 -8
  107. data/gemfiles/dry_equalizer_0.1.0.gemfile.lock +0 -82
  108. data/gemfiles/dry_equalizer_0.2.1.gemfile +0 -8
  109. data/lib/fear/done.rb +0 -22
  110. data/spec/fear/option_spec.rb +0 -15
@@ -0,0 +1,23 @@
1
+ RSpec.describe Fear::Extractor::Grammar, 'Array' do
2
+ let(:parser) { Fear::Extractor::GrammarParser.new }
3
+ let(:matcher) { parser.parse(pattern).to_matcher }
4
+
5
+ context 'non empty array' do
6
+ let(:pattern) { '[1, 2, 3, 4]' }
7
+
8
+ it do
9
+ first = matcher.head
10
+ rest_after_first = matcher.tail
11
+
12
+ expect(first).to be_kind_of(Fear::Extractor::ArrayHeadMatcher)
13
+ expect(first.matcher.value).to eq(1)
14
+ expect(rest_after_first).to be_kind_of(Fear::Extractor::ArrayMatcher)
15
+
16
+ second = rest_after_first.head
17
+ rest_after_second = rest_after_first.tail
18
+ expect(second).to be_kind_of(Fear::Extractor::ArrayHeadMatcher)
19
+ expect(second.matcher.value).to eq(2)
20
+ expect(rest_after_second).to be_kind_of(Fear::Extractor::ArrayMatcher)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,47 @@
1
+ RSpec.describe 'Fear::Extractor::IdentifiedMatcher' do
2
+ let(:parser) { Fear::Extractor::GrammarParser.new }
3
+ let(:matcher) { parser.parse(pattern).to_matcher }
4
+
5
+ describe '#defined_at?' do
6
+ subject { matcher }
7
+
8
+ let(:pattern) { 'array @ [1, *tail]' }
9
+
10
+ it { is_expected.to be_defined_at([1, 2]) }
11
+ it { is_expected.not_to be_defined_at('foo') }
12
+ it { is_expected.not_to be_defined_at([2, 1]) }
13
+ end
14
+
15
+ describe '#call' do
16
+ subject { matcher.call(other) }
17
+
18
+ context 'defined' do
19
+ let(:other) { [1, 2] }
20
+ let(:pattern) { 'array @ [1, *tail]' }
21
+
22
+ it { is_expected.to eq(array: [1, 2], tail: [2]) }
23
+ end
24
+ end
25
+
26
+ describe '#failure_reason' do
27
+ subject { matcher.failure_reason(other) }
28
+
29
+ let(:pattern) { 'array @ [1, *tail]' }
30
+
31
+ context 'match integer' do
32
+ let(:other) { [1, 2] }
33
+
34
+ it { is_expected.to eq(Fear.none) }
35
+ end
36
+
37
+ context 'does not match float' do
38
+ let(:other) { [2, 2] }
39
+
40
+ it { is_expected.to eq(Fear.some(<<-ERROR.strip)) }
41
+ Expected `2` to match:
42
+ array @ [1, *tail]
43
+ ~~~~~~~~~^
44
+ ERROR
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,66 @@
1
+ RSpec.describe Fear::Extractor::IdentifierMatcher do
2
+ let(:parser) { Fear::Extractor::GrammarParser.new }
3
+ let(:matcher) { parser.parse(pattern).to_matcher }
4
+
5
+ describe '#defined_at?' do
6
+ subject { matcher }
7
+
8
+ let(:pattern) { 'number' }
9
+
10
+ it { is_expected.to be_defined_at(1) }
11
+ it { is_expected.to be_defined_at('foo') }
12
+ it { is_expected.to be_defined_at(1.2) }
13
+ it { is_expected.to be_defined_at([1, '2']) }
14
+
15
+ context 'within array' do
16
+ let(:pattern) { '[1, n, 2]' }
17
+
18
+ it { is_expected.to be_defined_at([1, 2, 2]) }
19
+ it { is_expected.to be_defined_at([1, 'foo', 2]) }
20
+ it { is_expected.not_to be_defined_at([1, 'foo']) }
21
+ end
22
+ end
23
+
24
+ describe '#call' do
25
+ subject { matcher.call(other) }
26
+
27
+ let(:pattern) { '1.0' }
28
+
29
+ context 'defined' do
30
+ let(:other) { 1 }
31
+
32
+ it { is_expected.to eq({}) }
33
+ end
34
+ end
35
+
36
+ describe '#failure_reason' do
37
+ subject { matcher.failure_reason(other) }
38
+
39
+ let(:pattern) { '1.0' }
40
+
41
+ context 'match integer' do
42
+ let(:other) { 1 }
43
+ let(:pattern) { '1' }
44
+
45
+ it { is_expected.to eq(Fear.none) }
46
+ end
47
+
48
+ context 'match float' do
49
+ let(:other) { 1.0 }
50
+ let(:pattern) { '1' }
51
+
52
+ it { is_expected.to eq(Fear.none) }
53
+ end
54
+
55
+ context 'does not match another integer' do
56
+ let(:other) { 2 }
57
+ let(:pattern) { '1' }
58
+
59
+ it { is_expected.to eq(Fear.some(<<-ERROR.strip)) }
60
+ Expected `2` to match:
61
+ 1
62
+ ^
63
+ ERROR
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,32 @@
1
+ RSpec.describe Fear::Extractor::Pattern do
2
+ describe '.new' do
3
+ context 'invalid syntax' do
4
+ subject { -> { described_class.new('[1, 2, 3') } }
5
+
6
+ it 'shows where the error happens' do
7
+ is_expected.to raise_error(Fear::PatternSyntaxError) { |error|
8
+ lines = error.message.split("\n")
9
+ expect(lines[0]).to start_with('Expected one of')
10
+ .and(end_with('at line 1, column 9 (byte 9):'))
11
+
12
+ expect(lines[1]).to eq('[1, 2, 3')
13
+ expect(lines[2]).to eq('~~~~~~~~^')
14
+ }
15
+ end
16
+ end
17
+ end
18
+
19
+ describe '#failure_reason' do
20
+ let(:pattern) { described_class.new('Some([:err, 444])') }
21
+
22
+ context 'not defined' do
23
+ subject { pattern.failure_reason(Fear.some([:err, 445])) }
24
+
25
+ it { is_expected.to eq(<<-MSG.strip) }
26
+ Expected `445` to match:
27
+ Some([:err, 444])
28
+ ~~~~~~~~~~~~^
29
+ MSG
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,62 @@
1
+ RSpec.describe Fear::Extractor::TypedIdentifierMatcher do
2
+ let(:parser) { Fear::Extractor::GrammarParser.new }
3
+ let(:matcher) { parser.parse(pattern).to_matcher }
4
+
5
+ describe '#defined_at?' do
6
+ subject { matcher }
7
+
8
+ let(:pattern) { 'var : Integer' }
9
+
10
+ it { is_expected.to be_defined_at(1) }
11
+ it { is_expected.not_to be_defined_at('foo') }
12
+ it { is_expected.not_to be_defined_at(1.2) }
13
+
14
+ context 'within array' do
15
+ let(:pattern) { '[1, n : String, 2]' }
16
+
17
+ it { is_expected.to be_defined_at([1, 'foo', 2]) }
18
+ it { is_expected.not_to be_defined_at([1, 2, 2]) }
19
+ it { is_expected.not_to be_defined_at([1, 'foo']) }
20
+ end
21
+ end
22
+
23
+ describe '#call' do
24
+ subject { matcher.call(other) }
25
+
26
+ context 'defined' do
27
+ let(:other) { 1 }
28
+ let(:pattern) { 'var : Integer' }
29
+
30
+ it { is_expected.to eq(var: 1) }
31
+ end
32
+
33
+ context 'defined within array' do
34
+ let(:other) { [4, 2, 1, 6] }
35
+ let(:pattern) { '[head : Integer, *tail]' }
36
+
37
+ it { is_expected.to eq(head: 4, tail: [2, 1, 6]) }
38
+ end
39
+ end
40
+
41
+ describe '#' do
42
+ subject { matcher.failure_reason(other) }
43
+
44
+ let(:pattern) { 'var : Integer' }
45
+
46
+ context 'match integer' do
47
+ let(:other) { 1 }
48
+
49
+ it { is_expected.to eq(Fear.none) }
50
+ end
51
+
52
+ context 'does not match float' do
53
+ let(:other) { 1.0 }
54
+
55
+ it { is_expected.to eq(Fear.some(<<-ERROR.strip)) }
56
+ Expected `1.0` to match:
57
+ var : Integer
58
+ ~~~~~~^
59
+ ERROR
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,77 @@
1
+ RSpec.describe Fear::Extractor::ValueMatcher, 'Number' do
2
+ let(:parser) { Fear::Extractor::GrammarParser.new }
3
+ let(:matcher) { parser.parse(pattern).to_matcher }
4
+
5
+ describe '#defined_at?' do
6
+ subject { matcher }
7
+
8
+ context 'Integer' do
9
+ let(:pattern) { '1' }
10
+
11
+ it { is_expected.to be_defined_at(1) }
12
+ it { is_expected.to be_defined_at(1.0) }
13
+ it { is_expected.not_to be_defined_at(2) }
14
+ it { is_expected.not_to be_defined_at('1') }
15
+ end
16
+
17
+ context 'Float' do
18
+ context 'against float' do
19
+ let(:pattern) { '1.2' }
20
+
21
+ it { is_expected.to be_defined_at(1.2) }
22
+ it { is_expected.not_to be_defined_at(1.3) }
23
+ end
24
+
25
+ context 'against integer' do
26
+ let(:pattern) { '1.0' }
27
+
28
+ it { is_expected.to be_defined_at(1) }
29
+ it { is_expected.not_to be_defined_at(2) }
30
+ it { is_expected.not_to be_defined_at('1') }
31
+ end
32
+ end
33
+ end
34
+
35
+ describe '#call' do
36
+ subject { matcher.call(other) }
37
+
38
+ let(:pattern) { '1.0' }
39
+
40
+ context 'defined' do
41
+ let(:other) { 1 }
42
+
43
+ it { is_expected.to eq({}) }
44
+ end
45
+ end
46
+
47
+ describe '#failure_reason' do
48
+ subject { matcher.failure_reason(other) }
49
+
50
+ let(:pattern) { '1.0' }
51
+
52
+ context 'match integer' do
53
+ let(:other) { 1 }
54
+ let(:pattern) { '1' }
55
+
56
+ it { is_expected.to eq(Fear.none) }
57
+ end
58
+
59
+ context 'match float' do
60
+ let(:other) { 1.0 }
61
+ let(:pattern) { '1' }
62
+
63
+ it { is_expected.to eq(Fear.none) }
64
+ end
65
+
66
+ context 'does not match another integer' do
67
+ let(:other) { 2 }
68
+ let(:pattern) { '1' }
69
+
70
+ it { is_expected.to eq(Fear.some(<<-ERROR.strip)) }
71
+ Expected `2` to match:
72
+ 1
73
+ ^
74
+ ERROR
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,86 @@
1
+ RSpec.describe Fear::Extractor::ValueMatcher, 'String' do
2
+ let(:parser) { Fear::Extractor::GrammarParser.new }
3
+ let(:matcher) { parser.parse(pattern).to_matcher }
4
+
5
+ describe '#defined_at?' do
6
+ subject { matcher }
7
+
8
+ context 'double quotas' do
9
+ let(:pattern) { %("foo") }
10
+
11
+ it { is_expected.to be_defined_at('foo') }
12
+ it { is_expected.not_to be_defined_at('boo') }
13
+ it { is_expected.not_to be_defined_at(2) }
14
+
15
+ context 'single quotes inside' do
16
+ let(:pattern) { %("f'o'o") }
17
+
18
+ it { is_expected.to be_defined_at(%(f'o'o)) }
19
+ it { is_expected.not_to be_defined_at(%(f"o"o)) }
20
+ end
21
+
22
+ context 'escaped double quotes inside' do
23
+ let(:pattern) { '"f\"oo"' }
24
+
25
+ it { is_expected.to be_defined_at('f"oo') }
26
+ it { is_expected.not_to be_defined_at("f'oo") }
27
+ end
28
+ end
29
+
30
+ context 'single quotas' do
31
+ let(:pattern) { %('foo') }
32
+
33
+ it { is_expected.to be_defined_at('foo') }
34
+ it { is_expected.not_to be_defined_at('boo') }
35
+ it { is_expected.not_to be_defined_at(2) }
36
+
37
+ context 'double quotes inside' do
38
+ let(:pattern) { %('f"o"o') }
39
+
40
+ it { is_expected.to be_defined_at(%(f"o"o)) }
41
+ it { is_expected.not_to be_defined_at(%(f'o'o)) }
42
+ end
43
+
44
+ context 'escaped single quotes inside' do
45
+ let(:pattern) { "'f\\'oo'" }
46
+
47
+ it { is_expected.to be_defined_at("f\\'oo") }
48
+ it { is_expected.not_to be_defined_at("f'oo") }
49
+ end
50
+ end
51
+ end
52
+
53
+ describe '#call' do
54
+ subject { matcher.call(other) }
55
+
56
+ let(:pattern) { '"foo"' }
57
+
58
+ context 'defined' do
59
+ let(:other) { 'foo' }
60
+
61
+ it { is_expected.to eq({}) }
62
+ end
63
+ end
64
+
65
+ describe '#failure_reason' do
66
+ subject { matcher.failure_reason(other) }
67
+
68
+ context 'match' do
69
+ let(:other) { 'foo' }
70
+ let(:pattern) { '"foo"' }
71
+
72
+ it { is_expected.to eq(Fear.none) }
73
+ end
74
+
75
+ context 'does not match' do
76
+ let(:other) { 'bar' }
77
+ let(:pattern) { '"foo"' }
78
+
79
+ it { is_expected.to eq(Fear.some(<<-ERROR.strip)) }
80
+ Expected `"bar"` to match:
81
+ "foo"
82
+ ^
83
+ ERROR
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,69 @@
1
+ RSpec.describe Fear::Extractor::ValueMatcher, 'Symbol' do
2
+ let(:parser) { Fear::Extractor::GrammarParser.new }
3
+ let(:matcher) { parser.parse(pattern).to_matcher }
4
+
5
+ describe '#defined_at?' do
6
+ subject { matcher }
7
+
8
+ context 'no quotas' do
9
+ let(:pattern) { ':foo' }
10
+
11
+ it { is_expected.to be_defined_at(:foo) }
12
+ it { is_expected.not_to be_defined_at('foo') }
13
+ it { is_expected.not_to be_defined_at(:boo) }
14
+ it { is_expected.not_to be_defined_at(2) }
15
+ end
16
+
17
+ context 'double quotas' do
18
+ let(:pattern) { %(:"foo") }
19
+
20
+ it { is_expected.to be_defined_at(:foo) }
21
+ it { is_expected.not_to be_defined_at('foo') }
22
+ it { is_expected.not_to be_defined_at(:boo) }
23
+ it { is_expected.not_to be_defined_at(2) }
24
+ end
25
+
26
+ context 'single quotas' do
27
+ let(:pattern) { %(:'foo') }
28
+
29
+ it { is_expected.to be_defined_at(:foo) }
30
+ it { is_expected.not_to be_defined_at('foo') }
31
+ it { is_expected.not_to be_defined_at(:boo) }
32
+ it { is_expected.not_to be_defined_at(2) }
33
+ end
34
+ end
35
+
36
+ describe '#call' do
37
+ subject { matcher.call(other) }
38
+
39
+ let(:pattern) { ':foo' }
40
+
41
+ context 'defined' do
42
+ let(:other) { :foo }
43
+
44
+ it { is_expected.to eq({}) }
45
+ end
46
+ end
47
+
48
+ describe '#failure_reason' do
49
+ subject { matcher.failure_reason(other) }
50
+
51
+ context 'match' do
52
+ let(:other) { :foo }
53
+ let(:pattern) { ':foo' }
54
+
55
+ it { is_expected.to eq(Fear.none) }
56
+ end
57
+
58
+ context 'does not match' do
59
+ let(:other) { :bar }
60
+ let(:pattern) { ':foo' }
61
+
62
+ it { is_expected.to eq(Fear.some(<<-ERROR.strip)) }
63
+ Expected `:bar` to match:
64
+ :foo
65
+ ^
66
+ ERROR
67
+ end
68
+ end
69
+ end