ytry 1.0.0 → 2.0.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.
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