operatic 0.3.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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