unexceptional 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/unexceptional.rb +201 -0
- metadata +102 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 843955cd99c06a932f0594cc96ffa64488cd596a
|
4
|
+
data.tar.gz: 38523fd3cd5282fa9bcd44ad8dd6048ed4f94089
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4188134219b235eba0f0dc541adc67b360ac723f31d8be8e945202cb1afa9d4f088f3e2a760272da71be0b624fae767a7704995d580dc578441feb6865e085b1
|
7
|
+
data.tar.gz: fd32c477e1a02bb7317fb51e87d1b9cc00cf956ab4cc3479bac8a93689968d07bf7ab75c351d65033e86a4534ab37b1eea97973276eff4d773eed19b69ac8c8c
|
@@ -0,0 +1,201 @@
|
|
1
|
+
module Unexceptional
|
2
|
+
class Result
|
3
|
+
# Pass true or false and an error value. If the first argument is true, returns an ok
|
4
|
+
# Result. If the first argument is false, returns an err Result wrapping the error
|
5
|
+
# value.
|
6
|
+
def self.check(condition, error)
|
7
|
+
condition ? ok : err(error)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Returns a new Result respresenting failure. Optionally pass a wrapped result value.
|
11
|
+
def self.err(err)
|
12
|
+
new false, nil, err
|
13
|
+
end
|
14
|
+
|
15
|
+
# Pass a block and a collection. The block must accept a member of the collection and
|
16
|
+
# return a Result.
|
17
|
+
#
|
18
|
+
# If all members succeed, returns a Result wrapping all the mapped members:
|
19
|
+
#
|
20
|
+
# Result.map([1, 2]) do |i|
|
21
|
+
# Result.ok i * 2
|
22
|
+
# end
|
23
|
+
# # => Result.ok([1, 2])
|
24
|
+
#
|
25
|
+
# Aborts on the first failure:
|
26
|
+
#
|
27
|
+
# Result.map([1, 2, 3]) do |i|
|
28
|
+
# if i == 2
|
29
|
+
# Result.err '2 is invalid'
|
30
|
+
# elsif i == 3
|
31
|
+
# raise 'This is never executed because Result.map aborts on the previous element.'
|
32
|
+
# else
|
33
|
+
# Result.ok i * 2
|
34
|
+
# end
|
35
|
+
# end
|
36
|
+
# # => Result.err('2 is invalid')
|
37
|
+
def self.map_while(collection)
|
38
|
+
Result.ok(
|
39
|
+
collection.map do |member|
|
40
|
+
result = yield member
|
41
|
+
if result.err?
|
42
|
+
return result
|
43
|
+
else
|
44
|
+
result.unwrap
|
45
|
+
end
|
46
|
+
end
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns a new Result respresenting success. Optionally pass a wrapped result value.
|
51
|
+
def self.ok(val = nil)
|
52
|
+
new true, val, nil
|
53
|
+
end
|
54
|
+
|
55
|
+
# Given a block, runs an ActiveRecord transaction. The block must return a Result. If
|
56
|
+
# the Result is an error, rolls back the transaction. Either way, returns the Result.
|
57
|
+
# You must call `require 'active_record` before you call this method.
|
58
|
+
def self.transaction
|
59
|
+
unless defined?(ActiveRecord)
|
60
|
+
raise 'ActiveRecord is not defined'
|
61
|
+
end
|
62
|
+
result = nil
|
63
|
+
ActiveRecord::Base.transaction do
|
64
|
+
result = yield
|
65
|
+
if result.err?
|
66
|
+
raise ActiveRecord::Rollback
|
67
|
+
end
|
68
|
+
end
|
69
|
+
result
|
70
|
+
end
|
71
|
+
|
72
|
+
# Tries to run a list of procs, aborting on the first failure, if any. Each proc must
|
73
|
+
# return a Result--either ok or err. Aborts on the first err, if any, returning the
|
74
|
+
# failed Result. If all procs return ok, returns the last Result.
|
75
|
+
#
|
76
|
+
# Result.try(
|
77
|
+
# -> { Result.ok 2 },
|
78
|
+
# ->(i) { Result.ok 3 * i }
|
79
|
+
# )
|
80
|
+
# # => Result.ok(6)
|
81
|
+
#
|
82
|
+
# Result.try(
|
83
|
+
# -> { Result.ok 2 },
|
84
|
+
# ->(_) { Result.err :uh_oh },
|
85
|
+
# ->(i) { Result.ok 3 * i }
|
86
|
+
# )
|
87
|
+
# # => Result.err(:uh_oh)
|
88
|
+
#
|
89
|
+
# You can also pass tuples through and pattern-match:
|
90
|
+
#
|
91
|
+
# Result.try(
|
92
|
+
# -> { Result.ok [1, 2] },
|
93
|
+
# ->((a, b)) { Result.ok a + b }
|
94
|
+
# )
|
95
|
+
# # => Result.ok(3)
|
96
|
+
def self.try(*procs)
|
97
|
+
if procs.empty?
|
98
|
+
raise 'Must past at least one proc to Result.try'
|
99
|
+
end
|
100
|
+
procs.inject(nil) do |last_result, proc|
|
101
|
+
if last_result.nil?
|
102
|
+
proc.call
|
103
|
+
elsif last_result.ok?
|
104
|
+
if proc.parameters.length == 0
|
105
|
+
proc.call
|
106
|
+
else
|
107
|
+
proc.call last_result.unwrap
|
108
|
+
end
|
109
|
+
else
|
110
|
+
last_result
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# If this Result is an err, returns self:
|
116
|
+
#
|
117
|
+
# Result
|
118
|
+
# .err(:uh_oh)
|
119
|
+
# .and_then { 'This block never executes' }
|
120
|
+
# # => Result.err(:uh_oh)
|
121
|
+
#
|
122
|
+
# If this Result is ok, then the behavior depends on what you passed to `and_then`:
|
123
|
+
#
|
124
|
+
# # Passing a single argument:
|
125
|
+
# Result
|
126
|
+
# .ok('This value gets dropped')
|
127
|
+
# .and_then(Result.ok('This is the final value'))
|
128
|
+
# # => Result.ok('This is the final value')
|
129
|
+
#
|
130
|
+
# # Passing a block:
|
131
|
+
# Result
|
132
|
+
# .ok(3)
|
133
|
+
# .and_then { |v| v * 2 }
|
134
|
+
# # => Result.ok(6)
|
135
|
+
#
|
136
|
+
# # Passing nothing:
|
137
|
+
# Result
|
138
|
+
# .ok('This value gets dropped')
|
139
|
+
# .and_then
|
140
|
+
# # => Result.ok
|
141
|
+
def and_then(next_result = nil)
|
142
|
+
if @ok
|
143
|
+
if block_given?
|
144
|
+
yield
|
145
|
+
elsif next_result
|
146
|
+
next_result
|
147
|
+
else
|
148
|
+
Result.ok
|
149
|
+
end
|
150
|
+
else
|
151
|
+
self
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# Yields this Result if this Result is an err.
|
156
|
+
def if_err
|
157
|
+
yield self.err if !@ok
|
158
|
+
self
|
159
|
+
end
|
160
|
+
|
161
|
+
# Yields this Result if this Result is ok.
|
162
|
+
def if_ok
|
163
|
+
yield self.val if @ok
|
164
|
+
self
|
165
|
+
end
|
166
|
+
|
167
|
+
# Returns the wrapped err value. Raises if this Result is ok.
|
168
|
+
def err
|
169
|
+
if !@ok
|
170
|
+
@err
|
171
|
+
else
|
172
|
+
raise "Called #err, but Result was ok."
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# Returns true if this Result is an err, false if this Result is ok.
|
177
|
+
def err?
|
178
|
+
!@ok
|
179
|
+
end
|
180
|
+
|
181
|
+
def initialize(ok, val, err) # :nodoc:
|
182
|
+
@ok = ok
|
183
|
+
@val = val
|
184
|
+
@err = err
|
185
|
+
end
|
186
|
+
|
187
|
+
# Returns true if this Result is ok, false if this Result is an err.
|
188
|
+
def ok?
|
189
|
+
@ok
|
190
|
+
end
|
191
|
+
|
192
|
+
# Returns the wrapped success value. Raises if this Result is an err.
|
193
|
+
def unwrap
|
194
|
+
if @ok
|
195
|
+
@val
|
196
|
+
else
|
197
|
+
raise "Called #unwrap on error: #{@err.inspect}"
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: unexceptional
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jarrett Colby
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-03-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: minitest
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '5'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: minitest-reporters
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: activerecord
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '4'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '4'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: sqlite3
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1'
|
69
|
+
description: Provides a Result class for more elegant, exception-free error handling.
|
70
|
+
Especially useful for processing input that could be invalid for many different
|
71
|
+
reasons.
|
72
|
+
email: jarrett@madebyhq.com
|
73
|
+
executables: []
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- lib/unexceptional.rb
|
78
|
+
homepage: https://github.com/jarrett/unexceptional
|
79
|
+
licenses:
|
80
|
+
- MIT
|
81
|
+
metadata: {}
|
82
|
+
post_install_message:
|
83
|
+
rdoc_options: []
|
84
|
+
require_paths:
|
85
|
+
- lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
requirements: []
|
97
|
+
rubyforge_project:
|
98
|
+
rubygems_version: 2.2.2
|
99
|
+
signing_key:
|
100
|
+
specification_version: 4
|
101
|
+
summary: Unexceptional
|
102
|
+
test_files: []
|