fear 0.9.0 → 1.2.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 (155) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/rubocop.yml +39 -0
  3. data/.github/workflows/spec.yml +42 -0
  4. data/.gitignore +0 -1
  5. data/.rubocop.yml +4 -12
  6. data/.simplecov +17 -0
  7. data/CHANGELOG.md +40 -0
  8. data/Gemfile +5 -2
  9. data/Gemfile.lock +130 -0
  10. data/LICENSE.txt +1 -1
  11. data/README.md +1293 -97
  12. data/Rakefile +369 -1
  13. data/benchmarks/README.md +1 -0
  14. data/benchmarks/dry_do_vs_fear_for.txt +11 -0
  15. data/benchmarks/dry_some_fmap_vs_fear_some_map.txt +11 -0
  16. data/benchmarks/factorial.txt +16 -0
  17. data/benchmarks/fear_gaurd_and1_vs_new.txt +13 -0
  18. data/benchmarks/fear_gaurd_and2_vs_and.txt +13 -0
  19. data/benchmarks/fear_gaurd_and3_vs_and_and.txt +13 -0
  20. data/benchmarks/fear_pattern_extracting_with_vs_without_cache.txt +11 -0
  21. data/benchmarks/fear_pattern_matching_construction_vs_execution.txt +13 -0
  22. data/benchmarks/pattern_matching_dry_vs_qo_vs_fear_try.txt +14 -0
  23. data/benchmarks/pattern_matching_qo_vs_fear_pattern_extraction.txt +11 -0
  24. data/benchmarks/pattern_matching_qo_vs_fear_try_execution.txt +11 -0
  25. data/examples/pattern_extracting.rb +17 -0
  26. data/examples/pattern_extracting_ruby2.7.rb +15 -0
  27. data/examples/pattern_matching_binary_tree_set.rb +101 -0
  28. data/examples/pattern_matching_number_in_words.rb +60 -0
  29. data/fear.gemspec +34 -23
  30. data/lib/dry/types/fear.rb +8 -0
  31. data/lib/dry/types/fear/option.rb +125 -0
  32. data/lib/fear.rb +65 -15
  33. data/lib/fear/await.rb +33 -0
  34. data/lib/fear/awaitable.rb +28 -0
  35. data/lib/fear/either.rb +131 -71
  36. data/lib/fear/either_api.rb +23 -0
  37. data/lib/fear/either_pattern_match.rb +53 -0
  38. data/lib/fear/empty_partial_function.rb +38 -0
  39. data/lib/fear/extractor.rb +112 -0
  40. data/lib/fear/extractor/anonymous_array_splat_matcher.rb +10 -0
  41. data/lib/fear/extractor/any_matcher.rb +17 -0
  42. data/lib/fear/extractor/array_head_matcher.rb +36 -0
  43. data/lib/fear/extractor/array_matcher.rb +40 -0
  44. data/lib/fear/extractor/array_splat_matcher.rb +16 -0
  45. data/lib/fear/extractor/empty_list_matcher.rb +20 -0
  46. data/lib/fear/extractor/extractor_matcher.rb +44 -0
  47. data/lib/fear/extractor/grammar.rb +203 -0
  48. data/lib/fear/extractor/grammar.treetop +129 -0
  49. data/lib/fear/extractor/identifier_matcher.rb +18 -0
  50. data/lib/fear/extractor/matcher.rb +53 -0
  51. data/lib/fear/extractor/matcher/and.rb +38 -0
  52. data/lib/fear/extractor/named_array_splat_matcher.rb +17 -0
  53. data/lib/fear/extractor/pattern.rb +58 -0
  54. data/lib/fear/extractor/typed_identifier_matcher.rb +26 -0
  55. data/lib/fear/extractor/value_matcher.rb +19 -0
  56. data/lib/fear/extractor_api.rb +35 -0
  57. data/lib/fear/failure.rb +46 -14
  58. data/lib/fear/failure_pattern_match.rb +10 -0
  59. data/lib/fear/for.rb +37 -95
  60. data/lib/fear/for_api.rb +68 -0
  61. data/lib/fear/future.rb +497 -0
  62. data/lib/fear/future_api.rb +21 -0
  63. data/lib/fear/left.rb +19 -2
  64. data/lib/fear/left_pattern_match.rb +11 -0
  65. data/lib/fear/none.rb +67 -3
  66. data/lib/fear/none_pattern_match.rb +14 -0
  67. data/lib/fear/option.rb +120 -56
  68. data/lib/fear/option_api.rb +40 -0
  69. data/lib/fear/option_pattern_match.rb +48 -0
  70. data/lib/fear/partial_function.rb +176 -0
  71. data/lib/fear/partial_function/and_then.rb +50 -0
  72. data/lib/fear/partial_function/any.rb +28 -0
  73. data/lib/fear/partial_function/combined.rb +53 -0
  74. data/lib/fear/partial_function/empty.rb +10 -0
  75. data/lib/fear/partial_function/guard.rb +80 -0
  76. data/lib/fear/partial_function/guard/and.rb +38 -0
  77. data/lib/fear/partial_function/guard/and3.rb +41 -0
  78. data/lib/fear/partial_function/guard/or.rb +38 -0
  79. data/lib/fear/partial_function/lifted.rb +23 -0
  80. data/lib/fear/partial_function/or_else.rb +64 -0
  81. data/lib/fear/partial_function_class.rb +38 -0
  82. data/lib/fear/pattern_match.rb +114 -0
  83. data/lib/fear/pattern_matching_api.rb +137 -0
  84. data/lib/fear/promise.rb +95 -0
  85. data/lib/fear/right.rb +20 -2
  86. data/lib/fear/right_biased.rb +6 -14
  87. data/lib/fear/right_pattern_match.rb +11 -0
  88. data/lib/fear/some.rb +55 -3
  89. data/lib/fear/some_pattern_match.rb +13 -0
  90. data/lib/fear/struct.rb +248 -0
  91. data/lib/fear/success.rb +35 -5
  92. data/lib/fear/success_pattern_match.rb +12 -0
  93. data/lib/fear/try.rb +136 -79
  94. data/lib/fear/try_api.rb +33 -0
  95. data/lib/fear/try_pattern_match.rb +33 -0
  96. data/lib/fear/unit.rb +32 -0
  97. data/lib/fear/utils.rb +39 -14
  98. data/lib/fear/version.rb +4 -1
  99. data/spec/dry/types/fear/option/constrained_spec.rb +22 -0
  100. data/spec/dry/types/fear/option/core_spec.rb +77 -0
  101. data/spec/dry/types/fear/option/default_spec.rb +21 -0
  102. data/spec/dry/types/fear/option/hash_spec.rb +58 -0
  103. data/spec/dry/types/fear/option/option_spec.rb +97 -0
  104. data/spec/fear/awaitable_spec.rb +17 -0
  105. data/spec/fear/done_spec.rb +8 -6
  106. data/spec/fear/either/mixin_spec.rb +17 -0
  107. data/spec/fear/either_pattern_match_spec.rb +37 -0
  108. data/spec/fear/either_pattern_matching_spec.rb +28 -0
  109. data/spec/fear/extractor/array_matcher_spec.rb +230 -0
  110. data/spec/fear/extractor/extractor_matcher_spec.rb +153 -0
  111. data/spec/fear/extractor/grammar_array_spec.rb +25 -0
  112. data/spec/fear/extractor/identified_matcher_spec.rb +49 -0
  113. data/spec/fear/extractor/identifier_matcher_spec.rb +68 -0
  114. data/spec/fear/extractor/pattern_spec.rb +34 -0
  115. data/spec/fear/extractor/typed_identifier_matcher_spec.rb +64 -0
  116. data/spec/fear/extractor/value_matcher_number_spec.rb +79 -0
  117. data/spec/fear/extractor/value_matcher_string_spec.rb +88 -0
  118. data/spec/fear/extractor/value_matcher_symbol_spec.rb +71 -0
  119. data/spec/fear/extractor_api_spec.rb +115 -0
  120. data/spec/fear/extractor_spec.rb +61 -0
  121. data/spec/fear/failure_spec.rb +145 -45
  122. data/spec/fear/for_spec.rb +57 -67
  123. data/spec/fear/future_spec.rb +691 -0
  124. data/spec/fear/guard_spec.rb +103 -0
  125. data/spec/fear/left_spec.rb +112 -46
  126. data/spec/fear/none_spec.rb +114 -16
  127. data/spec/fear/option/mixin_spec.rb +39 -0
  128. data/spec/fear/option_pattern_match_spec.rb +35 -0
  129. data/spec/fear/option_pattern_matching_spec.rb +34 -0
  130. data/spec/fear/option_spec.rb +121 -8
  131. data/spec/fear/partial_function/empty_spec.rb +38 -0
  132. data/spec/fear/partial_function_and_then_spec.rb +147 -0
  133. data/spec/fear/partial_function_composition_spec.rb +82 -0
  134. data/spec/fear/partial_function_or_else_spec.rb +276 -0
  135. data/spec/fear/partial_function_spec.rb +239 -0
  136. data/spec/fear/pattern_match_spec.rb +93 -0
  137. data/spec/fear/pattern_matching_api_spec.rb +31 -0
  138. data/spec/fear/promise_spec.rb +96 -0
  139. data/spec/fear/right_biased/left.rb +29 -32
  140. data/spec/fear/right_biased/right.rb +51 -54
  141. data/spec/fear/right_spec.rb +109 -41
  142. data/spec/fear/some_spec.rb +80 -15
  143. data/spec/fear/success_spec.rb +99 -32
  144. data/spec/fear/try/mixin_spec.rb +19 -0
  145. data/spec/fear/try_pattern_match_spec.rb +37 -0
  146. data/spec/fear/try_pattern_matching_spec.rb +34 -0
  147. data/spec/fear/utils_spec.rb +16 -14
  148. data/spec/spec_helper.rb +13 -7
  149. data/spec/struct_pattern_matching_spec.rb +36 -0
  150. data/spec/struct_spec.rb +226 -0
  151. data/spec/support/dry_types.rb +6 -0
  152. metadata +320 -29
  153. data/.travis.yml +0 -9
  154. data/lib/fear/done.rb +0 -22
  155. data/lib/fear/for/evaluation_context.rb +0 -91
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Fear::Extractor::Grammar, "Array" do
4
+ let(:parser) { Fear::Extractor::GrammarParser.new }
5
+ let(:matcher) { parser.parse(pattern).to_matcher }
6
+
7
+ context "non empty array" do
8
+ let(:pattern) { "[1, 2, 3, 4]" }
9
+
10
+ it do
11
+ first = matcher.head
12
+ rest_after_first = matcher.tail
13
+
14
+ expect(first).to be_kind_of(Fear::Extractor::ArrayHeadMatcher)
15
+ expect(first.matcher.value).to eq(1)
16
+ expect(rest_after_first).to be_kind_of(Fear::Extractor::ArrayMatcher)
17
+
18
+ second = rest_after_first.head
19
+ rest_after_second = rest_after_first.tail
20
+ expect(second).to be_kind_of(Fear::Extractor::ArrayHeadMatcher)
21
+ expect(second.matcher.value).to eq(2)
22
+ expect(rest_after_second).to be_kind_of(Fear::Extractor::ArrayMatcher)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe "Fear::Extractor::IdentifiedMatcher" 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
+ let(:pattern) { "array @ [1, *tail]" }
11
+
12
+ it { is_expected.to be_defined_at([1, 2]) }
13
+ it { is_expected.not_to be_defined_at("foo") }
14
+ it { is_expected.not_to be_defined_at([2, 1]) }
15
+ end
16
+
17
+ describe "#call" do
18
+ subject { matcher.(other) }
19
+
20
+ context "defined" do
21
+ let(:other) { [1, 2] }
22
+ let(:pattern) { "array @ [1, *tail]" }
23
+
24
+ it { is_expected.to eq(array: [1, 2], tail: [2]) }
25
+ end
26
+ end
27
+
28
+ describe "#failure_reason" do
29
+ subject { matcher.failure_reason(other) }
30
+
31
+ let(:pattern) { "array @ [1, *tail]" }
32
+
33
+ context "match integer" do
34
+ let(:other) { [1, 2] }
35
+
36
+ it { is_expected.to eq(Fear.none) }
37
+ end
38
+
39
+ context "does not match float" do
40
+ let(:other) { [2, 2] }
41
+
42
+ it { is_expected.to eq(Fear.some(<<~ERROR.strip)) }
43
+ Expected `2` to match:
44
+ array @ [1, *tail]
45
+ ~~~~~~~~~^
46
+ ERROR
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Fear::Extractor::IdentifierMatcher 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
+ let(:pattern) { "number" }
11
+
12
+ it { is_expected.to be_defined_at(1) }
13
+ it { is_expected.to be_defined_at("foo") }
14
+ it { is_expected.to be_defined_at(1.2) }
15
+ it { is_expected.to be_defined_at([1, "2"]) }
16
+
17
+ context "within array" do
18
+ let(:pattern) { "[1, n, 2]" }
19
+
20
+ it { is_expected.to be_defined_at([1, 2, 2]) }
21
+ it { is_expected.to be_defined_at([1, "foo", 2]) }
22
+ it { is_expected.not_to be_defined_at([1, "foo"]) }
23
+ end
24
+ end
25
+
26
+ describe "#call" do
27
+ subject { matcher.(other) }
28
+
29
+ let(:pattern) { "1.0" }
30
+
31
+ context "defined" do
32
+ let(:other) { 1 }
33
+
34
+ it { is_expected.to eq({}) }
35
+ end
36
+ end
37
+
38
+ describe "#failure_reason" do
39
+ subject { matcher.failure_reason(other) }
40
+
41
+ let(:pattern) { "1.0" }
42
+
43
+ context "match integer" do
44
+ let(:other) { 1 }
45
+ let(:pattern) { "1" }
46
+
47
+ it { is_expected.to eq(Fear.none) }
48
+ end
49
+
50
+ context "match float" do
51
+ let(:other) { 1.0 }
52
+ let(:pattern) { "1" }
53
+
54
+ it { is_expected.to eq(Fear.none) }
55
+ end
56
+
57
+ context "does not match another integer" do
58
+ let(:other) { 2 }
59
+ let(:pattern) { "1" }
60
+
61
+ it { is_expected.to eq(Fear.some(<<~ERROR.strip)) }
62
+ Expected `2` to match:
63
+ 1
64
+ ^
65
+ ERROR
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Fear::Extractor::Pattern do
4
+ describe ".new" do
5
+ context "invalid syntax" do
6
+ subject { -> { described_class.new("[1, 2, 3") } }
7
+
8
+ it "shows where the error happens" do
9
+ is_expected.to raise_error(Fear::PatternSyntaxError) { |error|
10
+ lines = error.message.split("\n")
11
+ expect(lines[0]).to start_with("Expected one of")
12
+ .and(end_with("at line 1, column 9 (byte 9):"))
13
+
14
+ expect(lines[1]).to eq("[1, 2, 3")
15
+ expect(lines[2]).to eq("~~~~~~~~^")
16
+ }
17
+ end
18
+ end
19
+ end
20
+
21
+ describe "#failure_reason" do
22
+ let(:pattern) { described_class.new("Some([:err, 444])") }
23
+
24
+ context "not defined" do
25
+ subject { pattern.failure_reason(Fear.some([:err, 445])) }
26
+
27
+ it { is_expected.to eq(<<~MSG.strip) }
28
+ Expected `445` to match:
29
+ Some([:err, 444])
30
+ ~~~~~~~~~~~~^
31
+ MSG
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Fear::Extractor::TypedIdentifierMatcher 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
+ let(:pattern) { "var : Integer" }
11
+
12
+ it { is_expected.to be_defined_at(1) }
13
+ it { is_expected.not_to be_defined_at("foo") }
14
+ it { is_expected.not_to be_defined_at(1.2) }
15
+
16
+ context "within array" do
17
+ let(:pattern) { "[1, n : String, 2]" }
18
+
19
+ it { is_expected.to be_defined_at([1, "foo", 2]) }
20
+ it { is_expected.not_to be_defined_at([1, 2, 2]) }
21
+ it { is_expected.not_to be_defined_at([1, "foo"]) }
22
+ end
23
+ end
24
+
25
+ describe "#call" do
26
+ subject { matcher.(other) }
27
+
28
+ context "defined" do
29
+ let(:other) { 1 }
30
+ let(:pattern) { "var : Integer" }
31
+
32
+ it { is_expected.to eq(var: 1) }
33
+ end
34
+
35
+ context "defined within array" do
36
+ let(:other) { [4, 2, 1, 6] }
37
+ let(:pattern) { "[head : Integer, *tail]" }
38
+
39
+ it { is_expected.to eq(head: 4, tail: [2, 1, 6]) }
40
+ end
41
+ end
42
+
43
+ describe "#" do
44
+ subject { matcher.failure_reason(other) }
45
+
46
+ let(:pattern) { "var : Integer" }
47
+
48
+ context "match integer" do
49
+ let(:other) { 1 }
50
+
51
+ it { is_expected.to eq(Fear.none) }
52
+ end
53
+
54
+ context "does not match float" do
55
+ let(:other) { 1.0 }
56
+
57
+ it { is_expected.to eq(Fear.some(<<~ERROR.strip)) }
58
+ Expected `1.0` to match:
59
+ var : Integer
60
+ ~~~~~~^
61
+ ERROR
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,79 @@
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
@@ -0,0 +1,88 @@
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
@@ -0,0 +1,71 @@
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