ytry 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +38 -0
  3. data/lib/ytry.rb +57 -13
  4. data/lib/ytry/version.rb +1 -1
  5. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 445fb840f8d15382ce1b280cdc6f746b907e2dfb
4
- data.tar.gz: 559d814425ba8e70f0e23932890922d99a5393d3
3
+ metadata.gz: 6575212d45e0fb109f44a9da7cba53ad23693ee7
4
+ data.tar.gz: 7054016b7dfb31dcff32878f13ae6ef30e0e9b1c
5
5
  SHA512:
6
- metadata.gz: e4dcc9c8e5518b8ed3ee38e9d2c6fed5c858f6bcb64dcb72adc86e6bc61950b211d4de880cb8ed0bbbb9f79f589ea9709fda6130bfd7499599fb16c5368c4d33
7
- data.tar.gz: 3d65b7ee4d84e21b90f42c84ba11a91cc27f8d26bfa3235bb9377823dfa97b7d712ad7ff74a1866b3c945b834f6daaa57307683fc3defc3e75628f20025c32d6
6
+ metadata.gz: 7e1deff71f7aad49b3b29832fca250bcfb5b05778cb6c05111dd5dd13289c4b23857bc79b8901c420bc023a661973d0ca30b9a791d8c5b85df1d5d7b4be5037d
7
+ data.tar.gz: b519003d0a36612a5e70610b4a5369a7c1f92c75359b56739980ad0e5ff381fcce3aaafd139f118c8d869bce3e5fc36868dc58d840a15025eef56d85577d2290
data/README.md CHANGED
@@ -82,6 +82,44 @@ Using Try instead of rescue blocks can make your software both clearer and safer
82
82
  - privileges method chaining thus reducing the need for auxiliary variables to store intermediate results in a computation
83
83
  - encourages programming towards immutability, where the data is transformed rather than mutated in place.
84
84
 
85
+ ## Advanced Usage
86
+ ### #reduce
87
+ Given a Try instance `try`, a value `c` and a lambda `f`,
88
+ ```
89
+ try.reduce(c, &f)
90
+ ```
91
+ returns `f.(c, try)` if `try` is a `Success` AND the evaluation of the lambda `f` did not throw any error, it returns `c` otherwise.
92
+
93
+ This is a shortcut to
94
+ ```
95
+ try.map{|v| f.(c,v)}.get_or_else {c}
96
+ ```
97
+
98
+
99
+ ### #flatten
100
+ When dealing with nested `Try`s we can use flatten to reduce the level of nesting
101
+ ```
102
+ success = Try{:ok}
103
+ failure = Try{fail}
104
+ Try{success}.flatten # Success(:ok)
105
+ Try{failure}.flatten # Failure(RuntimeError)
106
+ ```
107
+
108
+ ### Interoperability with Array-like obects
109
+ Because of it's ary-like nature, instances of `Try` play well with Array instances. In particular, flattening an Array of `Try`s is equivalent to filtering out the `Failures` from the array and then calling #get on the Success instances
110
+ ```
111
+ (1..4).map{|v| Try{v}.select(&:odd?)}
112
+ .flatten # [1, 3]
113
+ ```
114
+ Behind the scenes `Array#flatten` is iterating over the collection and concatenating the ary-representation of each element.
115
+ Now `Failure#to_ary` returns `[]`, while `Success#to_ary` returns `[v]` - where `v` is the value wrapped by Success - and that does the trick.
116
+
117
+ We can squeeze the code listed above even more with `Array#flat_map`
118
+ ```
119
+ (1..4).flat_map{|v| Try{v}.select(&:odd?)} # [1, 3]
120
+ ```
121
+ Again, there is no magic behind this behaviour, we are just exploiting Ruby's duck typing.
122
+
85
123
  ## Development
86
124
 
87
125
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -24,14 +24,30 @@ module Ytry
24
24
  end
25
25
  def each
26
26
  return enum_for(__method__) unless block_given?
27
- yield self.get unless empty?
27
+ Try { yield self.get unless empty? }
28
28
  return self
29
29
  end
