fear 0.1.0 → 0.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.
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: {}