fear 0.9.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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