30
- %i(map select reject collect collect_concat).each do |method|
31
- define_method method, ->(&block) {
32
- block or return enum_for(method)
33
- self.empty? ? self : Try.ary_to_type(Try{super(&block)}.flatten)
34
- }
30
+ alias_method :on_success, :each
31
+ def on_failure
32
+ return enum_for(__method__) unless block_given?
33
+ return self
34
+ end
35
+ def map &block
36
+ block or return enum_for(__method__)
37
+ self.empty? ? self : Try{block.call(self.get)}
38
+ end
39
+ alias_method :collect, :map
40
+ def select &block
41
+ block or return enum_for(__method__)
42
+ return self if empty?
43
+ predicate = Try{ block.call(self.get) }
44
+ return predicate if predicate.empty?
45
+ predicate.get ? self : Try.ary_to_type([])
46
+ end
47
+ def reject &block
48
+ if block_given? then select {|v| ! block.call(v)}
49
+ else enum_for(__method__)
50
+ end
35
51
  end
36
52
  def flat_map &block
37
53
  block or return enum_for(method)
@@ -40,12 +56,19 @@ module Ytry
40
56
  return wrapped_result if (!wrapped_result.empty? && !wrapped_result.get.respond_to?(:to_ary))
41
57
  Try.ary_to_type(wrapped_result.flatten)
42
58
  end
43
- def grep(pattern, &block)
44
- self.empty? ? self : Try.ary_to_type(Try{super}.flatten)
59
+ alias_method :collect_concat, :flat_map
60
+ def grep pattern
61
+ return self if self.empty?
62
+ match = Try {
63
+ (pattern === self.get) ? self.get : (raise RuntimeError.new("Element not found"))
64
+ }
65
+ block_given? ? match.map{|v| yield v} : match
45
66
  end
46
- def flatten
47
- return self if empty?
48
- Try.ary_to_type self.get
67
+ def flatten level = 1
68
+ level.times.reduce(self) { |current, _|
69
+ break(current) if current.empty?
70
+ Try.ary_to_type current.get
71
+ }
49
72
  end
50
73
  def zip *others
51
74
  Try.zip(self, *others)
@@ -55,8 +78,12 @@ module Ytry
55
78
  end
56
79
  def or_else
57
80
  return self unless empty?
58
- other = yield
59
- other.is_a?(Try) ? other : raise(Try.invalid_argument('Block should evaluate to an Try', other))
81
+ candidate = Try{ yield }
82
+ if (!candidate.empty? && !candidate.get.is_a?(Try))
83
+ raise(Try.invalid_argument('Block should evaluate to an instance of Try', candidate.get))
84
+ else
85
+ candidate.flatten
86
+ end
60
87
  end
61
88
  def get_or_else
62
89
  raise ArgumentError, 'missing block' unless block_given?
@@ -88,6 +115,10 @@ module Ytry
88
115
  raise ArgumentError, 'missing block' unless block_given?
89
116
  self
90
117
  end
118
+ def recover_with &block
119
+ raise ArgumentError, 'missing block' unless block_given?
120
+ self
121
+ end
91
122
  end
92
123
  class Failure
93
124
  include Try
@@ -105,10 +136,23 @@ module Ytry
105
136
  def === other
106
137
  other.is_a?(Failure) && self.error === other.error
107
138
  end
139
+ def on_failure
140
+ return enum_for(__method__) unless block_given?
141
+ Try { yield @error }
142
+ return self
143
+ end
108
144
  def recover &block
109
145
  raise ArgumentError, 'missing block' unless block_given?
110
146
  candidate = Success.new(@error).map &block
111
147
  (!candidate.empty? && candidate.get.nil?) ? self : candidate
112
148
  end
149
+ def recover_with &block
150
+ candidate = self.recover(&block)
151
+ if (!candidate.empty? && !candidate.get.is_a?(Try))
152
+ raise(Try.invalid_argument('Block should evaluate to an instance of Try', candidate.get))
153
+ else
154
+ candidate.flatten
155
+ end
156
+ end
113
157
  end
114
158
  end
@@ -1,3 +1,3 @@
1
1
  module Ytry
2
- VERSION = "1.0.0"
2
+ VERSION = "2.0.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ytry
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - lorenzo.barasti
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-07-03 00:00:00.000000000 Z
11
+ date: 2016-08-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler