result-monad 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +190 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/lib/result/error.rb +27 -0
- data/lib/result/ok.rb +18 -0
- data/lib/result/version.rb +3 -0
- data/lib/result.rb +138 -0
- data/result.gemspec +27 -0
- metadata +118 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a46ce98ac67bb98c771e13aa150b09f2bbfd913f
|
4
|
+
data.tar.gz: 6de578017fbab8aa918ee5c5dff8aaac2c9bf0a5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4efe4a7c1d80d5485dfb861df83f7a22132bd404ea579e050b73ed8bdd6f87268ef98ee8293a11b6a5a8b8fe53ce38e54fe8349f2c3a49167a46cce34da022d7
|
7
|
+
data.tar.gz: 125c81ad50838e45931509ddb653355bccc27baa253dffcb063a804628cf8a3629e5bf27403306f4222eaf6886bce8a10e61becd6e36750ef54f4b270fe15ef2
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
4
|
+
|
5
|
+
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
|
6
|
+
|
7
|
+
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
8
|
+
|
9
|
+
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
10
|
+
|
11
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
|
12
|
+
|
13
|
+
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Zachary Daniel
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,190 @@
|
|
1
|
+
# Result
|
2
|
+
## Installation
|
3
|
+
|
4
|
+
Add this line to your application's Gemfile:
|
5
|
+
|
6
|
+
```ruby
|
7
|
+
gem 'result'
|
8
|
+
```
|
9
|
+
|
10
|
+
And then execute:
|
11
|
+
|
12
|
+
$ bundle
|
13
|
+
|
14
|
+
Or install it yourself as:
|
15
|
+
|
16
|
+
$ gem install result
|
17
|
+
|
18
|
+
## Usage
|
19
|
+
|
20
|
+
Use of the Result Monad is a paradigm that is baked into Rust and can lead to very good error handling. This is an
|
21
|
+
initial design, and will need to be refined. The `Capture()` constructor makes it quite easy to integrate Result into
|
22
|
+
a preexisting code base.
|
23
|
+
|
24
|
+
### Examples
|
25
|
+
|
26
|
+
#### `Capture` success into an Ok result, and exceptions into an Error result via
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
|
30
|
+
Capture { 100 / 10 } #=> Result<Ok(10)>
|
31
|
+
|
32
|
+
Capture { 100 / 0 } #=> Result<Error(divided by 0)>
|
33
|
+
|
34
|
+
```
|
35
|
+
|
36
|
+
#### Construct Ok or Error yourself
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
def can_go_wrong
|
40
|
+
res = do_operation
|
41
|
+
if res.meets_expectations
|
42
|
+
Ok(res)
|
43
|
+
else
|
44
|
+
Error(res)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
```
|
49
|
+
|
50
|
+
#### Basic Dichotomy
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
|
54
|
+
def divide(x, y)
|
55
|
+
Capture { x / y }
|
56
|
+
end
|
57
|
+
|
58
|
+
x = divide 50, 10 #=> Result<Ok(5)>
|
59
|
+
x.ok? #=> true
|
60
|
+
x.error? #=> false
|
61
|
+
|
62
|
+
y = divide 50, 0 #=> Result<Error(divided by 0)>
|
63
|
+
y.error? #=> true
|
64
|
+
y.ok? #=> false
|
65
|
+
```
|
66
|
+
|
67
|
+
#### Chaining computations with map
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
x = Ok(100)
|
71
|
+
|
72
|
+
x.map {|i| i / 10}
|
73
|
+
.map {|i| i / 5}
|
74
|
+
.map {|i| i / 1}
|
75
|
+
#=> Result<Ok(2)>
|
76
|
+
```
|
77
|
+
|
78
|
+
#### Carrying errors through computations
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
x = Ok(100)
|
82
|
+
|
83
|
+
x.map {|i| i / 10}
|
84
|
+
.map {|i| i / 0}
|
85
|
+
.map {|i| i / 10}
|
86
|
+
|
87
|
+
#=>Result<Error(divided by 0)>
|
88
|
+
```
|
89
|
+
|
90
|
+
#### Performing actions in sequence
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
x = Ok(5)
|
94
|
+
y = Error(99)
|
95
|
+
z = Ok(25)
|
96
|
+
|
97
|
+
# In this case, imagine x and z as successful actions, whereas y is an unsuccessful action.
|
98
|
+
|
99
|
+
x & y & z #=> Result<Error(99)>
|
100
|
+
|
101
|
+
```
|
102
|
+
|
103
|
+
#### Real World Scenario
|
104
|
+
|
105
|
+
Lets say we are using an external library to read data from a file over FTP,
|
106
|
+
write it to a local file, and then delete the file on the FTP server. Some things to note:
|
107
|
+
|
108
|
+
* We use overloaded operator & to chain on Ok
|
109
|
+
* Every call to any outside library, even standard library functions are either in a `map` block which
|
110
|
+
gets Captured or they are themselves Captured
|
111
|
+
* No independent action (as in someone calling `copy_file` directly) will execute without its requirements being met.
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
require 'net/ftp'
|
115
|
+
|
116
|
+
class SensitiveFileFTP
|
117
|
+
def initialize(server_uri)
|
118
|
+
@server_uri = server_uri
|
119
|
+
end
|
120
|
+
|
121
|
+
def transfer_and_delete(file_name)
|
122
|
+
login & # "&" chains on Ok
|
123
|
+
copy_file(file_name) &
|
124
|
+
ensure_file(file_name) &
|
125
|
+
delete_remote_file(file_name)
|
126
|
+
end
|
127
|
+
|
128
|
+
def ftp
|
129
|
+
@ftp ||= Capture { Net::FTP.new(@server_uri) }
|
130
|
+
end
|
131
|
+
|
132
|
+
def login
|
133
|
+
ftp.map(&:login)
|
134
|
+
end
|
135
|
+
|
136
|
+
def copy_file(source)
|
137
|
+
ftp.map { |f| f.getbinaryfile(source) }
|
138
|
+
end
|
139
|
+
|
140
|
+
def ensure_file(name)
|
141
|
+
Capture { raise "Can not verify local copy of file" unless File.exist?(name) }
|
142
|
+
end
|
143
|
+
|
144
|
+
def delete_remote_file(name)
|
145
|
+
ftp.map {|f| f.delete(name) }
|
146
|
+
end
|
147
|
+
|
148
|
+
def delete_local_file(name)
|
149
|
+
Capture { File.delete(name) }
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
ftp = SensitiveFileFTP.new('speedtest.tele2.net')
|
154
|
+
ftp.transfer_and_delete("5MB.zip")
|
155
|
+
```
|
156
|
+
|
157
|
+
`transfer_and_delete` can return numerous errors but there is no way for this code to raise an exception. Exceptions
|
158
|
+
raised in `map` or `map!` are caught and expressed as `Error`.
|
159
|
+
|
160
|
+
|
161
|
+
|
162
|
+
## What's next
|
163
|
+
|
164
|
+
* More documentation and refined use cases for existing methods, like `map_err`, `map!`, and `|`
|
165
|
+
|
166
|
+
* refine methods of conditionally executing blocks. Should the return values be wrapped in results, or should we trust
|
167
|
+
the users to do it?
|
168
|
+
|
169
|
+
* Right now, the only computational state kept is the last good result or the first error that occurred.
|
170
|
+
This may not be ideal or appropriate. We also don't join errors up the chain, so it is possible to end up with
|
171
|
+
something like this: `Error(Error(Error(Error(Error(...)))))`. We either need to find a way to join, and perhaps
|
172
|
+
store the errors as some aggregate object, or we need to provide utilities for probing for information on the different
|
173
|
+
nested errors. I think it may be appropriate to kep a collection of errors, and ensure that we don't nest.
|
174
|
+
|
175
|
+
|
176
|
+
## Development
|
177
|
+
|
178
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
179
|
+
|
180
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
181
|
+
|
182
|
+
## Contributing
|
183
|
+
|
184
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/result. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
|
185
|
+
|
186
|
+
|
187
|
+
## License
|
188
|
+
|
189
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
190
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "result"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/lib/result/error.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
class Error
|
2
|
+
def initialize(error)
|
3
|
+
@error = error
|
4
|
+
end
|
5
|
+
|
6
|
+
def map!(&block)
|
7
|
+
@error = Capture {block.call(@error)}
|
8
|
+
self
|
9
|
+
end
|
10
|
+
|
11
|
+
def map(&block)
|
12
|
+
capture_as_error { block.call(@error) }
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
"Error(#{@error})"
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def capture_as_error
|
21
|
+
begin
|
22
|
+
Error(yield)
|
23
|
+
rescue StandardError => e
|
24
|
+
Error(e)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/result/ok.rb
ADDED
data/lib/result.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
require "result/version"
|
2
|
+
require 'byebug'
|
3
|
+
|
4
|
+
def Error(error)
|
5
|
+
Result.error(error)
|
6
|
+
end
|
7
|
+
|
8
|
+
def Ok(value)
|
9
|
+
Result.ok(value)
|
10
|
+
end
|
11
|
+
|
12
|
+
def Capture
|
13
|
+
begin
|
14
|
+
Ok(yield)
|
15
|
+
rescue StandardError => e
|
16
|
+
Error(e)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Result
|
21
|
+
require 'result/error'
|
22
|
+
require 'result/ok'
|
23
|
+
|
24
|
+
def initialize(m)
|
25
|
+
@m = m
|
26
|
+
end
|
27
|
+
|
28
|
+
def ok?
|
29
|
+
@m.is_a? Ok
|
30
|
+
end
|
31
|
+
|
32
|
+
def error?
|
33
|
+
@m.is_a? Error
|
34
|
+
end
|
35
|
+
|
36
|
+
def map!(&block)
|
37
|
+
if ok?
|
38
|
+
@m.map!(&block)
|
39
|
+
self.lift!(val)
|
40
|
+
else
|
41
|
+
self
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def map_err!(&block)
|
46
|
+
if error?
|
47
|
+
@m.map!(&block)
|
48
|
+
else
|
49
|
+
self
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def map(&block)
|
54
|
+
if ok?
|
55
|
+
@m.map(&block)
|
56
|
+
else
|
57
|
+
self
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def map_err(&block)
|
62
|
+
if error?
|
63
|
+
@m.map(&block)
|
64
|
+
else
|
65
|
+
self
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def join!
|
70
|
+
if ok? && val.is_a?(Result)
|
71
|
+
lift!(val)
|
72
|
+
end
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
76
|
+
def lift!(other)
|
77
|
+
@m = (self & other.join!).instance_variable_get(:"@m")
|
78
|
+
self
|
79
|
+
end
|
80
|
+
|
81
|
+
def unwrap
|
82
|
+
raise ResultError.new(self), "Attempted to unwrap an error" if @m.is_a? Error
|
83
|
+
val
|
84
|
+
end
|
85
|
+
|
86
|
+
def and(other)
|
87
|
+
error? ? self : other
|
88
|
+
end
|
89
|
+
alias_method :&, :and
|
90
|
+
|
91
|
+
def or(other)
|
92
|
+
error? ? other : self
|
93
|
+
end
|
94
|
+
alias_method :|, :or
|
95
|
+
|
96
|
+
def and_then(&block)
|
97
|
+
Capture {block.call} if ok?
|
98
|
+
end
|
99
|
+
|
100
|
+
def or_else(&block)
|
101
|
+
Capture {block.call} if error?
|
102
|
+
end
|
103
|
+
|
104
|
+
def unwrap_or(cons)
|
105
|
+
ok? ? unwrap : cons
|
106
|
+
end
|
107
|
+
|
108
|
+
# Constructors
|
109
|
+
def self.error(error)
|
110
|
+
new(Error.new(error))
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.ok(result)
|
114
|
+
new(Ok.new(result))
|
115
|
+
end
|
116
|
+
|
117
|
+
def to_s
|
118
|
+
@m.to_s
|
119
|
+
end
|
120
|
+
|
121
|
+
def inspect
|
122
|
+
"Result<#{self}>"
|
123
|
+
end
|
124
|
+
|
125
|
+
private
|
126
|
+
|
127
|
+
def val
|
128
|
+
@m.instance_variable_get(:"@value")
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
class ResultError < StandardError
|
133
|
+
attr_reader :result
|
134
|
+
|
135
|
+
def initialize(result)
|
136
|
+
@result = result
|
137
|
+
end
|
138
|
+
end
|
data/result.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'result/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "result-monad"
|
8
|
+
spec.version = Result::VERSION
|
9
|
+
spec.authors = ["Zachary Daniel"]
|
10
|
+
spec.email = ["zachary.s.daniel@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = "The Result Monad implemented in ruby."
|
13
|
+
spec.description = "The Result Monad implemented in ruby. Designed to maximize usefulness, as sticking to the category theoretical Monad is not really feasible without an evolved static type system."
|
14
|
+
spec.homepage = "http://gitlab.com/zachdaniel/result"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
spec.required_ruby_version = '~> 2.0'
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
24
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
25
|
+
spec.add_development_dependency "rspec"
|
26
|
+
spec.add_development_dependency "byebug"
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: result-monad
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Zachary Daniel
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-11-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.10'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: byebug
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: The Result Monad implemented in ruby. Designed to maximize usefulness,
|
70
|
+
as sticking to the category theoretical Monad is not really feasible without an
|
71
|
+
evolved static type system.
|
72
|
+
email:
|
73
|
+
- zachary.s.daniel@gmail.com
|
74
|
+
executables: []
|
75
|
+
extensions: []
|
76
|
+
extra_rdoc_files: []
|
77
|
+
files:
|
78
|
+
- ".gitignore"
|
79
|
+
- ".rspec"
|
80
|
+
- ".travis.yml"
|
81
|
+
- CODE_OF_CONDUCT.md
|
82
|
+
- Gemfile
|
83
|
+
- LICENSE.txt
|
84
|
+
- README.md
|
85
|
+
- Rakefile
|
86
|
+
- bin/console
|
87
|
+
- bin/setup
|
88
|
+
- lib/result.rb
|
89
|
+
- lib/result/error.rb
|
90
|
+
- lib/result/ok.rb
|
91
|
+
- lib/result/version.rb
|
92
|
+
- result.gemspec
|
93
|
+
homepage: http://gitlab.com/zachdaniel/result
|
94
|
+
licenses:
|
95
|
+
- MIT
|
96
|
+
metadata: {}
|
97
|
+
post_install_message:
|
98
|
+
rdoc_options: []
|
99
|
+
require_paths:
|
100
|
+
- lib
|
101
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - "~>"
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '2.0'
|
106
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
requirements: []
|
112
|
+
rubyforge_project:
|
113
|
+
rubygems_version: 2.4.5
|
114
|
+
signing_key:
|
115
|
+
specification_version: 4
|
116
|
+
summary: The Result Monad implemented in ruby.
|
117
|
+
test_files: []
|
118
|
+
has_rdoc:
|