unexceptional 0.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.
- 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: []
|