tron 1.2.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +41 -24
- data/lib/tron/version.rb +5 -3
- data/lib/tron.rb +9 -30
- metadata +11 -69
- data/lib/tron/failure.rb +0 -13
- data/lib/tron/resultable.rb +0 -65
- data/lib/tron/success.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dc919787c5b549b14e26fc8d2d2a1cfd601bfbf6320751e0e4862d23856ec604
|
4
|
+
data.tar.gz: 74e32cc0cd310b830bb58bb5a92c4e44f4a966d9271557ad7482337051d8dcf5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 82a40a2046b62244b66cf7dbc52210215359b9f61112bd4836363b65491f7aec939be457d9a67de6bc7093cd9bdfc7676074a4cc791ba14a64389d18891c429f
|
7
|
+
data.tar.gz: 51149e095d5bf173eafeb4a99f46b4890bc47f6dcee8b8c8cbe4337ca59f5c94df436073b263aa2d1b152330a3e50d5654f15d0193728d174164e715f00e2923
|
data/README.md
CHANGED
@@ -1,21 +1,18 @@
|
|
1
1
|
[![Gem Version](https://img.shields.io/gem/v/tron.svg)](https://rubygems.org/gems/tron)
|
2
|
-
[![Build Status](https://
|
2
|
+
[![Build Status](https://github.com/halo/tron/actions/workflows/main.yml/badge.svg)](https://github.com/halo/tron/actions)
|
3
3
|
[![License](http://img.shields.io/badge/license-MIT-blue.svg)](http://github.com/halo/tron/blob/master/LICENSE.md)
|
4
4
|
|
5
5
|
## TL;DR
|
6
6
|
|
7
7
|
Tron is a minimalistic combination of a [monad](https://www.morozov.is/2018/09/08/monad-laws-in-ruby.html) and [value object](https://madeintandem.com/blog/creating-value-objects-in-ruby/), implemented in [a few lines](https://github.com/halo/tron/blob/master/lib/tron.rb) of code.
|
8
8
|
|
9
|
-
Return `Tron.success(:it_worked)` or `Tron.failure(:aww_too_bad)` from a method to explain why and how it succeded
|
10
|
-
|
11
|
-
The
|
12
|
-
|
13
|
-
Chaining can make your code cleaner: `result.on_success { download }.on_failure { show_message }`
|
9
|
+
* Return `Tron.success(:it_worked)` or `Tron.failure(:aww_too_bad)` from a method, to explain why and how it succeded or failed. That returns an immutable Data (value object) that responds to `result.success?` and `result.failure?`.
|
10
|
+
* Add metadata as a second argument: `Tron.failure(:nopes, error_code: 404)` which you then can access: `result.error_code #=> 404`.
|
11
|
+
* The result can also be queried with `result.success #=> :it_worked` and `result.failure #=> nil`.
|
12
|
+
* Chaining can make your code cleaner: `result.on_success { download }.on_failure { show_message }`
|
14
13
|
|
15
14
|
## Introduction
|
16
15
|
|
17
|
-
|
18
|
-
|
19
16
|
Imagine you have a class like this:
|
20
17
|
|
21
18
|
```ruby
|
@@ -42,7 +39,7 @@ class User
|
|
42
39
|
if @users.delete id
|
43
40
|
Tron.success :user_deleted, user: user
|
44
41
|
else
|
45
|
-
Tron.success :
|
42
|
+
Tron.success :already_deleted, id: id # Notice the success here
|
46
43
|
end
|
47
44
|
|
48
45
|
rescue ConnectionError
|
@@ -51,7 +48,7 @@ class User
|
|
51
48
|
end
|
52
49
|
```
|
53
50
|
|
54
|
-
One could
|
51
|
+
One could break the functionality apart into smaller pieces:
|
55
52
|
|
56
53
|
```ruby
|
57
54
|
class User
|
@@ -64,13 +61,13 @@ class User
|
|
64
61
|
def self.check_id_syntax(id)
|
65
62
|
return Tron.failure(:id_missing) unless id
|
66
63
|
return Tron.failure(:invalid_id, id: id) unless id.match /[a-f]{8}/
|
67
|
-
|
64
|
+
|
68
65
|
Tron.success :id_looks_good
|
69
66
|
end
|
70
67
|
|
71
68
|
def self.delete_user(id)
|
72
69
|
user = @users[id]
|
73
|
-
|
70
|
+
|
74
71
|
if @users.delete id
|
75
72
|
Tron.success :user_deleted, user: user
|
76
73
|
else
|
@@ -83,11 +80,20 @@ class User
|
|
83
80
|
end
|
84
81
|
```
|
85
82
|
|
86
|
-
|
83
|
+
On a side-note, the data object can be passed on further with modifications, that's due to the way `Data` object work.
|
87
84
|
|
88
|
-
|
85
|
+
```ruby
|
86
|
+
result = Tron.success(:api_not_responding, reason: :password_not_accepted)
|
87
|
+
|
88
|
+
result.with(code: :could_not_delete_user)
|
89
|
+
# => "#<data failure=:could_not_delete_user, reason=:password_not_accepted>"
|
90
|
+
```
|
89
91
|
|
90
|
-
|
92
|
+
## So, what are the benefits?
|
93
|
+
|
94
|
+
### 1. An internal API that doesn't change over time
|
95
|
+
|
96
|
+
Tron will give you a consistent, implementation-unaware, programming convention. That means that you can decide later, what constitutes a success or a failure, without changing the way the result is handled. You could also add metadata after-the-fact and the following code would still work fine:
|
91
97
|
|
92
98
|
```ruby
|
93
99
|
result = User.delete 42
|
@@ -99,7 +105,7 @@ else
|
|
99
105
|
end
|
100
106
|
```
|
101
107
|
|
102
|
-
The result is just
|
108
|
+
The result is just an instance of Data:
|
103
109
|
|
104
110
|
```ruby
|
105
111
|
result = User.delete 42
|
@@ -114,9 +120,7 @@ result.failure # => :deletion_failed_badly
|
|
114
120
|
|
115
121
|
# Access immutable metadata
|
116
122
|
result.message # => "..."
|
117
|
-
result.inspect # => "#<
|
118
|
-
|
119
|
-
result.message.upcase! # => modification raises an exception
|
123
|
+
result.inspect # => "#<data failure=:alright, user_id=42, message='...'>"
|
120
124
|
```
|
121
125
|
|
122
126
|
### 2. If will give you better tests
|
@@ -138,11 +142,20 @@ class Product
|
|
138
142
|
end
|
139
143
|
```
|
140
144
|
|
141
|
-
You cannot simply test for
|
145
|
+
You cannot simply test for `false` as expected return value, because it could mean anything. Tron helps you to check the response objects for every case. Data objects even support deconstruction for `case` statements.
|
142
146
|
|
143
147
|
### 3. It gives you documentation
|
144
148
|
|
145
|
-
While the code you're writing becomes slightly more verbose, that verbosity translates directly into
|
149
|
+
While the code you're writing becomes slightly more verbose, that verbosity translates directly into documentation. You see immediately what each line is doing.
|
150
|
+
|
151
|
+
## Upgrading from 2.0.0 to 3.0.0
|
152
|
+
|
153
|
+
* You will need to use at least Ruby `3.2`
|
154
|
+
* The result object doesn't respond to collection methods any more, such as `result[:some_key]` or `result.to_a`, but it's unlikely that you relied on them in the first place.
|
155
|
+
|
156
|
+
## Upgrading from 1.x.x to 2.0.0
|
157
|
+
|
158
|
+
* `1.2.0` and `2.0.0` are identical, except that all deprecations have been removed and don't work any more.
|
146
159
|
|
147
160
|
## Upgrading from 0.x.x to 1.x.x
|
148
161
|
|
@@ -157,12 +170,16 @@ Tron is a complete rewrite of its predecessor [operation](https://github.com/hal
|
|
157
170
|
|
158
171
|
## Requirements
|
159
172
|
|
160
|
-
* Ruby >= 2.
|
173
|
+
* Ruby >= 3.2.0
|
174
|
+
|
175
|
+
## Development
|
176
|
+
|
177
|
+
Clone the repository, run `bundle install` and run the tests with `bundle exec rake`.
|
161
178
|
|
162
179
|
## Copyright
|
163
180
|
|
164
|
-
MIT
|
181
|
+
MIT halo. See [MIT-LICENSE](http://github.com/halo/tron/blob/master/LICENSE.txt).
|
165
182
|
|
166
183
|
## Caveats
|
167
184
|
|
168
|
-
* There are no setter methods in the returned
|
185
|
+
* There are no setter methods in the returned Data, so you cannot overwrite the metadata. But you can use `Data#with` to essentially clone the object and change values.
|
data/lib/tron/version.rb
CHANGED
data/lib/tron.rb
CHANGED
@@ -2,12 +2,9 @@
|
|
2
2
|
|
3
3
|
require 'tron/version'
|
4
4
|
|
5
|
-
|
6
|
-
require 'tron/success' # Legacy
|
7
|
-
require 'tron/failure' # Legacy
|
8
|
-
|
5
|
+
# Return data objects that can indicate success or failure.
|
9
6
|
module Tron
|
10
|
-
def self.success(code, attributes = {}) # rubocop:disable Metrics/MethodLength
|
7
|
+
def self.success(code, attributes = {}) # rubocop:disable Metrics/MethodLength
|
11
8
|
code.respond_to?(:to_sym) ||
|
12
9
|
raise(ArgumentError, 'Tron.success must be called with a Symbol as first argument')
|
13
10
|
|
@@ -15,12 +12,10 @@ module Tron
|
|
15
12
|
raise(ArgumentError, 'The second argument (metadata) for Tron.success must respond to #keys')
|
16
13
|
|
17
14
|
attributes.respond_to?(:values) ||
|
18
|
-
raise(ArgumentError,
|
19
|
-
|
20
|
-
Struct.new(:success, *attributes.keys) do
|
21
|
-
undef_method :[]=
|
22
|
-
members.each { |member| undef_method :"#{member}=" }
|
15
|
+
raise(ArgumentError,
|
16
|
+
'The second argument (metadata) for Tron.success must respond to #values')
|
23
17
|
|
18
|
+
Data.define(:success, *attributes.keys) do
|
24
19
|
def success?
|
25
20
|
true
|
26
21
|
end
|
@@ -33,13 +28,6 @@ module Tron
|
|
33
28
|
nil
|
34
29
|
end
|
35
30
|
|
36
|
-
def code
|
37
|
-
warn 'DEPRECATION WARNING: Calling `#code` on a Tron object is deprecated and will be removed in Tron 2.0.0. ' \
|
38
|
-
"Please use `#success` instead. Called from `#{caller.first}`"
|
39
|
-
|
40
|
-
success
|
41
|
-
end
|
42
|
-
|
43
31
|
def on_success(proc = nil, &block)
|
44
32
|
(proc || block).call self
|
45
33
|
end
|
@@ -50,7 +38,7 @@ module Tron
|
|
50
38
|
end.new code.to_sym, *attributes.values
|
51
39
|
end
|
52
40
|
|
53
|
-
def self.failure(code, attributes = {}) # rubocop:disable Metrics/MethodLength
|
41
|
+
def self.failure(code, attributes = {}) # rubocop:disable Metrics/MethodLength
|
54
42
|
code.respond_to?(:to_sym) ||
|
55
43
|
raise(ArgumentError, 'Tron.failure must be called with a Symbol as first argument')
|
56
44
|
|
@@ -58,12 +46,10 @@ module Tron
|
|
58
46
|
raise(ArgumentError, 'The second argument (metadata) for Tron.failure must respond to #keys')
|
59
47
|
|
60
48
|
attributes.respond_to?(:values) ||
|
61
|
-
raise(ArgumentError,
|
62
|
-
|
63
|
-
Struct.new(:failure, *attributes.keys) do
|
64
|
-
undef_method :[]=
|
65
|
-
members.each { |member| undef_method :"#{member}=" }
|
49
|
+
raise(ArgumentError,
|
50
|
+
'The second argument (metadata) for Tron.failure must respond to #values')
|
66
51
|
|
52
|
+
Data.define(:failure, *attributes.keys) do
|
67
53
|
def success?
|
68
54
|
false
|
69
55
|
end
|
@@ -76,13 +62,6 @@ module Tron
|
|
76
62
|
nil
|
77
63
|
end
|
78
64
|
|
79
|
-
def code
|
80
|
-
warn 'DEPRECATION WARNING: Calling `#code` on a Tron object is deprecated and will be removed in Tron 2.0.0. ' \
|
81
|
-
"Please use `#failure` instead. Called from `#{caller.first}`"
|
82
|
-
|
83
|
-
failure
|
84
|
-
end
|
85
|
-
|
86
65
|
def on_success(_ = nil)
|
87
66
|
self
|
88
67
|
end
|
metadata
CHANGED
@@ -1,90 +1,32 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tron
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- halo
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
12
|
-
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: guard-rspec
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
20
|
-
type: :development
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '0'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: hashie
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: rb-fsevent
|
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: rspec
|
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'
|
11
|
+
date: 2023-08-25 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
69
13
|
description: General-purpose method return objects that can be chained. Think minimalistic
|
70
14
|
value object monads. Heavily inspired by the `deterministic` gem, but much much
|
71
15
|
more light-weight.
|
72
|
-
email:
|
16
|
+
email:
|
73
17
|
executables: []
|
74
18
|
extensions: []
|
75
19
|
extra_rdoc_files: []
|
76
20
|
files:
|
77
21
|
- README.md
|
78
22
|
- lib/tron.rb
|
79
|
-
- lib/tron/failure.rb
|
80
|
-
- lib/tron/resultable.rb
|
81
|
-
- lib/tron/success.rb
|
82
23
|
- lib/tron/version.rb
|
83
24
|
homepage: https://github.com/halo/tron
|
84
25
|
licenses:
|
85
26
|
- MIT
|
86
|
-
metadata:
|
87
|
-
|
27
|
+
metadata:
|
28
|
+
rubygems_mfa_required: 'true'
|
29
|
+
post_install_message:
|
88
30
|
rdoc_options: []
|
89
31
|
require_paths:
|
90
32
|
- lib
|
@@ -92,15 +34,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
92
34
|
requirements:
|
93
35
|
- - ">="
|
94
36
|
- !ruby/object:Gem::Version
|
95
|
-
version: 2.
|
37
|
+
version: 3.2.0
|
96
38
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
97
39
|
requirements:
|
98
40
|
- - ">="
|
99
41
|
- !ruby/object:Gem::Version
|
100
42
|
version: '0'
|
101
43
|
requirements: []
|
102
|
-
rubygems_version: 3.
|
103
|
-
signing_key:
|
44
|
+
rubygems_version: 3.4.18
|
45
|
+
signing_key:
|
104
46
|
specification_version: 4
|
105
47
|
summary: General-purpose method return objects that can be chained.
|
106
48
|
test_files: []
|
data/lib/tron/failure.rb
DELETED
data/lib/tron/resultable.rb
DELETED
@@ -1,65 +0,0 @@
|
|
1
|
-
module Tron
|
2
|
-
module Resultable
|
3
|
-
attr_reader :metadata
|
4
|
-
|
5
|
-
def self.included(receiver)
|
6
|
-
receiver.extend ::Tron::Resultable::ClassMethods
|
7
|
-
end
|
8
|
-
|
9
|
-
module ClassMethods
|
10
|
-
# Convenience wrapper
|
11
|
-
def call(code, metadata = nil)
|
12
|
-
new code: code, metadata: metadata
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def initialize(code: nil, metadata: nil)
|
17
|
-
@code = code
|
18
|
-
@metadata = metadata
|
19
|
-
warn 'DEPRECATION WARNING: As of Tron 1.0.0 calls to `Tron::Success.call` and `Tron::Failure.call` are deprecated. ' \
|
20
|
-
'They will be removed in Tron 2.0.0. Please migrate using `Tron.success` and `Tron.failure`. ' \
|
21
|
-
"See github.com/halo/tron Called in:: `#{caller[2]}`"
|
22
|
-
end
|
23
|
-
|
24
|
-
def success?
|
25
|
-
is_a? ::Tron::Success
|
26
|
-
end
|
27
|
-
|
28
|
-
def failure?
|
29
|
-
is_a? ::Tron::Failure
|
30
|
-
end
|
31
|
-
|
32
|
-
def code
|
33
|
-
return if @code.to_s == ''
|
34
|
-
|
35
|
-
@code.to_s.to_sym
|
36
|
-
end
|
37
|
-
|
38
|
-
# Convenience Wrapper
|
39
|
-
def object
|
40
|
-
metadata[:object] || metadata['object']
|
41
|
-
rescue StandardError
|
42
|
-
nil
|
43
|
-
end
|
44
|
-
|
45
|
-
def meta
|
46
|
-
if defined? ::Hashie::Mash
|
47
|
-
metamash
|
48
|
-
else
|
49
|
-
metadata
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
private
|
54
|
-
|
55
|
-
def metamash
|
56
|
-
if metadata.respond_to? :each_pair
|
57
|
-
::Hashie::Mash.new metadata
|
58
|
-
elsif metadata
|
59
|
-
metadata
|
60
|
-
else
|
61
|
-
::Hashie::Mash.new
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|