ytry 0.0.1 → 1.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 +57 -2
  3. data/lib/ytry.rb +12 -9
  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: 1d75a68288e060261679cfd5e4e94f9185230b2b
4
- data.tar.gz: 90087e023cf2c15966ccc9069af417f9e9399b9c
3
+ metadata.gz: 445fb840f8d15382ce1b280cdc6f746b907e2dfb
4
+ data.tar.gz: 559d814425ba8e70f0e23932890922d99a5393d3
5
5
  SHA512:
6
- metadata.gz: 2999648ea69177c1877b2ad818946be3216eb36d1ed2283b1d33044973ade655f3a2d06b761f2d0d60c2f23ae8e589404d1d8630e167b656626dbb8b236715a7
7
- data.tar.gz: 763fedbdf00f4465687b105964bead8bc3efdb8a6eb1d1bdfdda17d3c632b0b60860ef6d04a708df1eae02a17bbbc067fc067985f229c40268f15233b2c38038
6
+ metadata.gz: e4dcc9c8e5518b8ed3ee38e9d2c6fed5c858f6bcb64dcb72adc86e6bc61950b211d4de880cb8ed0bbbb9f79f589ea9709fda6130bfd7499599fb16c5368c4d33
7
+ data.tar.gz: 3d65b7ee4d84e21b90f42c84ba11a91cc27f8d26bfa3235bb9377823dfa97b7d712ad7ff74a1866b3c945b834f6daaa57307683fc3defc3e75628f20025c32d6
data/README.md CHANGED
@@ -23,9 +23,64 @@ Or install it yourself as:
23
23
 
24
24
  ## Basic usage
25
25
 
26
- The Try type represents a computation that may either result in an exception, or return a successfully computed value ([scala-docs](http://www.scala-lang.org/api/2.11.8/index.html#scala.util.Try))
26
+ The Try type represents a computation that may either result in an error, or return a successfully computed value.
27
27
 
28
- [TODO]
28
+ If the block passed to Try runs with no errors, then a `Success` wrapping the computed value is returned.
29
+
30
+ An instance of `Failure` wrapping the error is returned otherwise.
31
+
32
+ ```ruby
33
+ require 'ytry'
34
+ include Ytry
35
+
36
+ Try { 1 + 1 } # Success(2)
37
+
38
+ Try { 1 / 0 } # Failure(#<ZeroDivisionError: divided by 0>)
39
+ ```
40
+
41
+ `Success` and `Failure` provide a unified API that lets us express a sequence of tranformations in a fluent way, without error handling cluttering the flow:
42
+
43
+ ```ruby
44
+ def load_and_parse json_file
45
+ Try { File.read(json_file) }
46
+ .map {|content| JSON.parse(content)}
47
+ .select {|table| table.is_a? Array}
48
+ .recover {|e| puts "Recovering from #{e.message}"; []}
49
+ end
50
+
51
+ load_and_parse(nonexisting_file) # prints "Recovering from No such file..." # Success([])
52
+
53
+ load_and_parse(wrong_format_file) # prints "Recovering from Element not found" # Success([])
54
+
55
+ load_and_parse(actual_file) # Success([{"id"=>1, "name"=>"Lorenzo", "dob"=>"22/07/1985"}])
56
+ ```
57
+
58
+ `Try#map` and `Try#recover` are means to interact with the value wrapped by a Try in a safe way - i.e. with no risk of errors being raised.
59
+
60
+ `Try#select` transforms a Success into a Failure when the underlying value does not satisfy the given predicate - i.e. the given block returns false. That can be useful when validating some input.
61
+
62
+ `Try#get_or_else` provides a safe way of retrieving the possibly-missing value it contains. It returns the result of the given block when the Try is a Failure. It is equivalent to `Try#get` when the Try is a Success.
63
+
64
+ ```ruby
65
+ invalid_json = "[\"missing_quote]"
66
+
67
+ Try { JSON.parse(invalid_json) }
68
+ .get_or_else{ [] } # []
69
+
70
+ Try { JSON.parse("[]") }
71
+ .get_or_else { fail "this block is ignored"} # []
72
+ ```
73
+
74
+ It is preferable to use `Try#get_or_else` over `Try#get`, as `#get` will raise an error when called on a Failure. It is possible to check for failure via `#empty?`, but that tipically leads to non-idiomatic code
75
+
76
+ ## Why Try?
77
+
78
+ Using Try instead of rescue blocks can make your software both clearer and safer as it
79
+
80
+ - leads to less verbose error handling
81
+ - simplifies the way we deal with operations that might fail for several reasons (such as IO operations)
82
+ - privileges method chaining thus reducing the need for auxiliary variables to store intermediate results in a computation
83
+ - encourages programming towards immutability, where the data is transformed rather than mutated in place.
29
84
 
30
85
  ## Development
31
86
 
@@ -15,11 +15,17 @@ module Ytry
15
15
  raise Try.invalid_argument('Argument must be an array-like object', value) unless value.respond_to? :to_ary
16
16
  return value if value.is_a? Try
17
17
  value.to_ary.empty? ?
18
- Failure.new(RuntimeError.new("Could not convert empty array-like object to Success")) :
18
+ Failure.new(RuntimeError.new("Element not found").tap{|ex| ex.set_backtrace(caller)}) :
19
19
  Success.new(value.to_ary.first)
20
20
  end
21
- def each &block
22
- to_ary.each &block
21
+ def self.zip *try_array
22
+ first_failure = try_array.find(&:empty?)
23
+ first_failure.nil? ? Success.new(try_array.map(&:get)) : first_failure
24
+ end
25
+ def each
26
+ return enum_for(__method__) unless block_given?
27
+ yield self.get unless empty?
28
+ return self
23
29
  end
24
30
  %i(map select reject collect collect_concat).each do |method|
25
31
  define_method method, ->(&block) {
@@ -35,17 +41,14 @@ module Ytry
35
41
  Try.ary_to_type(wrapped_result.flatten)
36
42
  end
37
43
  def grep(pattern, &block)
38
- Try.ary_to_type super
44
+ self.empty? ? self : Try.ary_to_type(Try{super}.flatten)
39
45
  end
40
46
  def flatten
41
47
  return self if empty?
42
48
  Try.ary_to_type self.get
43
49
  end
44
50
  def zip *others
45
- # TODO return first Failure among arguments - if any
46
- return Failure.new(Exception.new) if self.empty? || others.any?(&:empty?)
47
- collection = others.reduce(self.to_a, &:concat)
48
- Success.new collection
51
+ Try.zip(self, *others)
49
52
  end
50
53
  def | lambda
51
54
  self.flat_map &lambda # slow but easy to read + supports symbols out of the box
@@ -94,7 +97,7 @@ module Ytry
94
97
  end
95
98
  def get() raise @error end
96
99
  def empty?() true end
97
- def to_s() "Failure(#{@error})" end
100
+ def to_s() "Failure(#{@error.inspect})" end
98
101
  def to_ary() [] end
99
102
  def == other
100
103
  other.is_a?(Failure) && self.error == other.error
@@ -1,3 +1,3 @@
1
1
  module Ytry
2
- VERSION = "0.0.1"
2
+ VERSION = "1.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: 0.0.1
4
+ version: 1.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-06-25 00:00:00.000000000 Z
11
+ date: 2016-07-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler