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
@@ -1,8 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fear
2
4
  class Success
3
5
  include Try
4
- include Dry::Equalizer(:value)
5
6
  include RightBiased::Right
7
+ include SuccessPatternMatch.mixin
8
+
9
+ EXTRACTOR = proc do |try|
10
+ if Fear::Success === try
11
+ Fear.some([try.get])
12
+ else
13
+ Fear.none
14
+ end
15
+ end
16
+ public_constant :EXTRACTOR
6
17
 
7
18
  attr_reader :value
8
19
  protected :value
@@ -48,9 +59,9 @@ module Fear
48
59
  if yield(value)
49
60
  self
50
61
  else
51
- fail NoSuchElementError, "Predicate does not hold for `#{value}`"
62
+ raise NoSuchElementError, "Predicate does not hold for `#{value}`"
52
63
  end
53
- rescue => error
64
+ rescue StandardError => error
54
65
  Failure.new(error)
55
66
  end
56
67
 
@@ -67,14 +78,14 @@ module Fear
67
78
  # @return [Try]
68
79
  def map
69
80
  super
70
- rescue => error
81
+ rescue StandardError => error
71
82
  Failure.new(error)
72
83
  end
73
84
 
74
85
  # @return [Try]
75
86
  def flat_map
76
87
  super
77
- rescue => error
88
+ rescue StandardError => error
78
89
  Failure.new(error)
79
90
  end
80
91
 
@@ -82,5 +93,24 @@ module Fear
82
93
  def to_either
83
94
  Right.new(value)
84
95
  end
96
+
97
+ # @param other [Any]
98
+ # @return [Boolean]
99
+ def ==(other)
100
+ other.is_a?(Success) && value == other.value
101
+ end
102
+
103
+ # @return [String]
104
+ def inspect
105
+ "#<Fear::Success value=#{value.inspect}>"
106
+ end
107
+
108
+ # @return [String]
109
+ alias to_s inspect
110
+
111
+ # @return [<any>]
112
+ def deconstruct
113
+ [value]
114
+ end
85
115
  end
86
116
  end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fear
4
+ # @api private
5
+ class SuccessPatternMatch < Fear::TryPatternMatch
6
+ # @param conditions [<#==>]
7
+ # @return [Fear::TryPatternMatch]
8
+ def failure(*_conditions)
9
+ self
10
+ end
11
+ end
12
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fear
2
4
  # The +Try+ represents a computation that may either result
3
5
  # in an exception, or return a successfully computed value. Instances of +Try+,
@@ -11,15 +13,23 @@ module Fear
11
13
  # @example
12
14
  # include Fear::Try::Mixin
13
15
  #
14
- # dividend = Try { Integer(params[:dividend]) }
15
- # divisor = Try { Integer(params[:divisor]) }
16
+ # dividend = Fear.try { Integer(params[:dividend]) }
17
+ # divisor = Fear.try { Integer(params[:divisor]) }
16
18
  # problem = dividend.flat_map { |x| divisor.map { |y| x / y } }
17
19
  #
18
- # if problem.success?
19
- # puts "Result of #{dividend.get} / #{divisor.get} is: #{problem.get}"
20
- # else
21
- # puts "You must've divided by zero or entered something wrong. Try again"
22
- # puts "Info from the exception: #{problem.exception.message}"
20
+ # problem.match |m|
21
+ # m.success do |result|
22
+ # puts "Result of #{dividend.get} / #{divisor.get} is: #{result}"
23
+ # end
24
+ #
25
+ # m.failure(ZeroDivisionError) do
26
+ # puts "Division by zero is not allowed"
27
+ # end
28
+ #
29
+ # m.failure do |exception|
30
+ # puts "You entered something wrong. Try again"
31
+ # puts "Info from the exception: #{exception.message}"
32
+ # end
23
33
  # end
24
34
  #
25
35
  # An important property of +Try+ shown in the above example is its
@@ -45,13 +55,13 @@ module Fear
45
55
  # @yieldreturn [any]
46
56
  # @return [any]
47
57
  # @example
