tron 2.0.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 +36 -23
- data/lib/tron/version.rb +4 -2
- data/lib/tron.rb +9 -12
- metadata +11 -94
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
|
@@ -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.
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
result = Tron.success(:api_not_responding, reason: :password_not_accepted)
|
87
87
|
|
88
|
-
|
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,15 +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.
|
146
155
|
|
147
156
|
## Upgrading from 1.x.x to 2.0.0
|
148
157
|
|
149
|
-
* 1.2.0 and 2.0.0 are identical, except that all deprecations have been removed and don't work any more.
|
158
|
+
* `1.2.0` and `2.0.0` are identical, except that all deprecations have been removed and don't work any more.
|
150
159
|
|
151
160
|
## Upgrading from 0.x.x to 1.x.x
|
152
161
|
|
@@ -161,12 +170,16 @@ Tron is a complete rewrite of its predecessor [operation](https://github.com/hal
|
|
161
170
|
|
162
171
|
## Requirements
|
163
172
|
|
164
|
-
* 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`.
|
165
178
|
|
166
179
|
## Copyright
|
167
180
|
|
168
|
-
MIT
|
181
|
+
MIT halo. See [MIT-LICENSE](http://github.com/halo/tron/blob/master/LICENSE.txt).
|
169
182
|
|
170
183
|
## Caveats
|
171
184
|
|
172
|
-
* 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,8 +2,9 @@
|
|
2
2
|
|
3
3
|
require 'tron/version'
|
4
4
|
|
5
|
+
# Return data objects that can indicate success or failure.
|
5
6
|
module Tron
|
6
|
-
def self.success(code, attributes = {}) # rubocop:disable Metrics/MethodLength
|
7
|
+
def self.success(code, attributes = {}) # rubocop:disable Metrics/MethodLength
|
7
8
|
code.respond_to?(:to_sym) ||
|
8
9
|
raise(ArgumentError, 'Tron.success must be called with a Symbol as first argument')
|
9
10
|
|
@@ -11,12 +12,10 @@ module Tron
|
|
11
12
|
raise(ArgumentError, 'The second argument (metadata) for Tron.success must respond to #keys')
|
12
13
|
|
13
14
|
attributes.respond_to?(:values) ||
|
14
|
-
raise(ArgumentError,
|
15
|
-
|
16
|
-
Struct.new(:success, *attributes.keys) do
|
17
|
-
undef_method :[]=
|
18
|
-
members.each { |member| undef_method :"#{member}=" }
|
15
|
+
raise(ArgumentError,
|
16
|
+
'The second argument (metadata) for Tron.success must respond to #values')
|
19
17
|
|
18
|
+
Data.define(:success, *attributes.keys) do
|
20
19
|
def success?
|
21
20
|
true
|
22
21
|
end
|
@@ -39,7 +38,7 @@ module Tron
|
|
39
38
|
end.new code.to_sym, *attributes.values
|
40
39
|
end
|
41
40
|
|
42
|
-
def self.failure(code, attributes = {}) # rubocop:disable Metrics/MethodLength
|
41
|
+
def self.failure(code, attributes = {}) # rubocop:disable Metrics/MethodLength
|
43
42
|
code.respond_to?(:to_sym) ||
|
44
43
|
raise(ArgumentError, 'Tron.failure must be called with a Symbol as first argument')
|
45
44
|
|
@@ -47,12 +46,10 @@ module Tron
|
|
47
46
|
raise(ArgumentError, 'The second argument (metadata) for Tron.failure must respond to #keys')
|
48
47
|
|
49
48
|
attributes.respond_to?(:values) ||
|
50
|
-
raise(ArgumentError,
|
51
|
-
|
52
|
-
Struct.new(:failure, *attributes.keys) do
|
53
|
-
undef_method :[]=
|
54
|
-
members.each { |member| undef_method :"#{member}=" }
|
49
|
+
raise(ArgumentError,
|
50
|
+
'The second argument (metadata) for Tron.failure must respond to #values')
|
55
51
|
|
52
|
+
Data.define(:failure, *attributes.keys) do
|
56
53
|
def success?
|
57
54
|
false
|
58
55
|
end
|
metadata
CHANGED
@@ -1,103 +1,19 @@
|
|
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'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: rubocop
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ">="
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0'
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ">="
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '0'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: rubocop-rspec
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - ">="
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
90
|
-
type: :development
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - ">="
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: '0'
|
11
|
+
date: 2023-08-25 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
97
13
|
description: General-purpose method return objects that can be chained. Think minimalistic
|
98
14
|
value object monads. Heavily inspired by the `deterministic` gem, but much much
|
99
15
|
more light-weight.
|
100
|
-
email:
|
16
|
+
email:
|
101
17
|
executables: []
|
102
18
|
extensions: []
|
103
19
|
extra_rdoc_files: []
|
@@ -108,8 +24,9 @@ files:
|
|
108
24
|
homepage: https://github.com/halo/tron
|
109
25
|
licenses:
|
110
26
|
- MIT
|
111
|
-
metadata:
|
112
|
-
|
27
|
+
metadata:
|
28
|
+
rubygems_mfa_required: 'true'
|
29
|
+
post_install_message:
|
113
30
|
rdoc_options: []
|
114
31
|
require_paths:
|
115
32
|
- lib
|
@@ -117,15 +34,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
117
34
|
requirements:
|
118
35
|
- - ">="
|
119
36
|
- !ruby/object:Gem::Version
|
120
|
-
version: 2.
|
37
|
+
version: 3.2.0
|
121
38
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
39
|
requirements:
|
123
40
|
- - ">="
|
124
41
|
- !ruby/object:Gem::Version
|
125
42
|
version: '0'
|
126
43
|
requirements: []
|
127
|
-
rubygems_version: 3.
|
128
|
-
signing_key:
|
44
|
+
rubygems_version: 3.4.18
|
45
|
+
signing_key:
|
129
46
|
specification_version: 4
|
130
47
|
summary: General-purpose method return objects that can be chained.
|
131
48
|
test_files: []
|