operatic 0.6.0 → 0.7.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: 05be6b41b0969a2f2ae37af1a149095939d78b8724591d7598c46543f15d9070
4
- data.tar.gz: 071dc0a5a5d652476a33498206e5e7a34e1c911cd6276018d9022b0558628815
3
+ metadata.gz: 56ef955a17eedc8782756d9a79b164662f2d558f16e2e8f48f5991d6452a30df
4
+ data.tar.gz: 660cb2d0ae5118614bcd449a8b3b06b041afe467d762bac8dbe3e10de506b2dc
5
5
  SHA512:
6
- metadata.gz: e76718b015b9f69c0d7fb8b9b600baacd95abfac4656b1c3d926f1c59b66e2d55e092f0c1cf2382978396fb03d65ce8a0f114f78eb9ee27879d28040e62426ad
7
- data.tar.gz: f8adfcf7e3852cf6031ad407b1ea9f0cbdd82d9cd7983bf71fe5fab46c769997406ca58bccfcaddd2bc18ed8e828f29420b937ba15db3ffbdf8b2a5bbfba50aa
6
+ metadata.gz: 7a432089ae7b3f71c3028bf34e278ea33553e924212bcc83bf5c02b0e3d28923e416e3f1cd054bfa664fce27343dcd76c9beec98dd88b4da2e85a625cb012e37
7
+ data.tar.gz: 271a800712179e79efcb75f1bd267e1dd12900b5eb07837a78aab4c35cffe3914e98733d08436a4cc3d52872934b07c04ae941648b70cd60eb61d0e3b6a6e030
@@ -0,0 +1,7 @@
1
+ version: 2
2
+
3
+ updates:
4
+ - package-ecosystem: github-actions
5
+ directory: "/"
6
+ schedule:
7
+ interval: daily
@@ -8,20 +8,16 @@ jobs:
8
8
  strategy:
9
9
  matrix:
10
10
  ruby:
11
- - '2.5'
12
- - '2.6'
13
11
  - '2.7'
14
12
  - '3.0'
15
13
  - '3.1'
14
+ - '3.2'
15
+ - '3.3'
16
16
  name: Ruby ${{ matrix.ruby }} RSpec
17
17
  steps:
18
- - uses: actions/checkout@v2
18
+ - uses: actions/checkout@v4
19
19
  - uses: ruby/setup-ruby@v1
20
20
  with:
21
21
  bundler-cache: true
22
22
  ruby-version: ${{ matrix.ruby }}
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 }}
23
+ - run: bundle exec rspec
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## Version 0.7.0 - 2024-05-12
4
+
5
+ - Data within an operation is now gathered on a separate `#data` object that's passed to a concrete `Operatic::Success`/`Operatic::Failure` result instance on completion. Convenience data accessors can be defined on the `Data` object (via the renamed `Operatic.data_attr`) but remain available on the result using the magic of `Result#method_missing`. <https://github.com/benpickles/operatic/pull/18>
6
+ - Require Ruby version 2.7+.
7
+ - Support pattern matching solely against a Result's data. <https://github.com/benpickles/operatic/pull/20>
8
+
3
9
  ## Version 0.6.0 - 2022-08-22
4
10
 
5
11
  - Support pattern matching a Result (in Ruby 2.7+). <https://github.com/benpickles/operatic/pull/12>
data/Gemfile CHANGED
@@ -2,3 +2,5 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in operatic.gemspec
4
4
  gemspec
5
+
6
+ gem 'yard'
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_attr :message
27
+ data_attr :message
28
28
 
29
29
  def call
30
30
  # Exit the method and mark the result as a failure.
@@ -36,12 +36,15 @@ class SayHello
36
36
  end
37
37
 
38
38
  result = SayHello.call(name: 'Dave')
39
+ result.class # => Operatic::Success
40
+ result.failure? # => false
39
41
  result.success? # => true
40
42
  result.message # => "Hello Dave"
41
43
  result[:message] # => "Hello Dave"
42
44
  result.to_h # => {:message=>"Hello Dave"}