48
- # Success(42).get_or_else { 24/2 } #=> 42
49
- # Failure(ArgumentError.new).get_or_else { 24/2 } #=> 12
58
+ # Fear.success(42).get_or_else { 24/2 } #=> 42
59
+ # Fear.failure(ArgumentError.new).get_or_else { 24/2 } #=> 12
50
60
  # @overload get_or_else(default)
51
61
  # @return [any]
52
62
  # @example
53
- # Success(42).get_or_else(12) #=> 42
54
- # Failure(ArgumentError.new).get_or_else(12) #=> 12
63
+ # Fear.success(42).get_or_else(12) #=> 42
64
+ # Fear.failure(ArgumentError.new).get_or_else(12) #=> 12
55
65
  #
56
66
  # @!method include?(other_value)
57
67
  # Returns +true+ if it has an element that is equal
@@ -59,9 +69,9 @@ module Fear
59
69
  # @param [any]
60
70
  # @return [Boolean]
61
71
  # @example
62
- # Success(17).include?(17) #=> true
63
- # Success(17).include?(7) #=> false
64
- # Failure(ArgumentError.new).include?(17) #=> false
72
+ # Fear.success(17).include?(17) #=> true
73
+ # Fear.success(17).include?(7) #=> false
74
+ # Fear.failure(ArgumentError.new).include?(17) #=> false
65
75
  #
66
76
  # @!method each(&block)
67
77
  # Performs the given block if this is a +Success+.
@@ -70,11 +80,11 @@ module Fear
70
80
  # @yieldreturn [void]
71
81
  # @return [Try] itself
72
82
  # @example
73
- # Success(17).each do |value|
83
+ # Fear.success(17).each do |value|
74
84
  # puts value
75
85
  # end #=> prints 17
76
86
  #
77
- # Failure(ArgumentError.new).each do |value|
87
+ # Fear.failure(ArgumentError.new).each do |value|
78
88
  # puts value
79
89
  # end #=> does nothing
80
90
  #
@@ -84,8 +94,8 @@ module Fear
84
94
  # @yieldparam [any] value
85
95
  # @yieldreturn [any]
86
96
  # @example
87
- # Success(42).map { |v| v/2 } #=> Success(21)
88
- # Failure(ArgumentError.new).map { |v| v/2 } #=> Failure(ArgumentError.new)
97
+ # Fear.success(42).map { |v| v/2 } #=> Fear.success(21)
98
+ # Fear.failure(ArgumentError.new).map { |v| v/2 } #=> Fear.failure(ArgumentError.new)
89
99
  #
90
100
  # @!method flat_map(&block)
91
101
  # Returns the given block applied to the value from this +Success+
@@ -94,26 +104,18 @@ module Fear
94
104
  # @yieldreturn [Try]
95
105
  # @return [Try]
96
106
  # @example
97
- # Success(42).flat_map { |v| Success(v/2) }
98
- # #=> Success(21)
99
- # Failure(ArgumentError.new).flat_map { |v| Success(v/2) }
100
- # #=> Failure(ArgumentError.new)
101
- #
102
- # @!method to_a
103
- # Returns an +Array+ containing the +Success+ value or an
104
- # empty +Array+ if this is a +Failure+.
105
- # @return [Array]
106
- # @example
107
- # Success(42).to_a #=> [21]
108
- # Failure(ArgumentError.new).to_a #=> []
107
+ # Fear.success(42).flat_map { |v| Fear.success(v/2) }
108
+ # #=> Fear.success(21)
109
+ # Fear.failure(ArgumentError.new).flat_map { |v| Fear.success(v/2) }
110
+ # #=> Fear.failure(ArgumentError.new)
109
111
  #
110
112
  # @!method to_option
111
113
  # Returns an +Some+ containing the +Success+ value or a +None+ if
112
114
  # this is a +Failure+.
113
115
  # @return [Option]
114
116
  # @example
115
- # Success(42).to_option #=> Some(21)
116
- # Failure(ArgumentError.new).to_option #=> None()
117
+ # Fear.success(42).to_option #=> Fear.some(21)
118
+ # Fear.failure(ArgumentError.new).to_option #=> Fear.none()
117
119
  #
