fear 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0a628e4319dcc4a672e6a632d6582fdcd9c8bd71
4
- data.tar.gz: 3833ced7c33d30fe1111234549b924449a999032
3
+ metadata.gz: 1fa320f98cb5dc71a7b4e59d7a500c01b729f449
4
+ data.tar.gz: ea1935382f1bed41adab1264490f3346ff7d93c9
5
5
  SHA512:
6
- metadata.gz: 56fb816f68a1a85bba4d8819e714c9669b7d2c9972d35a3b4e0c748e16698e2af16687a71cb8099d843800946da2d097fa71045b9c765f8779ef5863ca0a8021
7
- data.tar.gz: efa4007136b4b74dbdedddfdfe3e9cd3ccfa5edd4d71d6cd6b554ac0b074298076cdbca2ca3b57a95c81fcf79d4974b9d89a059dd4c5f78855770e100a1134a9
6
+ metadata.gz: 49da39785143896e2c6ae73b86765fd7aefdac8603ecbf2bd2d4516cce164dd5946fb9041201f7738eca4fbe8c1e6d78760696a0717d5918c36a7dfe48bfc6ff
7
+ data.tar.gz: 66404d368eb7162f626a4f822b4d4cc0975888f764ffa2bbb31354227a04b795ba8ced9f0ee769880aa6831dc90759f24ea87769a39d88795d8485228e053896
data/.rubocop.yml CHANGED
@@ -4,29 +4,5 @@ inherit_gem:
4
4
  Style/MethodName:
5
5
  Enabled: false
6
6
 
7
- # This configuration was generated by `rubocop --auto-gen-config`
8
- # on 2015-03-09 00:50:30 +0300 using RuboCop version 0.29.1.
9
- # The point is for the user to remove these configuration records
10
- # one by one as the offenses are removed from the code base.
11
- # Note that changes in the inspected code, or installation of new
12
- # versions of RuboCop, may require this file to be generated again.
13
-
14
- # Offense count: 1
15
- # Configuration parameters: CountComments.
16
- Metrics/ClassLength:
17
- Enabled: false
18
- Max: 133
19
-
20
- # Offense count: 1
21
- # Configuration parameters: CountComments.
22
- Metrics/ModuleLength:
23
- Max: 130
24
-
25
- # Offense count: 9
26
7
  Style/Documentation:
27
8
  Enabled: false
28
-
29
- # Offense count: 2
30
- # Configuration parameters: Methods.
31
- Style/SingleLineBlockParams:
32
- Enabled: false
data/README.md CHANGED
@@ -1,7 +1,9 @@
1
1
  # Fear