43
45
 
44
46
  result = SayHello.call
47
+ result.class # => Operatic::Failure
45
48
  result.failure? # => true
46
49
  result.success? # => false
47
50
  result.message # => nil
@@ -49,17 +52,6 @@ result[:message] # => nil
49
52
  result.to_h # => {}
50
53
  ```
51
54
 
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
61
- ```
62
-
63
55
  A Rails controller might use Operatic like this:
64
56
 
65
57
  ```ruby
@@ -76,15 +68,39 @@ class HellosController < ApplicationController
76
68
  end
77
69
  ```
78
70
 
79
- Or a pattern matching alternative:
71
+ ## Pattern matching
72
+
73
+ An Operatic result also supports pattern matching allowing you to match over a tuple of the result class and its data:
74
+
75
+ ```ruby
76
+ case SayHello.call(name: 'Dave')
77
+ in [Operatic::Success, { message: }]
78
+ # Result is a success, do something with the `message` variable.
79
+ in [Operatic::Failure, _]
80
+ # Result is a failure, do something else.
81
+ end
82
+ ```
83
+
84
+ Or match solely against its data:
85
+
86
+ ```ruby
87
+ case SayHello.call(name: 'Dave')
88
+ in message:
89
+ # Result has the `message` key, do something with the variable.
90
+ else
91
+ # Do something else.
92
+ end
93
+ ```
94
+
95
+ Which might be consumed in Rails like this:
80
96
 
