fear 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/fear/try.rb CHANGED
@@ -1,16 +1,16 @@
1
1
  module Fear
2
- # The `Try` represents a computation that may either result
3
- # in an exception, or return a successfully computed value.
2
+ # The +Try+ represents a computation that may either result
3
+ # in an exception, or return a successfully computed value. Instances of +Try+,
4
+ # are either an instance of +Success+ or +Failure+.
4
5
  #
5
- # Instances of `Try`, are either an instance of `Success` or
6
- # `Failure`.
7
- #
8
- # For example, `Try` can be used to perform division on a
6
+ # For example, +Try+ can be used to perform division on a
9
7
  # user-defined input, without the need to do explicit
10
8
  # exception-handling in all of the places that an exception
11
9
  # might occur.
12
10
  #
13
11
  # @example
12
+ # include Fear::Try::Mixin
13
+ #
14
14
  # dividend = Try { Integer(params[:dividend]) }
15
15
  # divisor = Try { Integer(params[:divisor]) }
16
16
  # problem = dividend.flat_map { |x| divisor.map { |y| x / y }
@@ -22,89 +22,226 @@ module Fear
22
22
  # puts "Info from the exception: #{problem.exception.message}"
23
23
  # end
24
24
  #
25
- # An important property of `Try` shown in the above example is its
26
- # ability to `pipeline`, or chain, operations, catching exceptions
27
- # along the way. The `flat_map` and `map` combinators in the above
25
+ # An important property of +Try+ shown in the above example is its
26
+ # ability to _pipeline_, or chain, operations, catching exceptions
27
+ # along the way. The +flat_map+ and +map+ combinators in the above
28
28
  # example each essentially pass off either their successfully completed
29
- # value, wrapped in the `Success` type for it to be further operated
29
+ # value, wrapped in the +Success+ type for it to be further operated
30
30
  # upon by the next combinator in the chain, or the exception wrapped
31
- # in the `Failure` type usually to be simply passed on down the chain.
32
- # Combinators such as `rescue` and `recover` are designed to provide some
31
+ # in the +Failure+ type usually to be simply passed on down the chain.
32
+ # Combinators such as +recover_with+ and +recover+ are designed to provide some
33
33
  # type of default behavior in the case of failure.
34
34
  #
