use_cases 1.0.4 → 1.0.13

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: 0c1ed9f7d86b9109062b207489f37e67f84b93e676d7e81b930f219abbd47224
4
- data.tar.gz: 2b9234f7075cb8240836875845d5d61036caa9a3c7feeb72abf31d2dd5830f91
3
+ metadata.gz: 3cec89e8a4f4ecdabae86c99156447d8b39ffd5de5e918130b21ccae18f045a7
4
+ data.tar.gz: e36cd9587aab933815775ee1181360ccea4a24e58e82c3a4a073a99bb565ff31
5
5
  SHA512:
6
- metadata.gz: fe3e66534633de66dff8811dad8a3d2c9f0f350d392db60a7e91989d11a861510e32937abc77f47ff8d74b9439a8c6bb321e18d655f655af0778bf676efcc28f
7
- data.tar.gz: 4da7071b68b859462feda741591b9862330f9b7dbe5ad127a76ff6052ac13bf7622241620c17a3ef20ee52e6f8b16c67e44b398f01621643df646b6d94759e62
6
+ metadata.gz: fdaa3ea0104e5bb38190c5c65f59e44569d38b08f0f4174e1c82f5f3b6f0698900a7001463dc3215007a2af2f4030616cc038ae2f2c78aba388b2cab90c520ad
7
+ data.tar.gz: 0c9663e589c521677529bb5b8a4cbe87c4c3591c82dfbc30ea9248b215c53f16c915f4a37dd3683f22b8dc7698f20e46e7cc8dfe20c5390ae9daaf418ee8e26d
data/CHANGELOG.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## [1.0.1] - 2021-12-19
4
4
 
5
- - Async published events are now suffixed by `".aync"`
5
+ - Async published events are now suffixed by `".async"`
6
6
 
7
7
  ## [1.0.0] - 2021-12-19
8
8
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- use_cases (1.0.2)
4
+ use_cases (1.0.12)
5
5
  activesupport
6
6
  dry-events
7
7
  dry-matcher
@@ -126,4 +126,4 @@ DEPENDENCIES
126
126
  use_cases!
127
127
 
128
128
  BUNDLED WITH
129
- 2.2.25
129
+ 2.2.28
data/README.md CHANGED
@@ -8,254 +8,174 @@
8
8
 
9
9
  It's concept is largely based on `dry-transaction` but does not use it behind the scenes. Instead it relies on other `dry` libraries like [dry-validation](https://dry-rb.org/gems/dry-validation/), [dry-events](https://dry-rb.org/gems/dry-validation/) and [dry-monads](https://dry-rb.org/gems/dry-validation/) to implement a DSL that can be flexible enough for your needs.
10
10
 
11
- ## Why `UseCases` came about:
11
+ ### Including UseCase
12
12
 
