operatic 0.6.0 → 0.7.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: 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