operatic 0.3.1 → 0.6.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 +4 -4
- data/.github/workflows/ruby.yml +6 -1
- data/CHANGELOG.md +14 -0
- data/README.md +50 -8
- data/lib/operatic/result.rb +42 -21
- data/lib/operatic/version.rb +1 -1
- data/lib/operatic.rb +34 -27
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 05be6b41b0969a2f2ae37af1a149095939d78b8724591d7598c46543f15d9070
|
4
|
+
data.tar.gz: 071dc0a5a5d652476a33498206e5e7a34e1c911cd6276018d9022b0558628815
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e76718b015b9f69c0d7fb8b9b600baacd95abfac4656b1c3d926f1c59b66e2d55e092f0c1cf2382978396fb03d65ce8a0f114f78eb9ee27879d28040e62426ad
|
7
|
+
data.tar.gz: f8adfcf7e3852cf6031ad407b1ea9f0cbdd82d9cd7983bf71fe5fab46c769997406ca58bccfcaddd2bc18ed8e828f29420b937ba15db3ffbdf8b2a5bbfba50aa
|
data/.github/workflows/ruby.yml
CHANGED
@@ -12,6 +12,7 @@ jobs:
|
|
12
12
|
- '2.6'
|
13
13
|
- '2.7'
|
14
14
|
- '3.0'
|
15
|
+
- '3.1'
|
15
16
|
name: Ruby ${{ matrix.ruby }} RSpec
|
16
17
|
steps:
|
17
18
|
- uses: actions/checkout@v2
|
@@ -19,4 +20,8 @@ jobs:
|
|
19
20
|
with:
|
20
21
|
bundler-cache: true
|
21
22
|
ruby-version: ${{ matrix.ruby }}
|
22
|
-
-
|
23
|
+
- name: 'Set RSpec exclude pattern for Ruby < 2.7'
|
24
|
+
run: echo "::set-output name=value::**/*_gte_ruby_2_7_spec.rb"
|
25
|
+
if: matrix.ruby == '2.5' || matrix.ruby == '2.6'
|
26
|
+
id: rspec_exclude_pattern
|
27
|
+
- run: bundle exec rspec --exclude-pattern=${{ steps.rspec_exclude_pattern.outputs.value }}
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,19 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## Version 0.6.0 - 2022-08-22
|
4
|
+
|
5
|
+
- Support pattern matching a Result (in Ruby 2.7+). <https://github.com/benpickles/operatic/pull/12>
|
6
|
+
|
7
|
+
## Version 0.5.0 - 2022-06-23
|
8
|
+
|
9
|
+
- Support custom initialize method to aid compatibility with other libraries. <https://github.com/benpickles/operatic/pull/11>
|
10
|
+
- Rename to `Operatic.result_attr` to be more specific about its functionality. <https://github.com/benpickles/operatic/pull/10>
|
11
|
+
- Get and set Result data with `#[]` / `#[]=`. <https://github.com/benpickles/operatic/pull/9>
|
12
|
+
|
13
|
+
## Version 0.4.0 - 2022-05-25
|
14
|
+
|
15
|
+
- Switch to keyword arguments. <https://github.com/benpickles/operatic/pull/8>
|
16
|
+
|
3
17
|
## Version 0.3.1 - 2020-01-05
|
4
18
|
|
5
19
|
- Less specific Rake dependency. <https://github.com/benpickles/operatic/pull/6>
|
data/README.md
CHANGED
@@ -24,7 +24,7 @@ class SayHello
|
|
24
24
|
attr_reader :name
|
25
25
|
|
26
26
|
# Declare convenience accessors on the result.
|
27
|
-
|
27
|
+
result_attr :message
|
28
28
|
|
29
29
|
def call
|
30
30
|
# Exit the method and mark the result as a failure.
|
@@ -36,15 +36,28 @@ class SayHello
|
|
36
36
|
end
|
37
37
|
|
38
38
|
result = SayHello.call(name: 'Dave')
|
39
|
-
result.success?
|
40
|
-
result.message
|
41
|
-
result
|
39
|
+
result.success? # => true
|
40
|
+
result.message # => "Hello Dave"
|
41
|
+
result[:message] # => "Hello Dave"
|
42
|
+
result.to_h # => {:message=>"Hello Dave"}
|
42
43
|
|
43
44
|
result = SayHello.call
|
44
|
-
result.failure?
|
45
|
-
result.success?
|
46
|
-
result.message
|
47
|
-
result
|
45
|
+
result.failure? # => true
|
46
|
+
result.success? # => false
|
47
|
+
result.message # => nil
|
48
|
+
result[:message] # => nil
|
49
|
+
result.to_h # => {}
|
50
|
+
```
|
51
|
+
|
52
|
+
An `Operatic::Result` also supports pattern matching in Ruby 2.7+ returning an array of `[success, data]`:
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
case SayHello.call(name: 'Dave')
|
56
|
+
in [true, { message: }]
|
57
|
+
# Result is a success, do something with the `message` variable.
|
58
|
+
in [false, _]
|
59
|
+
# Result is a failure, do something else.
|
60
|
+
end
|
48
61
|
```
|
49
62
|
|
50
63
|
A Rails controller might use Operatic like this:
|
@@ -63,6 +76,35 @@ class HellosController < ApplicationController
|
|
63
76
|
end
|
64
77
|
```
|
65
78
|
|
79
|
+
Or a pattern matching alternative:
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
class HellosController < ApplicationController
|
83
|
+
def create
|
84
|
+
case SayHello.call(name: params[:name])
|
85
|
+
in [true, { message: }]
|
86
|
+
render plain: message
|
87
|
+
in [false, _]
|
88
|
+
render :new
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
```
|
93
|
+
|
94
|
+
## Development
|
95
|
+
|
96
|
+
Run the tests with:
|
97
|
+
|
98
|
+
```
|
99
|
+
bundle exec rspec
|
100
|
+
```
|
101
|
+
|
102
|
+
Generate Yard documentation with:
|
103
|
+
|
104
|
+
```
|
105
|
+
bundle exec yardoc
|
106
|
+
```
|
107
|
+
|
66
108
|
## License
|
67
109
|
|
68
110
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/lib/operatic/result.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
module Operatic
|
2
2
|
class Result
|
3
3
|
# Generate a subclass of {Result} with named +attrs+ accessors. This
|
4
|
-
# wouldn't normally be called directly, see {ClassMethods#
|
4
|
+
# wouldn't normally be called directly, see {ClassMethods#result_attr} for
|
5
5
|
# example usage.
|
6
6
|
#
|
7
|
-
# @param attrs [Array<Symbol>] a list of
|
7
|
+
# @param attrs [Array<Symbol>] a list of convenience data accessors.
|
8
8
|
def self.generate(*attrs)
|
9
9
|
Class.new(self) do
|
10
10
|
attrs.each do |name|
|
@@ -24,17 +24,41 @@ module Operatic
|
|
24
24
|
@success = true
|
25
25
|
end
|
26
26
|
|
27
|
-
#
|
28
|
-
|
27
|
+
# Read data that's attached to the result.
|
28
|
+
def [](key)
|
29
|
+
@data[key]
|
30
|
+
end
|
31
|
+
|
32
|
+
# Set data on the result.
|
33
|
+
def []=(key, value)
|
34
|
+
@data[key] = value
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns an array of success and data.
|
38
|
+
#
|
39
|
+
# @example
|
40
|
+
# result = Result.new.success!(message: 'Hello world')
|
29
41
|
#
|
30
|
-
#
|
31
|
-
#
|
42
|
+
# case result
|
43
|
+
# in [true, { message: }]
|
44
|
+
# # Result is a success, do something with the `message` variable.
|
45
|
+
# in [false, _]
|
46
|
+
# # Result is a failure, do something else.
|
47
|
+
# end
|
32
48
|
#
|
33
|
-
# @
|
34
|
-
|
35
|
-
|
36
|
-
|
49
|
+
# @return [Array(Boolean, Hash<Symbol, anything>)]
|
50
|
+
def deconstruct
|
51
|
+
[@success, @data]
|
52
|
+
end
|
53
|
+
|
54
|
+
# Mark the result as a failure, optionally attach +data+ via kwargs, and
|
55
|
+
# freeze the object so it cannot be modified further.
|
56
|
+
#
|
57
|
+
# *Note*: Calling {#success!} or {#failure!} more than once will raise a
|
58
|
+
# +FrozenError+.
|
59
|
+
def failure!(**data)
|
37
60
|
@success = false
|
61
|
+
set_data(data)
|
38
62
|
freeze
|
39
63
|
end
|
40
64
|
|
@@ -47,21 +71,18 @@ module Operatic
|
|
47
71
|
super
|
48
72
|
end
|
49
73
|
|
50
|
-
# Mark the result as a success, optionally attach data, and
|
51
|
-
# object so it cannot be modified further.
|
74
|
+
# Mark the result as a success, optionally attach +data+ via kwargs, and
|
75
|
+
# freeze the object so it cannot be modified further.
|
52
76
|
#
|
53
|
-
# Calling this is not strictly necessary as a
|
77
|
+
# Calling this is not strictly necessary as a +Result+ defaults to being a
|
54
78
|
# success, but it's a convenient means of attaching data and of indicating
|
55
79
|
# intent in the consuming code.
|
56
80
|
#
|
57
|
-
# *Note*:
|
58
|
-
#
|
59
|
-
|
60
|
-
# @param data [Hash<Symbol, anything>] an optional hash of data to attach
|
61
|
-
# to the result.
|
62
|
-
def success!(data = nil)
|
63
|
-
set_data(data) if data
|
81
|
+
# *Note*: Calling {#success!} or {#failure!} more than once will raise a
|
82
|
+
# +FrozenError+.
|
83
|
+
def success!(**data)
|
64
84
|
@success = true
|
85
|
+
set_data(data)
|
65
86
|
freeze
|
66
87
|
end
|
67
88
|
|
@@ -72,7 +93,7 @@ module Operatic
|
|
72
93
|
# Returns the full (frozen) hash of data attached to the result via
|
73
94
|
# {#success!}, {#failure!}, or convenience accessors added with {.generate}.
|
74
95
|
#
|
75
|
-
# @return [Hash]
|
96
|
+
# @return [Hash<Symbol, anything>]
|
76
97
|
def to_h
|
77
98
|
@data
|
78
99
|
end
|
data/lib/operatic/version.rb
CHANGED
data/lib/operatic.rb
CHANGED
@@ -11,15 +11,12 @@ module Operatic
|
|
11
11
|
module ClassMethods
|
12
12
|
# The main way of calling an operation.
|
13
13
|
#
|
14
|
-
# The class is instantiated with supplied +attrs+
|
15
|
-
# returning a frozen {Result} instance.
|
14
|
+
# The class is instantiated with the supplied +attrs+ keyword arguments and
|
15
|
+
# calls {Operatic#call} returning a frozen {Result} instance.
|
16
16
|
#
|
17
|
-
# @
|
18
|
-
|
19
|
-
|
20
|
-
# @return a [Result]
|
21
|
-
def call(attrs = nil)
|
22
|
-
new(attrs)
|
17
|
+
# @return [Result]
|
18
|
+
def call(**attrs)
|
19
|
+
new(**attrs)
|
23
20
|
.tap(&:call)
|
24
21
|
.result
|
25
22
|
.freeze
|
@@ -30,8 +27,8 @@ module Operatic
|
|
30
27
|
# test setups, etc.
|
31
28
|
#
|
32
29
|
# @return [Result]
|
33
|
-
def call!(attrs
|
34
|
-
call(attrs).tap { |result|
|
30
|
+
def call!(**attrs)
|
31
|
+
call(**attrs).tap { |result|
|
35
32
|
raise FailureError if result.failure?
|
36
33
|
}
|
37
34
|
end
|
@@ -45,7 +42,7 @@ module Operatic
|
|
45
42
|
#
|
46
43
|
# attr_reader :name
|
47
44
|
#
|
48
|
-
#
|
45
|
+
# result_attr :message
|
49
46
|
#
|
50
47
|
# def call
|
51
48
|
# success!(message: "Hello #{name}")
|
@@ -55,8 +52,11 @@ module Operatic
|
|
55
52
|
# result = SayHello.call(name: 'Dave')
|
56
53
|
# result.success? # => true
|
57
54
|
# result.message # => "Hello Dave"
|
58
|
-
|
59
|
-
|
55
|
+
#
|
56
|
+
# @param attrs [Array<Symbol>] a list of convenience data accessors to
|
57
|
+
# define on the {Result}.
|
58
|
+
def result_attr(*attrs)
|
59
|
+
@result_class = Result.generate(*attrs)
|
60
60
|
end
|
61
61
|
|
62
62
|
def result_class
|
@@ -64,22 +64,16 @@ module Operatic
|
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
67
|
-
|
68
|
-
#
|
69
|
-
# @return [Result]
|
70
|
-
attr_reader :result
|
71
|
-
|
72
|
-
def initialize(attrs = nil)
|
73
|
-
@result = self.class.result_class.new
|
74
|
-
|
67
|
+
def initialize(**attrs)
|
75
68
|
attrs.each do |key, value|
|
76
69
|
instance_variable_set("@#{key}", value)
|
77
|
-
end
|
70
|
+
end
|
78
71
|
end
|
79
72
|
|
80
73
|
# Override this method with your implementation. Use {#success!} or
|
81
74
|
# {#failure!} methods to communicate the {#result}'s status and to attach
|
82
|
-
# data it. Define convenience result accessors with
|
75
|
+
# data to it. Define convenience result accessors with
|
76
|
+
# {ClassMethods#result_attr}.
|
83
77
|
#
|
84
78
|
# @example
|
85
79
|
# class SayHello
|
@@ -87,6 +81,8 @@ module Operatic
|
|
87
81
|
#
|
88
82
|
# attr_reader :name
|
89
83
|
#
|
84
|
+
# result_attr :message
|
85
|
+
#
|
90
86
|
# def call
|
91
87
|
# return failure! unless name
|
92
88
|
# success!(message: "Hello #{name}")
|
@@ -94,23 +90,34 @@ module Operatic
|
|
94
90
|
# end
|
95
91
|
#
|
96
92
|
# result = SayHello.call(name: 'Dave')
|
93
|
+
# result.failure? # => false
|
97
94
|
# result.success? # => true
|
95
|
+
# result.message # => "Hello Dave"
|
98
96
|
# result.to_h # => {:message=>"Hello Dave"}
|
99
97
|
#
|
100
98
|
# result = SayHello.call
|
101
99
|
# result.failure? # => true
|
102
100
|
# result.success? # => false
|
101
|
+
# result.message # => nil
|
103
102
|
# result.to_h # => {}
|
104
103
|
def call
|
105
104
|
end
|
106
105
|
|
107
106
|
# Convenience shortcut to the operation's {Result#failure!}.
|
108
|
-
def failure!(data
|
109
|
-
result.failure!(data)
|
107
|
+
def failure!(**data)
|
108
|
+
result.failure!(**data)
|
109
|
+
end
|
110
|
+
|
111
|
+
# An instance of {Result} or a subclass generated by
|
112
|
+
# {ClassMethods#result_attr}.
|
113
|
+
#
|
114
|
+
# @return [Result]
|
115
|
+
def result
|
116
|
+
@result ||= self.class.result_class.new
|
110
117
|
end
|
111
118
|
|
112
119
|
# Convenience shortcut to the operation's {Result#success!}.
|
113
|
-
def success!(data
|
114
|
-
result.success!(data)
|
120
|
+
def success!(**data)
|
121
|
+
result.success!(**data)
|
115
122
|
end
|
116
123
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: operatic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Pickles
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-08-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -97,7 +97,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
97
97
|
- !ruby/object:Gem::Version
|
98
98
|
version: '0'
|
99
99
|
requirements: []
|
100
|
-
rubygems_version: 3.
|
100
|
+
rubygems_version: 3.3.7
|
101
101
|
signing_key:
|
102
102
|
specification_version: 4
|
103
103
|
summary: A minimal standard interface for your operations
|