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.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/unexceptional.rb +201 -0
  3. 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: []