118
120
  # @!method any?(&predicate)
119
121
  # Returns +false+ if +Failure+ or returns the result of the
@@ -122,9 +124,9 @@ module Fear
122
124
  # @yieldreturn [Boolean]
123
125
  # @return [Boolean]
124
126
  # @example
125
- # Success(12).any?( |v| v > 10) #=> true
126
- # Success(7).any?( |v| v > 10) #=> false
127
- # Failure(ArgumentError.new).any?( |v| v > 10) #=> false
127
+ # Fear.success(12).any?( |v| v > 10) #=> true
128
+ # Fear.success(7).any?( |v| v > 10) #=> false
129
+ # Fear.failure(ArgumentError.new).any?( |v| v > 10) #=> false
128
130
  #
129
131
  # ---
130
132
  #
@@ -141,26 +143,27 @@ module Fear
141
143
  # if this is a +Failure+.
142
144
  # @return [any]
143
145
  # @example
144
- # Success(42).get #=> 42
145
- # Failure(ArgumentError.new).get #=> ArgumentError: ArgumentError
146
+ # Fear.success(42).get #=> 42
147
+ # Fear.failure(ArgumentError.new).get #=> ArgumentError: ArgumentError
146
148
  #
147
149
  # @!method or_else(&alternative)
148
150
  # Returns this +Try+ if it's a +Success+ or the given alternative if this is a +Failure+.
149
151
  # @return [Try]
150
152
  # @example
151
- # Success(42).or_else { Success(-1) } #=> Success(42)
152
- # Failure(ArgumentError.new).or_else { Success(-1) } #=> Success(-1)
153
- # Failure(ArgumentError.new).or_else { Try { 1/0 } } #=> Failure(ZeroDivisionError.new('divided by 0'))
153
+ # Fear.success(42).or_else { Fear.success(-1) } #=> Fear.success(42)
154
+ # Fear.failure(ArgumentError.new).or_else { Fear.success(-1) } #=> Fear.success(-1)
155
+ # Fear.failure(ArgumentError.new).or_else { Fear.try { 1/0 } }
156
+ # #=> Fear.failure(ZeroDivisionError.new('divided by 0'))
154
157
  #
155
158
  # @!method flatten
156
159
  # Transforms a nested +Try+, ie, a +Success+ of +Success+,
157
160
  # into an un-nested +Try+, ie, a +Success+.
158
161
  # @return [Try]
159
162
  # @example
160
- # Success(42).flatten #=> Success(42)
161
- # Success(Success(42)).flatten #=> Success(42)
162
- # Success(Failure(ArgumentError.new)).flatten #=> Failure(ArgumentError.new)
163
- # Failure(ArgumentError.new).flatten { -1 } #=> Failure(ArgumentError.new)
163
+ # Fear.success(42).flatten #=> Fear.success(42)
164
+ # Fear.success(Fear.success(42)).flatten #=> Fear.success(42)
165
+ # Fear.success(Fear.failure(ArgumentError.new)).flatten #=> Fear.failure(ArgumentError.new)
166
+ # Fear.failure(ArgumentError.new).flatten { -1 } #=> Fear.failure(ArgumentError.new)
164
167
  #
165
168
  # @!method select(&predicate)
166
169
  # Converts this to a +Failure+ if the predicate is not satisfied.
@@ -168,47 +171,80 @@ module Fear
168
171
  # @yieldreturn [Boolean]
169
172
  # @return [Try]
170
173
  # @example
171
- # Success(42).select { |v| v > 40 }
172
- # #=> Success(21)
173
- # Success(42).select { |v| v < 40 }
174
- # #=> Failure(Fear::NoSuchElementError.new("Predicate does not hold for 42"))
175
- # Failure(ArgumentError.new).select { |v| v < 40 }
176
- # #=> Failure(ArgumentError.new)
174
+ # Fear.success(42).select { |v| v > 40 }
175
+ # #=> Fear.success(21)
176
+ # Fear.success(42).select { |v| v < 40 }
177
+ # #=> Fear.failure(Fear::NoSuchElementError.new("Predicate does not hold for 42"))
178
+ # Fear.failure(ArgumentError.new).select { |v| v < 40 }
179
+ # #=> Fear.failure(ArgumentError.new)
177
180
  #