2
2
  [![Build Status](https://travis-ci.org/bolshakov/fear.svg?branch=master)](https://travis-ci.org/bolshakov/fear)
3
+ [![Gem Version](https://badge.fury.io/rb/fear.svg)](https://badge.fury.io/rb/fear)
3
4
 
4
- TODO: Write a gem description
5
+ This gem provides `Option`, `Either`, and `Try` monads implemented an idiomatic way.
6
+ It is highly inspired by scala's implementation.
5
7
 
6
8
  ## Installation
7
9
 
@@ -21,7 +23,136 @@ Or install it yourself as:
21
23
 
22
24
  ## Usage
23
25
 
24
- TODO: Write usage instructions here
26
+ ### Option
27
+
28
+ Represents optional values. Instances of `Option` are either an instance of
29
+ `Some` or the object `None`.
30
+
31
+ The most idiomatic way to use an `Option` instance is to treat it
32
+ as a collection and use `map`, `flat_map`, `select`, or `each`:
33
+
34
+ ```ruby
35
+ name = Option(params[:name])
36
+ upper = name.map(&:strip).select { |n| n.length != 0 }.map(&:upcase)
37
+ puts upper.get_or_else('')
38
+ ```
39
+
40
+ This allows for sophisticated chaining of `Option` values without
41
+ having to check for the existence of a value.
42
+
43
+ See full documentation [Fear::Option](http://www.rubydoc.info/github/bolshakov/fear/master/Fear/Option)
44
+
45
+ ### Try
46
+
47
+ The `Try` represents a computation that may either result in an exception,
48
+ or return a successfully computed value. Instances of `Try`, are either
49
+ an instance of `Success` or `Failure`.
50
+
51
+ For example, `Try` can be used to perform division on a user-defined input,
52
+ without the need to do explicit exception-handling in all of the places
53
+ that an exception might occur.
54
+
55
+ ```ruby
56
+ dividend = Try { Integer(params[:dividend]) }
57
+ divisor = Try { Integer(params[:divisor]) }
58
+
59
+ problem = dividend.flat_map { |x| divisor.map { |y| x / y }
60
+
61
+ if problem.success?
62
+ puts "Result of #{dividend.get} / #{divisor.get} is: #{problem.get}"
63
+ else
64
+ puts "You must've divided by zero or entered something wrong. Try again"
65
+ puts "Info from the exception: #{problem.exception.message}"
66
+ end
67
+ ```
68
+
69
+ See full documentation [Fear::Try](http://www.rubydoc.info/github/bolshakov/fear/master/Fear/Try)
70
+
71
+ ### Either
72
+
73
+ Represents a value of one of two possible types (a disjoint union.)
74
+ An instance of `Either` is either an instance of `Left` or `Right`.
75
+
76
+ A common use of `Either` is as an alternative to `Option` for dealing
77
+ with possible missing values. In this usage, `None` is replaced
78
+ with a `Left` which can contain useful information.
79
+ `Right` takes the place of `Some`. Convention dictates
80
+ that `Left` is used for failure and `Right` is used for success.
81
+
82
+ For example, you could use `Either<String, Fixnum>` to select whether a
83
+ received input is a `String` or an `Fixnum`.
84
+
85
+ ```ruby
86
+ input = Readline.readline('Type Either a string or an Int: ', true)
87
+ result = begin
88
+ Right(Integer(input))
89
+ rescue ArgumentError
90
+ Left(input)
91
+ end
92
+
93
+ puts(
94
+ result.reduce(
95
+ -> (x) { "You passed me the Int: #{x}, which I will increment. #{x} + 1 = #{x+1}" },
96
+ -> (x) { "You passed me the String: #{x}" }
97
+ )
98
+ )
99
+ ```
100
+
101
+ See full documentation [Fear::Either](http://www.rubydoc.info/github/bolshakov/fear/master/Fear/Either)
102
+
103
+ ### For composition
104
+
105
+ Provides syntactic sugar for composition of multiple monadic operations.
106
+ It supports two such operations - `flat_map` and `map`. Any class providing them
107
+ is supported by `For`.
108
+
109
+ ```ruby
110
+ For(a: Some(2), b: Some(3)) do
111
+ a * b
112
+ end #=> Some(6)
113
+ ```
114
+
115
+ It would be translated to
116
+
117
+ ```ruby
118
+ Some(2).flat_map do |a|
119
+ Some(3).map do |b|
120
+ a * b
121
+ end
122
+ end
123
+ ```
124
+
125
+ If one of operands is None, the result is None
126
+
127
+ ```ruby
128
+ For(a: Some(2), b: None()) do
129
+ a * b
130
+ end #=> None()
131
+
132
+ For(a: None(), b: Some(2)) do
133
+ a * b
134
+ end #=> None()
135
+ ```
136
+
137
+ `For` works with arrays as well
138
+
139
+ ```ruby
140
+ For(a: [1, 2], b: [2, 3], c: [3, 4]) do
141
+ a * b * c
142
+ end #=> [6, 8, 9, 12, 12, 16, 18, 24]
143
+ ```
144
+
145
+ would be translated to:
146
+
147
+ ```ruby
148
+ [1, 2].flat_map do |a|
149
+ [2, 3].flat_map do |b|
150
+ [3, 4].map do |c|
151
+ a * b * c
152
+ end
153
+ end
154
+ end
155
+ ```
25
156
 
26
157
  ## Contributing
27
158
 
data/lib/fear/either.rb CHANGED
@@ -8,7 +8,7 @@ module Fear
8
8
  # `Right` takes the place of `Some`. Convention dictates
9
9
  # that `Left` is used for failure and `Right` is used for success.
10
10
  #
11
- # For example, you could use `Either<String, Fixnum>` to detect whether a
11
+ # For example, you could use `Either<String, Fixnum>` to select whether a
12
12
  # received input is a `String` or an `Fixnum`.
13
13
  #
14
14
  # @example
@@ -26,7 +26,7 @@ module Fear
26
26
  # )
27
27
  # )
28
28
  #
29
- # `Either` is right-biased, which means that `Right` is assumed to be the default case to
29
+ # Either is right-biased, which means that `Right` is assumed to be the default case to
30
30
  # operate on. If it is `Left`, operations like `#map`, `#flat_map`, ... return the `Left` value
31
31
  # unchanged:
32
32
  #
@@ -63,11 +63,11 @@ module Fear
63
63
  # Right(12).flat_map { |x| Left('ruby') } #=> Left('ruby')
64
64
  # Left(12).flat_map { |x| Left('ruby') } #=> Left(12)
65
65
  #
66
- # @example #detect
67
- # Right(12).detect(-1, &:even?) #=> Right(12))
68
- # Right(7).detect(-1, &:even?) #=> Left(-1)
69
- # Left(12).detect(-1, &:even?) #=> Left(-1)
70
- # Left(12).detect(-> { -1 }, &:even?) #=> Left(-1)
66
+ # @example #select
67
+ # Right(12).select(-1, &:even?) #=> Right(12))
68
+ # Right(7).select(-1, &:even?) #=> Left(-1)
69
+ # Left(12).select(-1, &:even?) #=> Left(-1)
70
+ # Left(12).select(-> { -1 }, &:even?) #=> Left(-1)
71
71
  #
72
72
  # @example #to_a
73
73
  # Right(12).to_a #=> [12]
@@ -111,7 +111,6 @@ module Fear
111
111
  #
112
112
  module Either
113
113
  include Dry::Equalizer(:value)
114
- include Fear
115
114
 
116
115
  def left_class
117
116
  Left
@@ -125,6 +124,20 @@ module Fear
125
124
  @value = value
126
125
  end
127
126
 
127
+ # @return [Boolean]
128
+ def right?
129
+ is_a?(Right)
130
+ end
131
+
132
+ alias success? right?
133
+
134
+ # @return [Boolean]
135
+ def left?
136
+ !right?
137
+ end
138
+
139
+ alias failure? left?
140
+
128
141
  attr_reader :value
129
142
  protected :value
130
143
 
data/lib/fear/failure.rb CHANGED
@@ -32,8 +32,7 @@ module Fear
32
32
  end
33
33
 
34
34
  # @return [Failure] self
35
- # TODO: rename to select
36
- def detect
35
+ def select
37
36
  self
38
37
  end
39
38
 
data/lib/fear/left.rb CHANGED
@@ -8,7 +8,7 @@ module Fear
8
8
  # @param default [Proc, any]
9
9
  # @return [Either]
10
10
  #
11
- def detect(default)
11
+ def select(default)
12
12
  Left.new(Utils.return_or_call_proc(default))
13
13
  end
14
14
 
data/lib/fear/none.rb CHANGED
@@ -8,7 +8,7 @@ module Fear
8
8
  #
9
9
  # @return [None]
10
10
  #
11
- def detect(*)
11
+ def select(*)
12
12
  self
13
13
  end
14
14
 
data/lib/fear/option.rb CHANGED
@@ -2,20 +2,15 @@ module Fear
2
2
  # Represents optional values. Instances of `Option`
3
3
  # are either an instance of `Some` or the object `None`.
4
4
  #
5
- # The most idiomatic way to use an `Option` instance is to treat it
6
- # as a collection or monad and use `map`, `flat_map`, `detect`, or `each`:
7
- #
8
- # @example
5
+ # @example The most idiomatic way to use an `Option` instance is to treat it as a collection
9
6
  # name = Option(params[:name])
10
- # upper = name.map(&:strip).detect { |n| n.length != 0 }.map(&:upcase)
7
+ # upper = name.map(&:strip).select { |n| n.length != 0 }.map(&:upcase)
11
8
  # puts upper.get_or_else('')
12
9
  #
13
10
  # This allows for sophisticated chaining of `Option` values without
14
11
  # having to check for the existence of a value.
15
12
  #
16
- # A less-idiomatic way to use `Option` values is via pattern matching:
17
- #
18
- # @example
13
+ # @example A less-idiomatic way to use `Option` values is via pattern matching
19
14
  # name = Option(params[:name])
20
15
  # case name
21
16
  # when Some
@@ -24,9 +19,7 @@ module Fear
24
19
  # puts 'No name value'
25
20
  # end
26
21
  #
27
- # or manually checking for non emptiness:
28
- #
29
- # @example
22
+ # @example or manually checking for non emptiness
30
23
  # name = Option(params[:name])
31
24
  # if name.empty?
32
25
  # puts 'No name value'
@@ -34,17 +27,17 @@ module Fear
34
27
  # puts name.strip.upcase
35
28
  # end
36
29
  #
37
- # @example #detect
38
- # User.find(params[:id]).detect do |user|
30
+ # @example #select
31
+ # User.find(params[:id]).select do |user|
39
32
  # user.posts.count > 0
40
33
  # end #=> Some(User)
41
34
  #
42
- # User.find(params[:id]).detect do |user|
35
+ # User.find(params[:id]).select do |user|
43
36
  # user.posts.count > 0
44
37
  # end #=> None
45
38
  #
46
39
  # User.find(params[:id])
47
- # .detect(&:confirmed?)
40
+ # .select(&:confirmed?)
48
41
  # .map(&:posts)
49
42
  # .inject(0, &:count)
50
43
  #
data/lib/fear/right.rb CHANGED
@@ -9,7 +9,7 @@ module Fear
9
9
  # @param default [Proc, any]
10
10
  # @return [Either]
11
11
  #
12
- def detect(default)
12
+ def select(default)
13
13
  if yield(value)
14
14
  self
15
15
  else
@@ -17,7 +17,7 @@ module Fear
17
17
  end
18
18
 
19
19
  # Ensures that returned value either left, or right.
20
- def detect(*)
20
+ def select(*)
21
21
  super.tap do |result|
22
22
  Utils.assert_type!(result, left_class, right_class)
23
23
  end
data/lib/fear/some.rb CHANGED
@@ -20,7 +20,7 @@ module Fear
20
20
  # applying the `predicate` to this option's value
21
21
  # returns true. Otherwise, return `None`.
22
22
  #
23
- def detect
23
+ def select
24
24
  if yield(value)
25
25
  self
26
26
  else
data/lib/fear/success.rb CHANGED
@@ -42,7 +42,7 @@ module Fear
42
42
  # @yieldreturn [Boolean]
43
43
  # @return [Try]
44
44
  #
45
- def detect
45
+ def select
46
46
  if yield(value)
47
47
  self
48
48
  else
data/lib/fear/try.rb CHANGED
@@ -11,8 +11,8 @@ module Fear
11
11
  # might occur.
12
12
  #
13
13
  # @example
14
- # dividend = Try { params[:dividend].to_i }
15
- # divisor = Try { params[:divisor].to_i }
14
+ # dividend = Try { Integer(params[:dividend]) }
15
+ # divisor = Try { Integer(params[:divisor]) }
16
16
  # problem = dividend.flat_map { |x| divisor.map { |y| x / y }
17
17
  #
18
18
  # if problem.success?
@@ -53,12 +53,12 @@ module Fear
53
53
  # Success(42).map { |v| v/2 } #=> Success(21)
54
54
  # Failure(ArgumentError.new).map { |v| v/2 } #=> Failure(ArgumentError.new)
55
55
  #
56
- # @example #detect
57
- # Success(42).detect { |v| v > 40 }
56
+ # @example #select
57
+ # Success(42).select { |v| v > 40 }
58
58
  # #=> Success(21)
59
- # Success(42).detect { |v| v < 40 }
59
+ # Success(42).select { |v| v < 40 }
60
60
  # #=> Failure(NoSuchElementError.new("Predicate does not hold for 42"))
61
- # Failure(ArgumentError.new).detect { |v| v < 40 }
61
+ # Failure(ArgumentError.new).select { |v| v < 40 }
62
62
  # #=> Failure(ArgumentError.new)
63
63
  #
64
64
  # @example #recover_with
data/lib/fear/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Fear
2
- VERSION = '0.0.1'.freeze
2
+ VERSION = '0.1.0'.freeze
3
3
  end
@@ -33,8 +33,8 @@ RSpec.describe Fear::Failure do
33
33
  it { is_expected.to eq(failure) }
34
34
  end
35
35
 
36
- describe '#detect' do
37
- subject { failure.detect { |v| v == 'value' } }
36
+ describe '#select' do
37
+ subject { failure.select { |v| v == 'value' } }
38
38
  it { is_expected.to eq(failure) }
39
39
  end
40
40
 
@@ -5,9 +5,21 @@ RSpec.describe Fear::Left do
5
5
  let(:left) { described_class.new('value') }
6
6
  end
7
7
 
8
- describe '#detect' do
8
+ let(:left) { described_class.new('value') }
9
+
10
+ describe '#right?' do
11
+ subject { left }
12
+ it { is_expected.not_to be_right }
13
+ end
14
+
15
+ describe '#left?' do
16
+ subject { left }
17
+ it { is_expected.to be_left }
18
+ end
19
+
20
+ describe '#select' do
9
21
  subject do
10
- described_class.new('value').detect(default) { |v| v == 'value' }
22
+ left.select(default) { |v| v == 'value' }
11
23
  end
12
24
 
13
25
  context 'proc default' do
@@ -28,13 +40,13 @@ RSpec.describe Fear::Left do
28
40
  end
29
41
 
30
42
  describe '#swap' do
31
- subject { described_class.new('value').swap }
43
+ subject { left.swap }
32
44
  it { is_expected.to eq(Right('value')) }
33
45
  end
34
46
 
35
47
  describe '#reduce' do
36
48
  subject do
37
- described_class.new('value').reduce(
49
+ left.reduce(
38
50
  ->(left) { "Left: #{left}" },
39
51
  ->(right) { "Right: #{right}" },
40
52
  )
@@ -69,7 +81,7 @@ RSpec.describe Fear::Left do
69
81
  end
70
82
 
71
83
  context 'value is not Either' do
72
- subject { proc { described_class.new('error').join_left } }
84
+ subject { proc { left.join_left } }
73
85
 
74
86
  it 'fails with type error' do
75
87
  is_expected.to raise_error(TypeError)
@@ -19,8 +19,8 @@ RSpec.describe Fear::None do
19
19
  expect(result).to eq nil
20
20
  end
21
21
 
22
- describe '#detect' do
23
- subject { none.detect { |value| value > 42 } }
22
+ describe '#select' do
23
+ subject { none.select { |value| value > 42 } }
24
24
 
25
25
  it 'always return None' do
26
26
  is_expected.to eq(None())
@@ -5,8 +5,18 @@ RSpec.describe Fear::Right do
5
5
 
6
6
  let(:right) { described_class.new('value') }
7
7
 
8
- describe '#detect' do
9
- subject { right.detect(default, &predicate) }
8
+ describe '#right?' do
9
+ subject { right }
10
+ it { is_expected.to be_right }
11
+ end
12
+
13
+ describe '#left?' do
14
+ subject { right }
15
+ it { is_expected.not_to be_left }
16
+ end
17
+
18
+ describe '#select' do
19
+ subject { right.select(default, &predicate) }
10
20
 
11
21
  context 'predicate evaluates to true' do
12
22
  let(:predicate) { ->(v) { v == 'value' } }
@@ -28,13 +38,13 @@ RSpec.describe Fear::Right do
28
38
  end
29
39
 
30
40
  describe '#swap' do
31
- subject { described_class.new('value').swap }
41
+ subject { right.swap }
32
42
  it { is_expected.to eq(Fear::Left.new('value')) }
33
43
  end
34
44
 
35
45
  describe '#reduce' do
36
46
  subject do
37
- described_class.new('value').reduce(
47
+ right.reduce(
38
48
  ->(left) { "Left: #{left}" },
39
49
  ->(right) { "Right: #{right}" },
40
50
  )
@@ -8,8 +8,8 @@ RSpec.describe Fear::Some do
8
8
  subject(:some) { Some(value) }
9
9
  let(:value) { 42 }
10
10
 
11
- describe '#detect' do
12
- subject { some.detect(&predicate) }
11
+ describe '#select' do
12
+ subject { some.select(&predicate) }
13
13
 
14
14
  context 'predicate evaluates to true' do
15
15
  let(:predicate) { ->(v) { v > 40 } }
@@ -54,19 +54,19 @@ RSpec.describe Fear::Success do
54
54
  end
55
55
  end
56
56
 
57
- describe '#detect' do
57
+ describe '#select' do
58
58
  context 'predicate holds for value' do
59
- subject { success.detect { |v| v == 'value' } }
59
+ subject { success.select { |v| v == 'value' } }
60
60
  it { is_expected.to eq(success) }
61
61
  end
62
62
 
63
63
  context 'predicate does not hold for value' do
64
- subject { proc { success.detect { |v| v != 'value' }.get } }
64
+ subject { proc { success.select { |v| v != 'value' }.get } }
65
65
  it { is_expected.to raise_error(Fear::NoSuchElementError, 'Predicate does not hold for `value`') }
66
66
  end
67
67
 
68
68
  context 'predicate fails with error' do
69
- subject { proc { success.detect { fail 'foo' }.get } }
69
+ subject { proc { success.select { fail 'foo' }.get } }
70
70
  it { is_expected.to raise_error(RuntimeError, 'foo') }
71
71
  end
72
72
  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.0.1
4
+ version: 0.1.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-08 00:00:00.000000000 Z
11
+ date: 2016-12-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-equalizer