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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d23afc6809c6d2b335d1db3747eeaa96e931f6a8e3dd51c8cd52261512b78bd4
4
- data.tar.gz: f21c35d239aca9518215252494bc1aeb8572750b56e30a4c94ee6aa0e2215aa0
3
+ metadata.gz: 05be6b41b0969a2f2ae37af1a149095939d78b8724591d7598c46543f15d9070
4
+ data.tar.gz: 071dc0a5a5d652476a33498206e5e7a34e1c911cd6276018d9022b0558628815
5
5
  SHA512:
6
- metadata.gz: 010f2b26e6129b0fd7b6ac5c0c1499f1fadb9a0b31027b3ed4334cb54e2a5ecdbf4710e092c64457f6f10f7d97de016887bf7c356b2ffe8711be534e67214eee
7
- data.tar.gz: 5e32262f6568ff446897a86840cb1f8bb1a3b19ca0a89fa05ffd7c32ef9f388c8e81862a6b07ea070488d44f3550893d316833e416ec0e1d3509448edf4faec2
6
+ metadata.gz: e76718b015b9f69c0d7fb8b9b600baacd95abfac4656b1c3d926f1c59b66e2d55e092f0c1cf2382978396fb03d65ce8a0f114f78eb9ee27879d28040e62426ad
7
+ data.tar.gz: f8adfcf7e3852cf6031ad407b1ea9f0cbdd82d9cd7983bf71fe5fab46c769997406ca58bccfcaddd2bc18ed8e828f29420b937ba15db3ffbdf8b2a5bbfba50aa
@@ -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
- - run: bundle exec rspec
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
- result :message
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? # => true
40
- result.message # => "Hello Dave"
41
- result.to_h # => {:message=>"Hello Dave"}
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? # => true
45
- result.success? # => false
46
- result.message # => nil
47
- result.to_h # => {}
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).
@@ -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#result} for
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 accessors to the result's data.
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
- # Mark the result as a failure, optionally attach data, and freeze the
28
- # object so it cannot be modified further.
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
- # *Note*: After calling this method calling {#success!} or {#failure!}
31
- # again will raise a +FrozenError+.
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
- # @param data [Hash<Symbol, anything>] an optional hash of data to attach
34
- # to the result.
35
- def failure!(data = nil)
36
- set_data(data) if data
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 freeze the
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 result defaults to being 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*: After calling this method calling {#success!} or {#failure!}
58
- # again will raise a +FrozenError+.
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
@@ -1,3 +1,3 @@
1
1
  module Operatic
2
- VERSION = '0.3.1'
2
+ VERSION = '0.6.0'
3
3
  end
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+ and calls {Operatic#call}
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
- # @param attrs [Hash<Symbol, anything>] an optional hash of key/values to
18
- # to the result. The class must have corresponding +attr_reader+s
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 = nil)
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
- # result :message
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
- def result(*args)
59
- @result_class = Result.generate(*args)
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
- # An instance of {Result} or a subclass generated by {ClassMethods#result}.
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 if attrs
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 {ClassMethods#result}.
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 = nil)
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 = nil)
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.3.1
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: 2021-01-05 00:00:00.000000000 Z
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.2.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