178
181
  # @!method recover_with(&block)
179
182
  # Applies the given block to exception. This is like +flat_map+
180
183
  # for the exception.
181
- # @yieldparam [Exception] exception
182
- # @yieldreturn [Try]
183
- # @return [Try]
184
+ # @yieldparam [Fear::PatternMatch] matcher
185
+ # @yieldreturn [Fear::Try]
186
+ # @return [Fear::Try]
184
187
  # @example
185
- # Success(42).recover_with { |e| Success(e.massage) }
186
- # #=> Success(42)
187
- # Failure(ArgumentError.new).recover_with { |e| Success(e.massage) }
188
- # #=> Success('ArgumentError')
189
- # Failure(ArgumentError.new).recover_with { |e| fail }
190
- # #=> Failure(RuntimeError)
188
+ # Fear.success(42).recover_with do |m|
189
+ # m.case(ZeroDivisionError) { Fear.success(0) }
190
+ # end #=> Fear.success(42)
191
+ #
192
+ # Fear.failure(ArgumentError.new).recover_with do |m|
193
+ # m.case(ZeroDivisionError) { Fear.success(0) }
194
+ # m.case(ArgumentError) { |error| Fear.success(error.class.name) }
195
+ # end #=> Fear.success('ArgumentError')
196
+ #
197
+ # # If the block raises error, this new error returned as an result
198
+ #
199
+ # Fear.failure(ArgumentError.new).recover_with do |m|
200
+ # raise
201
+ # end #=> Fear.failure(RuntimeError)
191
202
  #
192
203
  # @!method recover(&block)
193
204
  # Applies the given block to exception. This is like +map+ for the exception.
194
- # @yieldparam [Exception] exception
205
+ # @yieldparam [Fear::PatternMatch] matcher
195
206
  # @yieldreturn [any]
196
- # @return [Try]
207
+ # @return [Fear::Try]
197
208
  # @example #recover
198
- # Success(42).recover { |e| e.massage }
199
- # #=> Success(42)
200
- # Failure(ArgumentError.new).recover { |e| e.massage }
201
- # #=> Success('ArgumentError')
202
- # Failure(ArgumentError.new).recover { |e| fail }
203
- # #=> Failure(RuntimeError)
209
+ # Fear.success(42).recover do |m|
210
+ # m.case(&:message)
211
+ # end #=> Fear.success(42)
212
+ #
213
+ # Fear.failure(ArgumentError.new).recover do |m|
214
+ # m.case(ZeroDivisionError) { 0 }
215
+ # m.case(&:message)
216
+ # end #=> Fear.success('ArgumentError')
217
+ #
218
+ # # If the block raises error, this new error returned as an result
219
+ #
220
+ # Fear.failure(ArgumentError.new).recover do |m|
221
+ # raise
222
+ # end #=> Fear.failure(RuntimeError)
204
223
  #
205
224
  # @!method to_either
206
225
  # Returns +Left+ with exception if this is a +Failure+, otherwise
207
226
  # returns +Right+ with +Success+ value.
208
227
  # @return [Right<any>, Left<StandardError>]
209
228
  # @example
210
- # Success(42).to_either #=> Right(42)
211
- # Failure(ArgumentError.new).to_either #=> Left(ArgumentError.new)
229
+ # Fear.success(42).to_either #=> Fear.right(42)
230
+ # Fear.failure(ArgumentError.new).to_either #=> Fear.left(ArgumentError.new)
231
+ #
232
+ # @!method match(&matcher)
233
+ # Pattern match against this +Try+
234
+ # @yield matcher [Fear::TryPatternMatch]
235
+ # @example
236
+ # Fear.try { ... }.match do |m|
237
+ # m.success(Integer) do |x|
238
+ # x * 2
239
+ # end
240
+ #
241
+ # m.success(String) do |x|
242
+ # x.to_i * 2
243
+ # end
244
+ #
245
+ # m.failure(ZeroDivisionError) { 'not allowed to divide by 0' }
246
+ # m.else { 'something unexpected' }
247
+ # end
212
248
  #