35
- # @note only non-fatal exceptions are caught by the combinators on `Try`.
36
- # Serious system errors, on the other hand, will be thrown.
37
- #
38
- # @note all `Try` combinators will catch exceptions and return failure
39
- # unless otherwise specified in the documentation.
40
- #
41
- # @example #or_else
42
- # Success(42).or_else { -1 } #=> Success(42)
43
- # Failure(ArgumentError.new).or_else { -1 } #=> Success(-1)
44
- # Failure(ArgumentError.new).or_else { 1/0 } #=> Failure(ZeroDivisionError.new('divided by 0'))
45
- #
46
- # @example #flatten
47
- # Success(42).flatten #=> Success(42)
48
- # Success(Success(42)).flatten #=> Success(42)
49
- # Success(Failure(ArgumentError.new)).flatten #=> Failure(ArgumentError.new)
50
- # Failure(ArgumentError.new).flatten { -1 } #=> Failure(ArgumentError.new)
51
- #
52
- # @example #map
53
- # Success(42).map { |v| v/2 } #=> Success(21)
54
- # Failure(ArgumentError.new).map { |v| v/2 } #=> Failure(ArgumentError.new)
55
- #
56
- # @example #select
57
- # Success(42).select { |v| v > 40 }
58
- # #=> Success(21)
59
- # Success(42).select { |v| v < 40 }
60
- # #=> Failure(NoSuchElementError.new("Predicate does not hold for 42"))
61
- # Failure(ArgumentError.new).select { |v| v < 40 }
62
- # #=> Failure(ArgumentError.new)
63
- #
64
- # @example #recover_with
65
- # Success(42).recover_with { |e| Success(e.massage) }
66
- # #=> Success(42)
67
- # Failure(ArgumentError.new).recover_with { |e| Success(e.massage) }
68
- # #=> Success('ArgumentError')
69
- # Failure(ArgumentError.new).recover_with { |e| fail }
70
- # #=> Failure(RuntimeError)
71
- #
72
- # @example #recover
73
- # Success(42).recover { |e| e.massage }
74
- # #=> Success(42)
75
- # Failure(ArgumentError.new).recover { |e| e.massage }
76
- # #=> Success('ArgumentError')
77
- # Failure(ArgumentError.new).recover { |e| fail }
78
- # #=> Failure(RuntimeError)
35
+ # @note only non-fatal exceptions are caught by the combinators on +Try+.
36
+ # Serious system errors, on the other hand, will be thrown.
37
+ #
38
+ # @note all +Try+ combinators will catch exceptions and return failure unless
39
+ # otherwise specified in the documentation.
40
+ #
41
+ # @!method get_or_else(*args)
42
+ # Returns the value from this +Success+ or evaluates the given
43
+ # default argument if this is a +Failure+.
44
+ # @overload get_or_else(&default)
45
+ # @yieldreturn [any]
46
+ # @return [any]
47
+ # @example
48
+ # Success(42).get_or_else { 24/2 } #=> 42
49
+ # Failure(ArgumentError.new).get_or_else { 24/2 } #=> 12
50
+ # @overload get_or_else(default)
51
+ # @return [any]
52
+ # @example
53
+ # Success(42).get_or_else(12) #=> 42
54
+ # Failure(ArgumentError.new).get_or_else(12) #=> 12
55
+ #
56
+ # @!method include?(other_value)
57
+ # Returns +true+ if it has an element that is equal
58
+ # (as determined by +==+) to +other_value+, +false+ otherwise.
59
+ # @param [any]
60
+ # @return [Boolean]
61
+ # @example
62
+ # Success(17).include?(17) #=> true
63
+ # Success(17).include?(7) #=> false
64
+ # Failure(ArgumentError.new).include?(17) #=> false
65
+ #
66
+ # @!method each(&block)
67
+ # Performs the given block if this is a +Success+.
68
+ # @note if block raise an error, then this method may raise an exception.
69
+ # @yieldparam [any] value
70
+ # @yieldreturn [void]
71
+ # @return [Try] itself
72
+ # @example
73
+ # Success(17).each do |value|
74
+ # puts value
75
+ # end #=> prints 17
76
+ #
77
+ # Failure(ArgumentError.new).each do |value|
78
+ # puts value
79
+ # end #=> does nothing
80
+ #
81
+ # @!method map(&block)
82
+ # Maps the given block to the value from this +Success+ or
83
+ # returns this if this is a +Failure+.
84
+ # @yieldparam [any] value
85
+ # @yieldreturn [any]
86
+ # @example
87
+ # Success(42).map { |v| v/2 } #=> Success(21)
88
+ # Failure(ArgumentError.new).map { |v| v/2 } #=> Failure(ArgumentError.new)
89
+ #
90
+ # @!method flat_map(&block)
91
+ # Returns the given block applied to the value from this +Success+
92
+ # or returns this if this is a +Failure+.
93
+ # @yieldparam [any] value
94
+ # @yieldreturn [Try]
95
+ # @return [Try]
96
+ # @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 #=> []
109
+ #
110
+ # @!method to_option
111
+ # Returns an +Some+ containing the +Success+ value or a +None+ if
112
+ # this is a +Failure+.
113
+ # @return [Option]
114
+ # @example
115
+ # Success(42).to_option #=> Some(21)
116
+ # Failure(ArgumentError.new).to_option #=> None()
117
+ #
118
+ # @!method any?(&predicate)
119
+ # Returns +false+ if +Failure+ or returns the result of the
120
+ # application of the given predicate to the +Success+ value.
121
+ # @yieldparam [any] value
122
+ # @yieldreturn [Boolean]
123
+ # @return [Boolean]
124
+ # @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
128
+ #
129
+ # ---
130
+ #
131
+ # @!method success?
132
+ # Returns +true+ if it is a +Success+, +false+ otherwise.
133
+ # @return [Boolean]
134
+ #
135
+ # @!method failure?
136
+ # Returns +true+ if it is a +Failure+, +false+ otherwise.
137
+ # @return [Boolean]
138
+ #
139
+ # @!method get
140
+ # Returns the value from this +Success+ or raise the exception
141
+ # if this is a +Failure+.
142
+ # @return [any]
143
+ # @example
144
+ # Success(42).get #=> 42
145
+ # Failure(ArgumentError.new).get #=> ArgumentError: ArgumentError
146
+ #
147
+ # @!method or_else(&default)
148
+ # Returns this +Try+ if it's a +Success+ or the given default
149
+ # argument if this is a +Failure+.
150
+ # @return [Try]
151
+ # @example
152
+ # Success(42).or_else { -1 } #=> Success(42)
153
+ # Failure(ArgumentError.new).or_else { -1 } #=> Success(-1)
154
+ # Failure(ArgumentError.new).or_else { 1/0 } #=> Failure(ZeroDivisionError.new('divided by 0'))
155
+ #
156
+ # @!method flatten
157
+ # Transforms a nested +Try+, ie, a +Success+ of +Success+,
158
+ # into an un-nested +Try+, ie, a +Success+.
159
+ # @return [Try]
160
+ # @example
161
+ # Success(42).flatten #=> Success(42)
162
+ # Success(Success(42)).flatten #=> Success(42)
163
+ # Success(Failure(ArgumentError.new)).flatten #=> Failure(ArgumentError.new)
164
+ # Failure(ArgumentError.new).flatten { -1 } #=> Failure(ArgumentError.new)
165
+ #
166
+ # @!method select(&predicate)
167
+ # Converts this to a +Failure+ if the predicate is not satisfied.
168
+ # @yieldparam [any] value
169
+ # @yieldreturn [Boolean]
170
+ # @return [Try]
171
+ # @example
172
+ # Success(42).select { |v| v > 40 }
173
+ # #=> Success(21)
174
+ # Success(42).select { |v| v < 40 }
175
+ # #=> Failure(Fear::NoSuchElementError.new("Predicate does not hold for 42"))
176
+ # Failure(ArgumentError.new).select { |v| v < 40 }
177
+ # #=> Failure(ArgumentError.new)
178
+ #
179
+ # @!method recover_with(&block)
180
+ # Applies the given block to exception. This is like +flat_map+
181
+ # for the exception.
182
+ # @yieldparam [Exception] exception
183
+ # @yieldreturn [Try]
184
+ # @return [Try]
185
+ # @example
186
+ # Success(42).recover_with { |e| Success(e.massage) }
187
+ # #=> Success(42)
188
+ # Failure(ArgumentError.new).recover_with { |e| Success(e.massage) }
189
+ # #=> Success('ArgumentError')
190
+ # Failure(ArgumentError.new).recover_with { |e| fail }
191
+ # #=> Failure(RuntimeError)
192
+ #
193
+ # @!method recover(&block)
194
+ # Applies the given block to exception. This is like +map+ for the exception.
195
+ # @yieldparam [Exception] exception
196
+ # @yieldreturn [any]
197
+ # @return [Try]
198
+ # @example #recover
199
+ # Success(42).recover { |e| e.massage }
200
+ # #=> Success(42)
201
+ # Failure(ArgumentError.new).recover { |e| e.massage }
202
+ # #=> Success('ArgumentError')
203
+ # Failure(ArgumentError.new).recover { |e| fail }
204
+ # #=> Failure(RuntimeError)
205
+ #
206
+ # @!method to_either
207
+ # Returns +Left+ with exception if this is a +Failure+, otherwise
208
+ # returns +Right+ with +Success+ value.
209
+ # @return [Right<any>, Left<StandardError>]
210
+ # @example
211
+ # Success(42).to_either #=> Right(42)
212
+ # Failure(ArgumentError.new).to_either #=> Left(ArgumentError.new)
79
213
  #