13
- 1. It allows you to use `dry-validation` without much gymastics.
14
- 2. It abstracts common steps like **authorization** and **validation** into macros.
15
- 3. It solves what we consider a problem of `dry-transaction`. The way it funnels down `input` through `Dry::Monads` payloads alone. `UseCases` offers more flexibility in a way that still promotes functional programming values.
16
- 4. It implements a simple pub/sub mechanism which can be async when `ActiveJob` is a project dependency.
17
- 5. It implements an `enqueue` mechanism to delay execution of steps, also using `ActiveJob` as a dependency.
13
+ Including the `UseCase` module ensures that your class implements the base use case [Base DSL](#the-base-dsl).
18
14
 
19
- ## Installation
15
+ ```ruby
16
+ class Users::Create
17
+ include UseCase
18
+ end
19
+ ````
20
20
 
21
- Add this line to your application's Gemfile:
21
+ In order to add optional modules (optins), use the following notation:
22
22
 
23
23
  ```ruby
24
- gem 'use_cases'
24
+ class Users::Create
25
+ include UseCase[:validated, :transactional, :publishing]
26
+ end
25
27
  ```
26
28
 
27
- And then execute:
28
-
29
- $ bundle install
30
-
31
- Or install it yourself as:
29
+ ### Using a UseCase
32
30
 
33
- $ gem install use_cases
34
-
35
- ## Usage
31
+ ```ruby
32
+ create_user = Users::Create.new
33
+ params = { first_name: 'Don', last_name: 'Quixote' }
36
34
 
37
- To get a good basis to get started on `UseCases`, make sure to read [dry-transaction](https://dry-rb.org/gems/dry-transaction/0.13/)'s documentation first.
35
+ result = create_user.call(params, current_user)
38
36
 
39
- ### Validations
37
+ # Checking if succeeded
38
+ result.success?
40
39
 
41
- See [dry-validation](https://dry-rb.org/gems/dry-validation/)
40
+ # Checking if failed
41
+ result.failure?
42
42
 
43
- ### Creating a Use Case
43
+ # Getting return value
44
+ result.value!
45
+ ```
44
46
 
45
- **Basic Example**
47
+ Or with using dry-matcher by passing a block:
46
48
 
47
49
  ```ruby
48
- class DeleteUser
49
- include UseCase
50
-
51
- check :current_user_is_user?
52
- step :build_user
53
- map :persist_user
50
+ create_user = Users::Create.new
51
+ params = { first_name: 'Don', last_name: 'Quixote' }
54
52
 
55
- private
56
-
57
- def current_user_is_user?(params, current_user)
53
+ create_user.call(params, current_user) do |on|
54
+ on.success do |user|
55
+ puts "#{user.first_name} created!"
58
56
  end
59
57
 
60
- def build_user(params, current_user)
58
+ on.failure do |(code, message)|
59
+ puts "Failure (#{code}): #{message}"
61
60
  end
61
+ end
62
+ ```
62
63
 
64
+ ### Available Optins
63
65
 
64
- def do_something(user, params, current_user)
65
- params[:should_fail] ? Failure([:failed, "failed"]) : Success("it succeeds!")
66
- end
67
- end
68
66
 
69
- params = { should_fail: true }
67
+ | Optin | Description |
68
+ |---|---|
69
+ | `:authorized` | Adds an extra `authorize` step macro, used to check user permissions. |
70
+ | `:prepared` | Adds an extra `prepare` step macro, used to run some code before the use case runs. |
71
+ | `:publishing` | Adds extra extra `publish` option to all steps, which allows a step to broadcast an event after executing |
72
+ | `:transactional` | Calls `#transaction` on a given `transaction_handler` object around the use case execution |
73
+ | `:validated` | Adds all methods of `dry-transaction` to the use case DSL, which run validations on the received `params` object. |
70
74
 
71
- YourCase.new.call(params, nil) do |match|
72
- match.failure :failed do |(code, result)|
73
- puts code
74
- end
75
+ ### The base DSL
75
76
 
76
- match.success do |message|
77
- puts message
78
- end
79
- end
80
- # => failed
77
+ Use cases implements a DSL similar to dry-transaction, using the [Railway programming paradigm](https://fsharpforfunandprofit.com/rop/).
81
78
 
82
- params = { should_fail: false }
83
79
 
84
- YourCase.new.call(params, nil) do |match|
85
- match.failure :failed do |(code, result)|
86
- puts code
87
- end
80
+ Each step macro has a different use case, and so a different subset of available options, different expectations in return values, and interaction with the following step.
88
81
 
89
- match.success do |message|
90
- puts message
91
- end
92
- end
93
- # => it succeeds!
94
- ```
82
+ By taking a simple look at the definition of a use case, anyone should be able to understand the business rules it emcompasses. For that it is necessary to understand the following matrix.
95
83
 
96
- **Complex Example**
97
84
 
98
- ```ruby
99
- class YourCase < UseCases::Base
100
- params {}
85
+ | | Rationale for use | Accepted Options | Expected return | Passes return value |
86
+ |---|---|---|---|---|
87
+ | **step** | This step has some complexity, and it can fail or succeed. | `with`, `pass` | `Success`/ `Failure` | ✅ |
88
+ | **check** | This step checks sets some rules for the operation, usually verifying that domain models fulfil some conditions. | `with`, `pass`, `failure`, `failure_message` | `boolean` | ❌ |
89
+ | **map** | Nothing should go wrong within this step. If it does, it's an unexpected application error. | `with`, `pass` | `any` | ✅ |
90
+ | **try** | We expect that, in some cases, errors will occur, and the operation fails in that case. | `catch`, `with`, `pass`, `failure`, `failure_message` | `any` | ✅ |
91
+ | **tee** | We don't care if this step succeeds or fails, it's used for non essential side effects. | `with`, `pass` | `any` | ❌ |
101
92
 
102
- try :load_some_resource
93
+ #### Optional steps
103
94
 
104
- step :change_this_resource
95
+ | | Rationale for use | Accepted Options | Expected return | Passes return value |
96
+ |---|---|---|---|---|
97
+ | **enqueue** *(requires ActiveJob defined) | The same as a `tee`, but executed later to perform non-essential expensive operations. | `with`, `pass`, and sidekiq options | `any` | ❌ |
98
+ | **authorize**<br> *(requires authorized) | Performs authorization on the current user, by running a `check` which, in case of failure, always returns an `unauthorized` failure. | `with`, `pass`, `failure_message` | `boolean` | ❌ |
99
+ | **prepare**<br> *(requires prepared) | Adds a `tee` step that always runs first. Used to mutate params if necessary. | `with`, `pass` | `any` | ❌ |
105
100
 
106
- tee :log_a_message
107
101
 
108
- check :user_can_create_another_resource?
102
+ ### Defining Steps
109
103
 
110
- map :create_some_already_validated_resource
104
+ Defining a step can be done in the body of the use case.
111
105
 
112
- enqueue :send_email_to_user
106
+ ```ruby
107
+ class Users::DeleteAccount
108
+ include UseCases[:validated, :transactional, :publishing, :validated]
113
109
 
114
- private
110
+ step :do_something, {}
111
+ ```
115
112
 
116
- def load_some_resource(_, params)
117
- Resource.find(params[:id])
118
- end
113
+ In real life, a simple use case would look something like:
119
114
 
120
- def change_this_resource(resource, params)
121
- resource.text = params[:new_text]
115
+ ```ruby
116
+ class Users::DeleteAccount
117
+ include UseCases[:validated, :transactional, :publishing, :validated]
122
118
 
123
- if resource.text == params[:new_text]
124
- Success(resource)
125
- else
126
- Failure([:failed, "could not update resource"])
127
- end
119
+ params do
120
+ required(:id).filled(:str?)
128
121
  end
129
122
 
130
- def log_a_message(resource)
131
- Logger.info('Resource updated')
132
- end
123
+ authorize :user_owns_account?, failure_message: 'Cannot delete account'
124
+ try :load_account, catch: ActiveRecord::RecordNotFound, failure: :account_not_found, failure_message: 'Account not found'
125
+ map :delete_account, publish: :account_deleted
126
+ enqueue :send_farewell_email
133
127
 
134
- def user_can_create_another_resource?(_, _, user)
135
- user.can_create?(Resource)
136
- end
128
+ private
137
129
 
138
- def create_some_already_validated_resource(resource, params, user)
139
- new_resource = Resource.create(text: params[:text])
130
+ def user_owns_account?(_previous_step_input, params, current_user)
131
+ current_user.account_id == params[:id]
140
132
  end
141
133
 
142
- def send_email_to_user(new_resource, _, user)
143
- ResourcEMailer.notify_user(user, new_resource).deliver!
134
+ def load_account(_previous_step_input, params, _current_user)
135
+ Account.find_by!(user_id: params[:id])
144
136
  end
145
- end
146
- ```
147
-
148
- ### Authorization
149
-
150
- `authorize` is a `check` step that returns an `:unauthorized` code in it's `Failure`.
151
137
 
152
- **Example**
153
-
154
- ```ruby
155
- class YourCase < UseCases::Base
156
- authorize 'User must be over 18' do |user|
157
- user.age >= 18
138
+ def delete_account(account, _params, _current_user)
139
+ account.destroy!
158
140
  end
159
- end
160
141
 
161
- user = User.where('age = 15').first
162
-
163
- YourCase.new.call({}, user) do |match|
164
- match.failure :unauthorized do |(code, result)|
165
- puts code
142
+ # since this executed async, all args are serialized
143
+ def send_farewell_email(account_attrs, params, current_user_attrs)
144
+ user = User.find(params[:id])
145
+ UserMailer.farewell(user).deliver_now!
166
146
  end
167
147
  end
168
- # => User must be over 18
169
148
  ```
170
149
 
171
- ### Example
150
+ #### Available Options
172
151
 
173
- For the case of creating posts within a thread.
152
+ | Name | Description | Expected Usage |
153
+ |---|---|---|
154
+ | `with` | Retrieves the callable object used to perform the step. |<em><br> Symbol: `send(options[:with])` <br> String: `UseCases.config.container[options[:with]]` <br> Class: `options[:with]`</em> |
155
+ | `pass` | An array of the arguments to pass to the object set by `with`. <br> _options: params, current_user & previous_step_result_ | Array\<Symbol> |
156
+ | `failure` | The code passed to the Failure object. | Symbol / String |
157
+ | `failure_message` | The string message passed to the Failure object. | Symbol / String |
158
+ | `catch` | Array of error classes to rescue from. | Array\<Exception>
174
159
 
175
- **Specs**
160
+ ## Installation
176
161
 
177
- - Only active users or the thread owner can post.
178
- - The post must be between 25 and 150 characters.
179
- - The post must be sanitized to remove any sensitive or explicit content.
180
- - The post must be saved to the database in the end.
181
- - In case any conditions are not met, an failure should be returned with it's own error code.
162
+ Add this line to your application's Gemfile:
182
163
 
183
164
  ```ruby
184
- class Posts::Create < UseCases::Base
185
-
186
- params do
187
- required(:body).filled(:string).value(size?: 25..150)
188
- required(:thread_id).filled(:integer)
189
- end
190
-
191
- try :load_post_thread, failure: :not_found
192
-
193
- authorize 'User is not active' do |user, params, thread|
194
- user.active?
195
- end
196
-
197
- authorize 'User must be active or the thread author.' do |user, params, thread|
198
- user.active? || thread.author == user
199
- end
200
-
201
- step :sanitize_body
202
-
203
- step :create_post
204
-
205
- private
165
+ gem 'use_cases'
166
+ ```
206
167
 
207
- def load_post_thread(params, user)
208
- Thread.find(params[:thread_id])
209
- end
168
+ And then execute:
210
169
 
211
- def sanitize_body(params)
212
- SanitizeText.new.call(params[:body]).to_monad
213
- end
170
+ $ bundle install
214
171
 
215
- def create_post(body, params, user)
216
- post = Post.new(body: body, user_id: user.id, thread_id: params[:thread_id])
172
+ Or install it yourself as:
217
173
 
218
- post.save ? Success(post) : Failure([:failed_to_save, post.errors.details])
219
- end
220
- end
221
- ```
174
+ $ gem install use_cases
222
175
 
223
- And in your controller action
176
+ ## Usage
224
177
 
225
- ```ruby
226
- # app/controllers/posts_controller.rb
227
- class PostsController < ApplicationController
228
- def create
229
- Posts::Create.new.call(params, current_user) do |match|
230
-
231
- # in success, the return value is the Success payload of the last step (#create_post)
232
- match.success do |post|
233
- # result => <Post:>
234
- end
235
-
236
- # in case ::params or any other dry-validation fails.
237
- match.failure :validation_error do |result|
238
- # result => [:validation_error, ['validation_error', { thread_id: 'is missing' }]
239
- end
240
-
241
- # in case ::try raises an error (ActiveRecord::NotFound in this case)
242
- match.failure :not_found do |result|
243
- # result => [:not_found, ['not_found', 'Could not find thread with id='<params[:thread_id]>'']
244
- end
245
-
246
- # in case any of the ::authorize blocks returns false
247
- match.failure :unauthorized do |result|
248
- # result => [:unauthorized, ['unauthorized', 'User is not active']
249
- end
250
-
251
- # in case #create_post returns a Failure
252
- match.failure :failed_to_save do |result|
253
- # result => [:failed_to_save, ['failed_to_save', { user_id: 'some error' }]
254
- end
255
- end
256
- end
257
- end
258
- ```
178
+ To get a good basis to get started on `UseCases`, make sure to read [dry-transaction](https://dry-rb.org/gems/dry-transaction/0.13/)'s documentation first.
259
179
 
260
180
  ## Development
261
181
 
@@ -5,14 +5,8 @@ module UseCases
5
5
  module Events
6
6
  class PublishJob < ActiveJob::Base
7
7
  def perform(publish_key, payload)
8
- publish_key += '.async'
9
- UseCases.publisher.publish(publish_key, payload)
10
- end
11
-
12
- private
13
-
14
- def publisher
15
- UseCases.publisher
8
+ publish_key += ".async"
9
+ UseCases.publisher.subscribe_and_publish_event(publish_key, payload)
16
10
  end
17
11
  end
18
12
  end
@@ -1,9 +1,52 @@
1
- require 'dry/events/publisher'
1
+ require "dry/events/publisher"
2
2
 
3
3
  module UseCases
4
4
  module Events
5
5
  class Publisher
6
6
  include Dry::Events::Publisher[:use_cases]
7
+
8
+ def subscribe_and_publish_event(event_name, payload)
9
+ subscribe_to_event(event_name)
10
+ publish(event_name, payload)
11
+ publish_async(event_name, payload) if event_should_be_published_asynchronously?(event_name)
12
+ end
13
+
14
+ private
15
+
16
+ def publish_async(event_name, payload)
17
+ UseCases::Events::PublishJob.perform_later(event_name, payload)
18
+ end
19
+
20
+ def event_should_be_published_asynchronously?(event_name)
21
+ defined? UseCases::Events::PublishJob && !event_name.end_with?(".async")
22
+ end
23
+
24
+ def subscribe_to_event(event_name)
25
+ subscribers_for(event_name).each(&method(:subscribe))
26
+ end
27
+
28
+ def subscribers_for(event_name)
29
+ available_subscribers.select do |subscriber|
30
+ subscriber.should_subscribe_to?(event_name) && !subscribed?(subscriber)
31
+ end
32
+ end
33
+
34
+ def subscribe(listener)
35
+ subcribers << listener
36
+ super
37
+ end
38
+
39
+ def subscribed?(listener)
40
+ subcribers.include?(listener)
41
+ end
42
+
43
+ def subcribers
44
+ @subcribers ||= []
45
+ end
46
+
47
+ def available_subscribers
48
+ UseCases.subscribers
49
+ end
7
50
  end
8
51
  end
9
52
  end
@@ -1,6 +1,15 @@
1
1
  module UseCases
2
2
  module Events
3
- class Subscriber
3
+ module Subscriber
4
+ def self.included(base)
5
+ UseCases.subscribers << base.new
6
+ end
7
+
8
+ def should_subscribe_to?(event_name)
9
+ event_handler_name = "on_#{event_name.gsub('.', '_')}"
10
+
11
+ respond_to?(event_handler_name.to_sym)
12
+ end
4
13
  end
5
14
  end
6
15
  end
@@ -9,35 +9,60 @@ module UseCases
9
9
  module Publishing
10
10
  def self.included(base)
11
11
  super
12
- base.prepend DoCallPatch
12
+ StepAdapters::Abstract.prepend StepPatch
13
13
  end
14
14
 
15
- module DoCallPatch
16
- def do_call(*args)
17
- super(*args, method(:publish_step_result).to_proc)
15
+ module StepPatch
16
+ def initialize(*args)
17
+ super
18
+ return unless options[:publish] && UseCases.publisher
19
+
20
+ register_events
21
+ end
22
+
23
+ def register_events
24
+ event_names.each do |event_name|
25
+ next if UseCases.publisher.class.events[event_name]
26
+
27
+ UseCases.publisher.class.register_event(event_name)
28
+ end
18
29
  end
19
30
 
20
- def publish_step_result(step_result, step_object)
21
- publish_key = step_object.options[:publish]
22
- publish_key += step_result.success? ? ".success" : ".failure"
23
- payload = step_result.value!
24
- return unless publish_key
31
+ def event_names
32
+ event_name = options[:publish]
33
+ [
34
+ "#{event_name}.success", "#{event_name}.failure",
35
+ "#{event_name}.success.async", "#{event_name}.failure.async"
36
+ ]
37
+ end
25
38
 
26
- register_event(publish_key)
27
- peform_publish(publish_key, payload)
39
+ def call(*args)
40
+ super(*args).tap do |result|
41
+ publish_step_result(result, args)
42
+ end
28
43
  end
29
44
 
30
- def register_event(publish_key)
31
- return if UseCases.publisher.class.events[publish_key]
45
+ def publish_step_result(step_result, args)
46
+ return unless options[:publish]
47
+
48
+ key = extract_event_key(step_result)
49
+ payload = extract_payload(step_result, args)
32
50
 
33
- UseCases.publisher.class.register_event(publish_key)
51
+ UseCases.publisher.subscribe_and_publish_event(key, payload)
34
52
  end
35
53
 
36
- def peform_publish(publish_key, payload)
37
- UseCases.publisher.publish(publish_key, payload)
38
- return unless defined? UseCases::Events::PublishJob
54
+ private
55
+
56
+ def extract_payload(step_result, args)
57
+ {
58
+ return_value: step_result.value!,
59
+ params: args[-2],
60
+ current_user: args[-1]
61
+ }
62
+ end
39
63
 
40
- UseCases::Events::PublishJob.perform_later(publish_key, payload.to_json)
64
+ def extract_event_key(step_result)
65
+ options[:publish].to_s + (step_result.success? ? ".success" : ".failure")
41
66
  end
42
67
  end
43
68
  end
@@ -38,7 +38,7 @@ RSpec::Matchers.define(:be_failure_with) do |*expected_failure|
38
38
  expect(test_subject.failure).to eql expected_failure
39
39
  end
40
40
 
41
- expected_result, expected_code = expect_failure
41
+ expected_result, expected_code = expected_failure
42
42
 
43
43
  failure_message do |test_subject|
44
44
  if test_subject.failure?
@@ -17,8 +17,6 @@ module UseCases
17
17
  def do_call(*args)
18
18
  stack.call do
19
19
  result = _run_step(stack, args)
20
- args.last.call(result, stack.current_step) if args.last.is_a? Proc
21
-
22
20
  return result if result.failure?
23
21
 
24
22
  result
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "dry/monads/all"
4
- require "byebug"
5
4
 
6
5
  module UseCases
7
6
  module StepAdapters
@@ -42,7 +41,7 @@ module UseCases
42
41
 
43
42
  return args.first(callable_args_count) unless external? && selects_external_args?
44
43
 
45
- hashed_args(args)
44
+ hashed_args(args).values
46
45
  end
47
46
 
48
47
  def hashed_args(args)
@@ -86,11 +85,11 @@ module UseCases
86
85
  end
87
86
 
88
87
  def external?
89
- with_option.present?
88
+ !with_option.nil?
90
89
  end
91
90
 
92
91
  def selects_external_args?
93
- pass_option.present?
92
+ !pass_option.nil?
94
93
  end
95
94
 
96
95
  def callable_args_count
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module UseCases
4
- VERSION = "1.0.4"
4
+ VERSION = "1.0.13"
5
5
  end
data/lib/use_cases.rb CHANGED
@@ -15,24 +15,6 @@ module UseCases
15
15
  extend Dry::Configurable
16
16
 
17
17
  setting :container, reader: true
18
- setting :publisher, ::UseCases::Events::Publisher.new, reader: true
19
- setting :subscribers, [], reader: true
20
-
21
- def self.configure(&blk)
22
- super
23
- finalize_subscriptions!
24
- end
25
-
26
- def self.finalize_subscriptions!
27
- subscribe(*subscribers)
28
- return unless UseCases::Events::Subscriber.respond_to?(:descendants)
29
-
30
- usecase_subscriber_children = UseCases::Events::Subscriber.descendants
31
- subscribe(*usecase_subscriber_children)
32
- subscribers.concat(usecase_subscriber_children)
33
- end
34
-
35
- def self.subscribe(*subscribers)
36
- subscribers.each { |subscriber| publisher.subscribe(subscriber) }
37
- end
18
+ setting :publisher, default: ::UseCases::Events::Publisher.new, reader: true
19
+ setting :subscribers, default: [], reader: true
38
20
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: use_cases
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 1.0.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ring Twice
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-01-05 00:00:00.000000000 Z
11
+ date: 2022-02-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -178,7 +178,7 @@ metadata:
178
178
  homepage_uri: https://github.com/listminut/use_cases
179
179
  source_code_uri: https://github.com/listminut/use_cases
180
180
  changelog_uri: https://github.com/listminut/use_cases/CHANGELOG.md
181
- post_install_message:
181
+ post_install_message:
182
182
  rdoc_options: []
183
183
  require_paths:
184
184
  - lib
@@ -193,8 +193,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
193
193
  - !ruby/object:Gem::Version
194
194
  version: '0'
195
195
  requirements: []
196
- rubygems_version: 3.0.3.1
197
- signing_key:
196
+ rubygems_version: 3.1.6
197
+ signing_key:
198
198
  specification_version: 4
199
199
  summary: Use Cases
200
200
  test_files: []