81
97
  ```ruby
82
98
  class HellosController < ApplicationController
83
99
  def create
84
100
  case SayHello.call(name: params[:name])
85
- in [true, { message: }]
101
+ in [Operatic::Success, { message: }]
86
102
  render plain: message
87
- in [false, _]
103
+ in [Operatic::Failure, _]
88
104
  render :new
89
105
  end
90
106
  end
@@ -111,4 +127,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
111
127
 
112
128
  ## Code of Conduct
113
129
 
114
- Everyone interacting in the Operatic project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/benpickles/operatic/blob/master/CODE_OF_CONDUCT.md).
130
+ Everyone interacting in the Operatic project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/benpickles/operatic/blob/main/CODE_OF_CONDUCT.md).
@@ -0,0 +1,72 @@
1
+ module Operatic
2
+ class Data
3
+ # Generate a subclass of {Data} with named +attrs+ accessors. This wouldn't
4
+ # normally be called directly, see {ClassMethods#data_attr} for example
5
+ # usage.
6
+ #
7
+ # @param attrs [Array<Symbol>] a list of convenience data accessors.
8
+ def self.define(*attrs)
9
+ Class.new(self) do
10
+ attrs.each do |name|
11
+ define_method name do
12
+ self[name]
13
+ end
14
+
15
+ define_method "#{name}=" do |value|
16
+ self[name] = value
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ # @param kwargs [Hash<Symbol, anything>]
23
+ def initialize(**kwargs)
24
+ @data = kwargs
25
+ end
26
+
27
+ # Return the value for +key+.
28
+ #
29
+ # @param key [Symbol]
30
+ #
31
+ # @return [anything]
32
+ def [](key)
33
+ @data[key]
34
+ end
35
+
36
+ # Set data on the result.
37
+ #
38
+ # @param key [Symbol]
39
+ # @param value [anything]
40
+ def []=(key, value)
41
+ @data[key] = value
42
+ end
43
+
44
+ # @return [self]
45
+ def freeze
46
+ @data.freeze
47
+ super
48
+ end
49
+
50
+ # @param hash [Hash<Symbol, anything>]
51
+ #
52
+ # @return [Data]
53
+ def merge(hash)
54
+ self.class.new.tap { |other|
55
+ other.set_data(@data)
56
+ other.set_data(hash)
57
+ }
58
+ end
59
+
60
+ # @return [Hash<Symbol, anything>]
61
+ def to_h
62
+ @data
63
+ end
64
+
65
+ protected
66
+ def set_data(data)
67
+ data.each do |key, value|
68
+ @data[key] = value
69
+ end
70
+ end
71
+ end
72
+ end
@@ -1,108 +1,116 @@
1
1
  module Operatic
2
2
  class Result
3
- # Generate a subclass of {Result} with named +attrs+ accessors. This
4
- # wouldn't normally be called directly, see {ClassMethods#result_attr} for
5
- # example usage.
6
- #
7
- # @param attrs [Array<Symbol>] a list of convenience data accessors.
8
- def self.generate(*attrs)
9
- Class.new(self) do
10
- attrs.each do |name|
11
- define_method name do
12
- @data[name]
13
- end
14
-
15
- define_method "#{name}=" do |value|
16
- @data[name] = value
17
- end
18
- end
19
- end
20
- end
3
+ # @return [Data]
4
+ attr_reader :data
21
5
 
22
- def initialize
23
- @data = {}
24
- @success = true
6
+ # @param data [Data]
7
+ def initialize(data)
8
+ @data = data
25
9
  end
26
10
 
27
- # Read data that's attached to the result.
11
+ # Convenience proxy to read the +key+ from its {#data} object.
12
+ #
13
+ # @param key [Symbol]
14
+ #
15
+ # @return [anything]
28
16
  def [](key)
29
- @data[key]
17
+ data[key]
30
18
  end
31
19
 
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.
20
+ # Returns a tuple of self and {#to_h} allowing you to pattern match across
21
+ # both the result's status and its data.
38
22
  #
39
23
  # @example
40
- # result = Result.new.success!(message: 'Hello world')
24
+ # class SayHello
25
+ # include Operatic
26
+ #
27
+ # def call
28
+ # data[:message] = 'Hello world'
29
+ # end
30
+ # end
41
31
  #
42
- # case result
43
- # in [true, { message: }]
32
+ # case SayHello.call
33
+ # in [Operatic::Success, { message: }]
44
34
  # # Result is a success, do something with the `message` variable.
45
- # in [false, _]
35
+ # in [Operatic::Failure, _]
46
36
  # # Result is a failure, do something else.
47
37
  # end
48
38
  #
49
- # @return [Array(Boolean, Hash<Symbol, anything>)]
39
+ # @return [Array(self, Hash<Symbol, anything>)]
50
40
  def deconstruct
51
- [@success, @data]
41
+ [self, to_h]
52
42
  end
53
43
 
54
- # Mark the result as a failure, optionally attach +data+ via kwargs, and
55
- # freeze the object so it cannot be modified further.
44
+ # Pattern match against the result's data via {#to_h}.
56
45
  #
57
- # *Note*: Calling {#success!} or {#failure!} more than once will raise a
58
- # +FrozenError+.
59
- def failure!(**data)
60
- @success = false
61
- set_data(data)
62
- freeze
63
- end
64
-
65
- def failure?
66
- !@success
46
+ # @example
47
+ # class SayHello
48
+ # include Operatic
49
+ #
50
+ # def call
51
+ # data[:message] = 'Hello world'
52
+ # end
53
+ # end
54
+ #
55
+ # case SayHello.call
56
+ # in message:
57
+ # # Result has the `message` key, do something with the variable.
58
+ # else
59
+ # # Do something else.
60
+ # end
61
+ #
62
+ # @return [Hash<Symbol, anything>]
63
+ def deconstruct_keys(keys = nil)
64
+ to_h
67
65
  end
68
66
 
67
+ # @return [self]
69
68
  def freeze
70
- @data.freeze
69
+ data.freeze
71
70
  super
72
71
  end
73
72
 
74
- # Mark the result as a success, optionally attach +data+ via kwargs, and
75
- # freeze the object so it cannot be modified further.
76
- #
77
- # Calling this is not strictly necessary as a +Result+ defaults to being a
78
- # success, but it's a convenient means of attaching data and of indicating
79
- # intent in the consuming code.
80
- #
81
- # *Note*: Calling {#success!} or {#failure!} more than once will raise a
82
- # +FrozenError+.
83
- def success!(**data)
84
- @success = true
85
- set_data(data)
86
- freeze
73
+ # Forwards unknown methods to its {#data} object allowing convenience
74
+ # accessors defined via {Data.define} to be available directly on the
75
+ # {Result}.
76
+ def method_missing(name, *args, **kwargs, &block)
77
+ return data.public_send(name, *args, **kwargs, &block) if data.respond_to?(name)
78
+ super
87
79
  end
88
80
 
89
- def success?
90
- @success
81
+ def respond_to?(...)
82
+ super || data.respond_to?(...)
91
83
  end
92
84
 
93
- # Returns the full (frozen) hash of data attached to the result via
94
- # {#success!}, {#failure!}, or convenience accessors added with {.generate}.
85
+ # Convenience proxy to {Data#to_h}.
95
86
  #
96
87
  # @return [Hash<Symbol, anything>]
97
88
  def to_h
98
- @data
89
+ data.to_h
90
+ end
91
+ end
92
+
93
+ class Success < Result
94
+ # @return [false]
95
+ def failure?
96
+ false
99
97
  end
100
98
 
101
- private
102
- def set_data(data)
103
- data.each do |key, value|
104
- @data[key] = value
105
- end
106
- end
99
+ # @return [true]
100
+ def success?
101
+ true
102
+ end
103
+ end
104
+
105
+ class Failure < Result
106
+ # @return [true]
107
+ def failure?
108
+ true
109
+ end
110
+
111
+ # @return [false]
112
+ def success?
113
+ false
114
+ end
107
115
  end
108
116
  end
@@ -1,3 +1,3 @@
1
1
  module Operatic
2
- VERSION = '0.6.0'
2
+ VERSION = '0.7.0'
3
3
  end
data/lib/operatic.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'operatic/data'
1
2
  require 'operatic/errors'
2
3
  require 'operatic/result'
3
4
  require 'operatic/version'
@@ -9,115 +10,152 @@ module Operatic
9
10
  end
10
11
 
11
12
  module ClassMethods
12
- # The main way of calling an operation.
13
+ # The main way to call an operation. This initializes the class with the
14
+ # supplied +attrs+ keyword arguments and calls {Operatic#call} returning a
15
+ # frozen {Result} instance.
13
16
  #
14
- # The class is instantiated with the supplied +attrs+ keyword arguments and
15
- # calls {Operatic#call} returning a frozen {Result} instance.
17
+ # @param attrs [Hash<Symbol, anything>]
16
18
  #
17
- # @return [Result]
19
+ # @return [Failure, Success]
18
20
  def call(**attrs)
19
- new(**attrs)
20
- .tap(&:call)
21
- .result
22
- .freeze
21
+ operation = new(**attrs)
22
+ operation.call
23
+ operation.result || Success.new(operation.data).freeze
23
24
  end
24
25
 
25
- # Calls {#call} but raises {FailureError} if the returned {Result} is a
26
- # {Result#failure?} - useful for things like background jobs, rake tasks,
27
- # test setups, etc.
26
+ # The same as {#call} but raises {FailureError} if the returned {#result} is
27
+ # a {Failure} - useful for things like background jobs, rake tasks, test
28
+ # setups, etc.
28
29
  #
29
- # @return [Result]
30
+ # @param attrs [Hash<Symbol, anything>]
31
+ #
32
+ # @return [Success]
33
+ #
34
+ # @raise [FailureError] if the operation is not a {Success}
30
35
  def call!(**attrs)
31
36
  call(**attrs).tap { |result|
32
37
  raise FailureError if result.failure?
33
38
  }
34
39
  end
35
40
 
36
- # Define a {Result} subclass with named accessors specific to the class via
37
- # {Result.generate}.
41
+ # Define a class-specific {Data} subclass with the named accessors added via
42
+ # {Data.define}.
38
43
  #
39
44
  # @example
40
45
  # class SayHello
41
46
  # include Operatic
42
47
  #
43
- # attr_reader :name
44
- #
45
- # result_attr :message
48
+ # data_attr :message
46
49
  #
47
50
  # def call
48
- # success!(message: "Hello #{name}")
51
+ # success!(message: "Hello #{@name}")
49
52
  # end
50
53
  # end
51
54
  #
52
55
  # result = SayHello.call(name: 'Dave')
53
- # result.success? # => true
54
- # result.message # => "Hello Dave"
56
+ # result.class # => Operatic::Success
57
+ # result.message # => "Hello Dave"
58
+ # result[:message] # => "Hello Dave"
59
+ # result.to_h # => {:message=>"Hello Dave"}
55
60
  #
56
61
  # @param attrs [Array<Symbol>] a list of convenience data accessors to
57
62
  # define on the {Result}.
58
- def result_attr(*attrs)
59
- @result_class = Result.generate(*attrs)
63
+ def data_attr(*attrs)
64
+ @data_class = Data.define(*attrs)
60
65
  end
61
66
 
62
- def result_class
63
- @result_class || Result
67
+ # @return [Class<Data>]
68
+ def data_class
69
+ @data_class || Data
64
70
  end
65
71
  end
66
72
 
73
+ # @return [Success, Failure]
74
+ attr_reader :result
75
+
76
+ # @param attrs [Hash<Symbol, anything>]
67
77
  def initialize(**attrs)
68
78
  attrs.each do |key, value|
69
79
  instance_variable_set("@#{key}", value)
70
80
  end
71
81
  end
72
82
 
73
- # Override this method with your implementation. Use {#success!} or
74
- # {#failure!} methods to communicate the {#result}'s status and to attach
75
- # data to it. Define convenience result accessors with
76
- # {ClassMethods#result_attr}.
83
+ # Override this method with your implementation. Use {#success!}/{#failure!}
84
+ # to define the status of the result {Success}/{Failure} and attach data.
77
85
  #
78
86
  # @example
79
87
  # class SayHello
80
88
  # include Operatic
81
89
  #
82
- # attr_reader :name
83
- #
84
- # result_attr :message
85
- #
86
90
  # def call
87
- # return failure! unless name
88
- # success!(message: "Hello #{name}")
91
+ # return failure! unless @name
92
+ # success!(message: "Hello #{@name}")
89
93
  # end
90
94
  # end
91
95
  #
92
96
  # result = SayHello.call(name: 'Dave')
93
- # result.failure? # => false
94
- # result.success? # => true
95
- # result.message # => "Hello Dave"
96
- # result.to_h # => {:message=>"Hello Dave"}
97
+ # result.class # => Operatic::Success
98
+ # result.failure? # => false
99
+ # result.success? # => true
100
+ # result[:message] # => "Hello Dave"
101
+ # result.to_h # => {:message=>"Hello Dave"}
97
102
  #
98
103
  # result = SayHello.call
99
- # result.failure? # => true
100
- # result.success? # => false
101
- # result.message # => nil
102
- # result.to_h # => {}
104
+ # result.class # => Operatic::Failure
105
+ # result.failure? # => true
106
+ # result.success? # => false
107
+ # result.to_h # => {}
103
108
  def call
104
109
  end
105
110
 
106
- # Convenience shortcut to the operation's {Result#failure!}.
107
- def failure!(**data)
108
- result.failure!(**data)
111
+ # Any data to be communicated via the operation's result should be added to
112
+ # this {Data} object.
113
+ #
114
+ # *Note*: This will be frozen when returned from an operation.
115
+ #
116
+ # @example
117
+ # class SayHello
118
+ # include Operatic
119
+ #
120
+ # def call
121
+ # data[:message] = "Hello #{@name}"
122
+ # end
123
+ # end
124
+ #
125
+ # result = SayHello.call(name: 'Dave')
126
+ # result.data.to_h # => {:message=>"Dave"}
127
+ # result.data.frozen? # => true
128
+ #
129
+ # @return [Data]
130
+ def data
131
+ @data ||= self.class.data_class.new
109
132
  end
110
133
 
111
- # An instance of {Result} or a subclass generated by
112
- # {ClassMethods#result_attr}.
134
+ # Mark the operation as a failure and prevent further modification to the
135
+ # operation, its result, and its data.
113
136
  #
114
- # @return [Result]
115
- def result
116
- @result ||= self.class.result_class.new
137
+ # @param kwargs [Hash<Symbol, anything>]
138
+ #
139
+ # @raise [FrozenError] if called more than once
140
+ def failure!(**kwargs)
141
+ @result = Failure.new(data.merge(kwargs))
142
+ freeze
143
+ end
144
+
145
+ # @return [self]
146
+ def freeze
147
+ @result.freeze
148
+ super
117
149
  end
118
150
 
119
- # Convenience shortcut to the operation's {Result#success!}.
120
- def success!(**data)
121
- result.success!(**data)
151
+ # Mark the operation as a success and prevent further modification to the
152
+ # operation, its result, and its data.
153
+ #
154
+ # @param kwargs [Hash<Symbol, anything>]
155
+ #
156
+ # @raise [FrozenError] if called more than once
157
+ def success!(**kwargs)
158
+ @result = Success.new(data.merge(kwargs))
159
+ freeze
122
160
  end
123
161
  end
data/operatic.gemspec CHANGED
@@ -14,12 +14,13 @@ Gem::Specification.new do |spec|
14
14
  spec.license = 'MIT'
15
15
 
16
16
  spec.metadata = {
17
- 'changelog_uri' => 'https://github.com/benpickles/operatic/blob/master/CHANGELOG.md',
17
+ 'changelog_uri' => 'https://github.com/benpickles/operatic/blob/main/CHANGELOG.md',
18
18
  'documentation_uri' => 'https://rubydoc.info/gems/operatic',
19
+ 'rubygems_mfa_required' => 'true',
19
20
  'source_code_uri' => 'https://github.com/benpickles/operatic',
20
21
  }
21
22
 
22
- spec.required_ruby_version = '>= 2.5.0'
23
+ spec.required_ruby_version = '>= 2.7.0'
23
24
 
24
25
  # Specify which files should be added to the gem when it is released.
25
26
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
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.6.0
4
+ version: 0.7.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: 2022-08-22 00:00:00.000000000 Z
11
+ date: 2024-05-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -59,6 +59,7 @@ executables: []
59
59
  extensions: []
60
60
  extra_rdoc_files: []
61
61
  files:
62
+ - ".github/dependabot.yml"
62
63
  - ".github/workflows/ruby.yml"
63
64
  - ".gitignore"
64
65
  - ".rspec"
@@ -71,6 +72,7 @@ files:
71
72
  - bin/console
72
73
  - bin/setup
73
74
  - lib/operatic.rb
75
+ - lib/operatic/data.rb
74
76
  - lib/operatic/errors.rb
75
77
  - lib/operatic/result.rb
76
78
  - lib/operatic/version.rb
@@ -79,8 +81,9 @@ homepage: https://github.com/benpickles/operatic
79
81
  licenses:
80
82
  - MIT
81
83
  metadata:
82
- changelog_uri: https://github.com/benpickles/operatic/blob/master/CHANGELOG.md
84
+ changelog_uri: https://github.com/benpickles/operatic/blob/main/CHANGELOG.md
83
85
  documentation_uri: https://rubydoc.info/gems/operatic
86
+ rubygems_mfa_required: 'true'
84
87
  source_code_uri: https://github.com/benpickles/operatic
85
88
  post_install_message:
86
89
  rdoc_options: []
@@ -90,14 +93,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
90
93
  requirements:
91
94
  - - ">="
92
95
  - !ruby/object:Gem::Version
93
- version: 2.5.0
96
+ version: 2.7.0
94
97
  required_rubygems_version: !ruby/object:Gem::Requirement
95
98
  requirements:
96
99
  - - ">="
97
100
  - !ruby/object:Gem::Version
98
101
  version: '0'
99
102
  requirements: []
100
- rubygems_version: 3.3.7
103
+ rubygems_version: 3.5.9
101
104
  signing_key:
102
105
  specification_version: 4
103
106
  summary: A minimal standard interface for your operations