80
214
  # @author based on Twitter's original implementation.
81
215
  # @see https://github.com/scala/scala/blob/2.11.x/src/library/scala/util/Try.scala
82
216
  #
83
217
  module Try
218
+ # @private
84
219
  def left_class
85
220
  Failure
86
221
  end
87
222
 
223
+ # @private
88
224
  def right_class
89
225
  Success
90
226
  end
91
227
 
92
- # @return [true, false] `true` if the `Try` is a `Failure`,
93
- # `false` otherwise.
228
+ # Include this mixin to access convenient factory methods.
229
+ # @example
230
+ # include Fear::Try::Mixin
231
+ #
232
+ # Try { 4/2 } #=> #<Fear::Success value=2>
233
+ # Try { 4/0 } #=> #<Fear::Failure value=#<ZeroDivisionError: divided by 0>>
234
+ # Success(2) #=> #<Fear::Success value=2>
94
235
  #
95
- def failure?
96
- !success?
97
- end
98
-
99
236
  module Mixin
100
- # Constructs a `Try` using the block. This
101
- # method will ensure any non-fatal exception is caught and a
102
- # `Failure` object is returned.
237
+ # Constructs a +Try+ using the block. This
238
+ # method will ensure any non-fatal exception )is caught and a
239
+ # +Failure+ object is returned.
103
240
  # @return [Try]