213
249
  # @author based on Twitter's original implementation.
214
250
  # @see https://github.com/scala/scala/blob/2.11.x/src/library/scala/util/Try.scala
@@ -224,38 +260,59 @@ module Fear
224
260
  Success
225
261
  end
226
262
 
263
+ class << self
264
+ # Build pattern matcher to be used later, despite off
265
+ # +Try#match+ method, id doesn't apply matcher immanently,
266
+ # but build it instead. Unusually in sake of efficiency it's better
267
+ # to statically build matcher and reuse it later.
268
+ #
269
+ # @example
270
+ # matcher =
271
+ # Try.matcher do |m|
272
+ # m.success(Integer, ->(x) { x > 2 }) { |x| x * 2 }
273
+ # m.success(String) { |x| x.to_i * 2 }
274
+ # m.failure(ActiveRecord::RecordNotFound) { :err }
275
+ # m.else { 'error '}
276
+ # end
277
+ # matcher.call(try)
278
+ #
279
+ # @yieldparam [Fear::TryPatternMatch]
280
+ # @return [Fear::PartialFunction]
281
+ def matcher(&matcher)
282
+ TryPatternMatch.new(&matcher)
283
+ end
284
+ end
285
+
227
286
  # Include this mixin to access convenient factory methods.
228
287
  # @example
229
288
  # include Fear::Try::Mixin
230
289
  #
231
- # Try { 4/2 } #=> #<Fear::Success value=2>
232
- # Try { 4/0 } #=> #<Fear::Failure value=#<ZeroDivisionError: divided by 0>>
233
- # Success(2) #=> #<Fear::Success value=2>
290
+ # Fear.try { 4/2 } #=> #<Fear::Success value=2>
291
+ # Fear.try { 4/0 } #=> #<Fear::Failure exception=#<ZeroDivisionError: divided by 0>>
292
+ # Fear.success(2) #=> #<Fear::Success value=2>
234
293
  #
235
294
  module Mixin
236
295
  # Constructs a +Try+ using the block. This
237
- # method will ensure any non-fatal exception )is caught and a
296
+ # method ensures any non-fatal exception is caught and a
238
297
  # +Failure+ object is returned.
239
298
  # @return [Try]
240
299
  #
241
- def Try
242
- Success.new(yield)
243
- rescue => error
244
- Failure.new(error)
300
+ def Try(&block)
301
+ Fear.try(&block)
245
302
  end
246
303
 
247
304
  # @param exception [StandardError]
248
305
  # @return [Failure]
249
306
  #
250
307
  def Failure(exception)
251
- Failure.new(exception)
308
+ Fear.failure(exception)
252
309
  end
253
310
 
254
311
  # @param value [any]
255
312
  # @return [Success]
256
313
  #
257
314
  def Success(value)
258
- Success.new(value)
315
+ Fear.success(value)
259
316
  end
260
317
  end
261
318
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fear
4
+ module TryApi
5
+ # Constructs a +Try+ using the block. This
6
+ # method ensures any non-fatal exception is caught and a
7
+ # +Failure+ object is returned.
8
+ # @return [Fear::Try]
9
+ # @example
10
+ # Fear.try { 4/0 } #=> #<Fear::Failure exception=#<ZeroDivisionError: divided by 0>>
11
+ # Fear.try { 4/2 } #=> #<Fear::Success value=2>
12
+ #
13
+ def try
14
+ success(yield)
15
+ rescue StandardError => error
16
+ failure(error)
17
+ end
18
+
19
+ # @param exception [StandardError]
20
+ # @return [Fear::Failure]
21
+ #
22
+ def failure(exception)
23
+ Fear::Failure.new(exception)
24
+ end
25
+
26
+ # @param value [any]
27
+ # @return [Fear::Success]
28
+ #
29
+ def success(value)
30
+ Fear::Success.new(value)
31
+ end
32
+ end
33
+ end