104
241
  #
105
242
  def Try
106
243
  Success.new(yield)
107
- rescue StandardError => error
244
+ rescue => error
108
245
  Failure.new(error)
109
246
  end
110
247
 
data/lib/fear/utils.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  module Fear
2
+ # @private
2
3
  module Utils
3
4
  extend self
4
5
 
data/lib/fear/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Fear
2
- VERSION = '0.1.0'.freeze
2
+ VERSION = '0.2.0'.freeze
3
3
  end
@@ -1,5 +1,6 @@
1
1
  RSpec.describe Fear::Failure do
2
- let(:failure) { described_class.new(RuntimeError.new('error')) }
2
+ let(:exception) { RuntimeError.new('error') }
3
+ let(:failure) { described_class.new(exception) }
3
4
 
4
5
  it_behaves_like Fear::RightBiased::Left do
5
6
  let(:left) { failure }
@@ -75,4 +76,9 @@ RSpec.describe Fear::Failure do
75
76
  it { expect { recover.get }.to raise_error(RuntimeError, 'unexpected error') }
76
77
  end
77
78
  end
79
+
80
+ describe '#to_either' do
81
+ subject { failure.to_either }
82
+ it { is_expected.to eq(Fear::Left.new(exception)) }
83
+ end
78
84
  end
@@ -17,9 +17,9 @@ RSpec.describe Fear::Left do
17
17
  it { is_expected.to be_left }
18
18
  end
19
19
 
20
- describe '#select' do
20
+ describe '#select_or_else' do
21
21
  subject do
22
- left.select(default) { |v| v == 'value' }
22
+ left.select_or_else(default) { |v| v == 'value' }
23
23
  end
24
24
 
25
25
  context 'proc default' do
@@ -39,6 +39,16 @@ RSpec.describe Fear::Left do
39
39
  end
40
40
  end
41
41
 
42
+ describe '#select' do
43
+ subject do
44
+ left.select { |v| v == 'value' }
45
+ end
46
+
47
+ it 'return self' do
48
+ is_expected.to eq(left)
49
+ end
50
+ end
51
+
42
52
  describe '#swap' do
43
53
  subject { left.swap }
44
54
  it { is_expected.to eq(Right('value')) }
@@ -7,16 +7,19 @@ RSpec.describe Fear::None do
7
7
 
8
8
  subject(:none) { None() }
9
9
 
10
- specify '#get fails with exception' do
11
- expect do
12
- none.get
13
- end.to raise_error(NoMethodError)
10
+ describe '#get' do
11
+ subject { proc { none.get } }
12
+ it { is_expected.to raise_error(Fear::NoSuchElementError) }
14
13
  end
15
14
 
16
- specify '#or_nil returns nil' do
17
- result = none.or_nil
15
+ describe '#or_nil' do
16
+ subject { none.or_nil }
17
+ it { is_expected.to eq(nil) }
18
+ end
18
19
 
19
- expect(result).to eq nil
20
+ describe '#empty?' do
21
+ subject { none.empty? }
22
+ it { is_expected.to eq(true) }
20
23
  end
21
24
 
22
25
  describe '#select' do
@@ -1,32 +1,15 @@
1
1
  RSpec.describe Fear::Option do
2
2
  include Fear::Option::Mixin
3
3
 
4
- describe 'Option()' do
5
- it 'returns Some if value is not nil' do
6
- option = Option(double)
7
-
8
- expect(option).to be_kind_of(Fear::Some)
9
- end
10
-
11
- it 'returns None if value is nil' do
12
- option = Option(nil)
13
-
14
- expect(option).to be_kind_of(Fear::None)
15
- end
16
- end
17
-
18
- let(:some) { Some(42) }
19
- let(:none) { None() }
20
-
21
- describe '#empty?' do
22
- context 'Some' do
23
- subject { some.empty? }
24
- it { is_expected.to eq(false) }
4
+ describe '#Option()' do
5
+ context 'value is nil' do
6
+ subject { Option(nil) }
7
+ it { is_expected.to eq(None()) }
25
8
  end
26
9
 
27
- context 'None' do
28
- subject { none.empty? }
29
- it { is_expected.to eq(true) }
10
+ context 'value is not nil' do
11
+ subject { Option(42) }
12
+ it { is_expected.to eq(Some(42)) }
30
13
  end
31
14
  end
32
15
  end
@@ -15,8 +15,8 @@ RSpec.describe Fear::Right do
15
15
  it { is_expected.not_to be_left }
16
16
  end
17
17
 
18
- describe '#select' do
19
- subject { right.select(default, &predicate) }
18
+ describe '#select_or_else' do
19
+ subject { right.select_or_else(default, &predicate) }
20
20
 
21
21
  context 'predicate evaluates to true' do
22
22
  let(:predicate) { ->(v) { v == 'value' } }
@@ -37,6 +37,20 @@ RSpec.describe Fear::Right do
37
37
  end
38
38
  end
39
39
 
40
+ describe '#select' do
41
+ subject { right.select(&predicate) }
42
+
43
+ context 'predicate evaluates to true' do
44
+ let(:predicate) { ->(v) { v == 'value' } }
45
+ it { is_expected.to eq(right) }
46
+ end
47
+
48
+ context 'predicate evaluates to false' do
49
+ let(:predicate) { ->(v) { v != 'value' } }
50
+ it { is_expected.to eq(Fear::Left.new('value')) }
51
+ end
52
+ end
53
+
40
54
  describe '#swap' do
41
55
  subject { right.swap }
42
56
  it { is_expected.to eq(Fear::Left.new('value')) }
@@ -5,8 +5,7 @@ RSpec.describe Fear::Some do
5
5
  let(:right) { described_class.new('value') }
6
6
  end
7
7
 
8
- subject(:some) { Some(value) }
9
- let(:value) { 42 }
8
+ subject(:some) { Some(42) }
10
9
 
11
10
  describe '#select' do
12
11
  subject { some.select(&predicate) }
@@ -36,13 +35,18 @@ RSpec.describe Fear::Some do
36
35
  end
37
36
  end
38
37
 
39
- specify '#get returns value' do
40
- expect(some.get).to eq value
38
+ describe '#get' do
39
+ subject { some.get }
40
+ it { is_expected.to eq(42) }
41
41
  end
42
42
 
43
- specify '#or_nil returns value' do
44
- result = some.or_nil
43
+ describe '#or_nil' do
44
+ subject { some.or_nil }
45
+ it { is_expected.to eq(42) }
46
+ end
45
47
 
46
- expect(result).to eq value
48
+ describe '#empty?' do
49
+ subject { some.empty? }
50
+ it { is_expected.to eq(false) }
47
51
  end
48
52
  end
@@ -80,4 +80,9 @@ RSpec.describe Fear::Success do
80
80
  subject { success.recover { |v| v * 2 } }
81
81
  it { is_expected.to eq(success) }
82
82
  end
83
+
84
+ describe '#to_either' do
85
+ subject { success.to_either }
86
+ it { is_expected.to eq(Fear::Right.new('value')) }
87
+ end
83
88
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fear
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tema Bolshakov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-12-09 00:00:00.000000000 Z
11
+ date: 2016-12-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-equalizer
@@ -105,6 +105,7 @@ files:
105
105
  - ".rspec"
106
106
  - ".rubocop.yml"
107
107
  - ".travis.yml"
108
+ - ".yardopts"
108
109
  - Gemfile
109
110
  - LICENSE.txt
110
111
  - README.md
@@ -136,7 +137,7 @@ files:
136
137
  - spec/fear/success_spec.rb
137
138
  - spec/fear/utils_spec.rb
138
139
  - spec/spec_helper.rb
139
- homepage: https://github.com/bolshakov/functional
140
+ homepage: https://github.com/bolshakov/fear
140
141
  licenses:
141
142
  - MIT
142
143
  metadata: {}