haku 1.3.1 → 1.4.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: 5fcbccd5d1d9c9be3d0d66259a210f75e52392ba1d76a7eff4b0ef375caae684
4
- data.tar.gz: 93632c6b6124810c8d08a316a162b449f8680612dec73ffaa931f01490a65a9c
3
+ metadata.gz: ca30959f8f3163c1aa8c7179f0e4ff3d84b2c46b736733ddefdbc3071c433b08
4
+ data.tar.gz: 8f1aaff1f0b2c64480ca5310b42cd68892306b64ac366d7813e99cf2f2616fce
5
5
  SHA512:
6
- metadata.gz: f9fe64de95c0c910d76f0309e746298f20aad3e64ce7222b7e7300645387a585b42a850cfdae520a7a12d5771dbc64c46660a935d9ac658e2b838dff0c7cf55b
7
- data.tar.gz: 5f0d174e5e0676e8fcee566bdb4d980b6a732cf028da40eb9fc1849585757e226add08f8f210bbdbb591ba31425ab9ee337ef57984d6044585bb26b1da069609
6
+ metadata.gz: 89b65efe9dc09eff84a3cf25def2301afaab54c296f8e0eb4b620750addc71af6f222aace23774edfd4b737fd88aed36fb2511373ed7fb3116eb811e97736f27
7
+ data.tar.gz: 296982f10418908126079b27030d5061316703cf2dbe415b8e707414954fd08d6ac36f93a236d930f7c25f818fffbfc3e475b8548f7aefb922b6dd98dcb79c9d
data/.rubocop.yml CHANGED
@@ -15,11 +15,29 @@ AllCops:
15
15
  - "**/gemfiles/**/*"
16
16
  - "**/Appraisals"
17
17
  NewCops: enable
18
- TargetRubyVersion: 2.6
18
+ TargetRubyVersion: 2.7
19
19
 
20
20
  Layout/SpaceAroundEqualsInParameterDefault:
21
21
  EnforcedStyle: no_space
22
22
 
23
+ Metrics/ClassLength:
24
+ CountAsOne:
25
+ - array
26
+ - hash
27
+ - heredoc
28
+
29
+ Metrics/MethodLength:
30
+ CountAsOne:
31
+ - array
32
+ - hash
33
+ - heredoc
34
+
35
+ Metrics/ModuleLength:
36
+ CountAsOne:
37
+ - array
38
+ - hash
39
+ - heredoc
40
+
23
41
  Style/Documentation:
24
42
  Enabled: false
25
43
 
data/Appraisals CHANGED
@@ -1,11 +1,7 @@
1
- appraise "activesupport-6.0" do
2
- gem "activesupport", "6.0"
1
+ appraise "rails-6.1" do
2
+ gem "rails", "~> 6.1.0"
3
3
  end
4
4
 
5
- appraise "activesupport-6.1" do
6
- gem "activesupport", "6.1"
7
- end
8
-
9
- appraise "activesupport-7.0" do
10
- gem "activesupport", "7.0"
5
+ appraise "rails-7.0" do
6
+ gem "rails", "~> 7.0"
11
7
  end
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.4.0] - 2022-09-19
4
+ - Improve `Haku::Eventable` module to support more data sources for event properties
5
+ - Add `Haku::Delayable` module to execute service object in background
6
+ - Improve README
7
+ - **Breaking**: Require `activesupport` >= 6.1
8
+ - **Breaking**: Require Ruby >= 2.7
9
+ - Fix CI test invocation
10
+
3
11
  ## [1.3.1] - 2022-04-27
4
12
  - Fix Haku::Controller
5
13
 
data/Gemfile.lock CHANGED
@@ -11,13 +11,13 @@ GIT
11
11
  PATH
12
12
  remote: .
13
13
  specs:
14
- haku (1.3.1)
15
- activesupport (>= 6.0, < 8.0)
14
+ haku (1.4.0)
15
+ activesupport (>= 6.1, < 8.0)
16
16
 
17
17
  GEM
18
18
  remote: https://rubygems.org/
19
19
  specs:
20
- activesupport (7.0.2.4)
20
+ activesupport (7.0.4)
21
21
  concurrent-ruby (~> 1.0, >= 1.0.2)
22
22
  i18n (>= 1.6, < 2)
23
23
  minitest (>= 5.1)
@@ -25,7 +25,7 @@ GEM
25
25
  ast (2.4.2)
26
26
  coderay (1.1.3)
27
27
  concurrent-ruby (1.1.10)
28
- i18n (1.10.0)
28
+ i18n (1.12.0)
29
29
  concurrent-ruby (~> 1.0)
30
30
  method_source (1.0.0)
31
31
  minitest (5.15.0)
@@ -56,7 +56,7 @@ GEM
56
56
  rubocop (~> 1.0)
57
57
  ruby-progressbar (1.11.0)
58
58
  thor (1.2.1)
59
- tzinfo (2.0.4)
59
+ tzinfo (2.0.5)
60
60
  concurrent-ruby (~> 1.0)
61
61
  unicode-display_width (2.1.0)
62
62
 
data/README.md CHANGED
@@ -20,9 +20,18 @@ bundle install
20
20
 
21
21
  ## Usage
22
22
 
23
- ### Basic example
23
+ **Haku** is made up of four modules that add functionality to our service objects:
24
24
 
25
- ````ruby
25
+ * `Haku::Core`
26
+ * `Haku::Delayable`
27
+ * `Haku::Eventable`
28
+ * `Haku::Resourceable`
29
+
30
+ Additionally, it's available the `Haku::Controller` module for use in ours Rails controllers.
31
+
32
+ ### Haku::Core
33
+
34
+ ```ruby
26
35
  class Users::Update
27
36
  include Haku::Core
28
37
 
@@ -49,11 +58,98 @@ response = Users::Update.call(user: User.first, attributes: { name: "Javier" })
49
58
  response.success? # => true
50
59
  response.result # => { resource: <User id="1" ...> }
51
60
  response.resource # => <User id="1" ...>
52
- ````
61
+ ```
62
+
63
+ As you can see, if the payload passed to `success!` or `failure!` is a hash, each key of the hash can be accessed
64
+ directly in response object.
65
+
66
+ ### Haku::Delayable
67
+
68
+ #### Basic example
69
+
70
+ ```ruby
71
+ class Users::ComputeHours
72
+ include Haku::Core
73
+ include Haku::Delayable
74
+
75
+ input :user
76
+
77
+ def call
78
+ # compute expensive data for user
79
+ end
80
+ end
81
+
82
+ Users::ComputeHours.delayed.call(user: User.first)
83
+ ```
84
+
85
+ Use `delayed.call` instead of `call` for execute service object in background using `ActiveJob` job.
86
+
87
+ #### Customize job
88
+
89
+ ```ruby
90
+ Users::ComputeHours.delayed(job: OtherJob).call(user: User.first)
91
+ ```
92
+
93
+ #### Customize job options
94
+
95
+ ```ruby
96
+ Users::ComputeHours.delayed(job: OtherJob, queue: :low, priority: 2).call(user: User.first)
97
+ ```
98
+
99
+ You can pass the same options allowed by ActiveJob
100
+ [set](https://api.rubyonrails.org/v7.0.4/classes/ActiveJob/Core/ClassMethods.html#method-i-set) method:
101
+
102
+
103
+ ### Haku::Eventable
104
+
105
+ #### Basic example
106
+
107
+ ```ruby
108
+ class Users::Update
109
+ include Haku::Core
110
+ include Haku::Eventable
111
+
112
+ input :user, :attributes
113
+ event resource: :user
114
+
115
+ def call
116
+ success! resource: user
117
+ end
118
+ end
119
+
120
+ Users::Update.call(user: User.first, attributes: { name: "Javier" })
121
+
122
+ # => call Event.create(name: "user:update", resource: User.first)
123
+ ```
124
+
125
+ The `name` attribute are calculated using the custom proc from `event_name` config option. You can change it with
126
+
127
+ ```ruby
128
+ event name: 'custom:name', resource: :user
129
+ ```
53
130
 
54
- ### Using resourceable helpers
131
+ #### Properties passed to event model
55
132
 
56
- ````ruby
133
+ Properties that should be passed to event model are defined in `event_properties` config option and by
134
+ default are `actor` `resource`, `target` and `context`.
135
+
136
+ 1. Append base properties. For each property defined in `event_properties` config option, it will try to:
137
+ 1. If defined a instance variable `@event_<property>`, uses the instance variable value to get the value of property.
138
+ 2. If respond_to method called `event_<property>`, method is used to get the value of property.
139
+ 3. If defined a instance variable `@<property>`, uses the instance variable value to get the value of property.
140
+ 4. If respond_to method called `<property>`, method is used to get the value of property.
141
+ 5. In other case, the property is not appended.
142
+ 2. Append properties defined in `event` class method. Overwrites previous values. For each property, will try to:
143
+ 1. If is a block, it is called to get the value of property.
144
+ 2. If is a symbol:
145
+ 1. If defined a instance variable `@<property>`, uses the instance variable value to get the value of property.
146
+ 2. If respond_to `<property>`, method is used to get the value of property.
147
+ 3. In other case, uses the raw value.
148
+
149
+
150
+ ### Haku::Resourceable
151
+
152
+ ```ruby
57
153
  class Users::Update
58
154
  include Haku::Core
59
155
  include Haku::Resourceable
@@ -71,12 +167,15 @@ class Users::Update
71
167
  UserMailer.with(user: user).update.deliver_later
72
168
  end
73
169
  end
74
- ````
170
+ ```
171
+
75
172
 
76
- ### Controller helpers
173
+ ### Haku::Controller
77
174
 
78
175
  ```ruby
79
176
  class UsersController < ApplicationController
177
+ include Haku::Controller
178
+
80
179
  before_action :find_user
81
180
 
82
181
  def update
@@ -101,26 +200,6 @@ class UsersController < ApplicationController
101
200
  end
102
201
  ```
103
202
 
104
- ### Using eventable
105
-
106
- ````ruby
107
- class Users::Update
108
- include Haku::Core
109
- include Haku::Resourceable
110
- include Haku::Eventable
111
-
112
- input :user, :attributes
113
- event resource: :user
114
-
115
- def call
116
- update_resource(user, attributes)
117
- end
118
- end
119
-
120
- Users::Update.call(user: User.first, attributes: { name: "Javier" })
121
-
122
- # => call Event.create(name: "user:update", resource: User.first)
123
- ````
124
203
 
125
204
  ### Using parent class
126
205
 
@@ -135,6 +214,7 @@ class Users::Update < ApplicationAction
135
214
  end
136
215
  ```
137
216
 
217
+
138
218
  ## Configure
139
219
 
140
220
  ### Example
@@ -149,14 +229,13 @@ end
149
229
 
150
230
  ### Allowed options
151
231
 
152
- | Config | Description | Default value |
153
- |:-----------------------------------|:-------------------------------------------------------|:---------------------------------------------------------|
154
- | `enable_in_action_controller_base` | Include controller helpers in `ActionController::Base` | `true` |
155
- | `enable_in_action_controller_api` | Include controller helpers in `ActionController::API` | `true` |
156
- | `event_model` | Name of the model used for create events | `Event` |
157
- | `event_properties` | List of attributes passed from service to event model | `%i[actor resource target context]` |
158
- | `event_property_for_name` | Property used for name in event model | `:name` |
159
- | `event_name` | String or Proc to determine the event name | Custom Proc. Example: `user:created` for `Users::Create` |
232
+ | Config | Description | Default value |
233
+ |:--------------------------|:------------------------------------------------------|:--------------------------------------------------------|
234
+ | `event_model` | Name of the model used for create events | `Event` |
235
+ | `event_properties` | List of attributes passed from service to event model | `%i[actor resource target context]` |
236
+ | `event_property_for_name` | Property used for name in event model | `:name` |
237
+ | `event_name` | String or Proc to determine the event name | Custom Proc. Example: `user:create` for `Users::Create` |
238
+ | `job_queue` | String or Symbol with queue name | `default` |
160
239
 
161
240
 
162
241
  ## Resourceable
@@ -210,6 +289,7 @@ Call to `destroy` method of the `resource`. Invoke `success!` if the model is de
210
289
 
211
290
  For more info please view the [source code](lib/haku/resourceable.rb) of the module.
212
291
 
292
+
213
293
  ## Development
214
294
 
215
295
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests.
@@ -221,16 +301,19 @@ To release a new version, update the version number in `version.rb`, and then ru
221
301
  which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to
222
302
  [rubygems.org](https://rubygems.org).
223
303
 
304
+
224
305
  ## Contributing
225
306
 
226
307
  Bug reports and pull requests are welcome, please follow
227
308
  [Github Flow](https://docs.github.com/en/get-started/quickstart/github-flow).
228
309
 
310
+
229
311
  ## Code of Conduct
230
312
 
231
313
  Everyone interacting in the Haku project's codebases, issue trackers, chat rooms and mailing lists is expected to
232
314
  follow the [code of conduct](https://github.com/javierav/haku/blob/development/CODE_OF_CONDUCT.md).
233
315
 
316
+
234
317
  ## License
235
318
 
236
319
  Copyright © 2022 Javier Aranda. Released under the terms of the [MIT license](LICENSE).
@@ -2,13 +2,13 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "appraisal", "~> 2.4"
5
+ gem "appraisal", github: "excid3/appraisal", branch: "fix-bundle-env"
6
6
  gem "minitest", "~> 5.0"
7
7
  gem "pry", "~> 0.14"
8
8
  gem "rake", "~> 13.0"
9
9
  gem "rubocop", "~> 1.21"
10
10
  gem "rubocop-minitest", "~> 0.19"
11
11
  gem "rubocop-rake", "~> 0.6"
12
- gem "activesupport", "6.1"
12
+ gem "rails", "~> 6.1.0"
13
13
 
14
14
  gemspec path: "../"
@@ -2,13 +2,13 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "appraisal", "~> 2.4"
5
+ gem "appraisal", github: "excid3/appraisal", branch: "fix-bundle-env"
6
6
  gem "minitest", "~> 5.0"
7
7
  gem "pry", "~> 0.14"
8
8
  gem "rake", "~> 13.0"
9
9
  gem "rubocop", "~> 1.21"
10
10
  gem "rubocop-minitest", "~> 0.19"
11
11
  gem "rubocop-rake", "~> 0.6"
12
- gem "activesupport", "6.0"
12
+ gem "rails", "~> 7.0"
13
13
 
14
14
  gemspec path: "../"
data/lib/haku/core.rb CHANGED
@@ -14,6 +14,7 @@ module Haku
14
14
  attr_reader :params
15
15
 
16
16
  class_attribute :haku_inputs, default: []
17
+ class_attribute :haku_before_call_callbacks, default: []
17
18
  class_attribute :haku_success_callbacks, default: []
18
19
  class_attribute :haku_failure_callbacks, default: []
19
20
  end
@@ -35,6 +36,10 @@ module Haku
35
36
  self.haku_inputs += names
36
37
  end
37
38
 
39
+ def before_call(*methods)
40
+ self.haku_before_call_callbacks += methods
41
+ end
42
+
38
43
  def on_success(*methods)
39
44
  self.haku_success_callbacks += methods
40
45
  end
@@ -48,13 +53,16 @@ module Haku
48
53
 
49
54
  module Callable
50
55
  def call
51
- response = catch(:finish) { super }
56
+ response = catch(:finish) do
57
+ (self.class.send(:haku_before_call_callbacks) || []).each { |cb| send(cb) }
58
+ super
59
+ end
52
60
 
53
61
  status = response.is_a?(Finish) ? response.status : :success
54
62
  payload = response.is_a?(Finish) ? response.payload : response
55
63
 
56
64
  Result.new(status, payload).tap do
57
- _haku_run_callbacks(status)
65
+ haku_run_callbacks(status)
58
66
  end
59
67
  end
60
68
  end
@@ -77,7 +85,7 @@ module Haku
77
85
  throw :finish, Finish.new(:failure, data)
78
86
  end
79
87
 
80
- def _haku_run_callbacks(status)
88
+ def haku_run_callbacks(status)
81
89
  (self.class.send("haku_#{status}_callbacks") || []).each { |cb| send(cb) }
82
90
  end
83
91
  end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+ require "active_support/core_ext/string/inflections"
5
+
6
+ module Haku
7
+ module Delayable
8
+ extend ActiveSupport::Concern
9
+
10
+ if defined?(ActiveJob::Base)
11
+ class Job < ActiveJob::Base
12
+ def perform(klass, params)
13
+ klass.call(params)
14
+ end
15
+ end
16
+ end
17
+
18
+ class Delayed
19
+ def initialize(service, options={})
20
+ @service = service
21
+ @options = options.reverse_merge(
22
+ job: "::Haku::Delayable::Job",
23
+ queue: Haku.job_queue
24
+ )
25
+ end
26
+
27
+ def call(params={})
28
+ if job.present? && defined?(job)
29
+ job.set(@options).perform_later(@service, params)
30
+ else
31
+ @service.call(params)
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def job
38
+ @job ||= begin
39
+ job = @options.delete(:job)
40
+ job.is_a?(String) ? job.safe_constantize : job
41
+ end
42
+ end
43
+ end
44
+
45
+ module ClassMethods
46
+ def delayed(options={})
47
+ ::Haku::Delayable::Delayed.new(self, options)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -47,21 +47,25 @@ module Haku
47
47
 
48
48
  def haku_event_data_base(data)
49
49
  Haku.event_properties.each do |property|
50
- data[property] = send(property) if respond_to?(property)
50
+ haku_event_data_base_value_for_property(data, property)
51
51
  end
52
52
  end
53
53
 
54
- def haku_process_value(value)
55
- if value.respond_to?(:call)
56
- instance_exec(&value)
57
- else
58
- value.is_a?(Symbol) ? send(value) : value
54
+ def haku_event_data_base_value_for_property(data, property)
55
+ if instance_variable_defined?("@event_#{property}")
56
+ data[property] = instance_variable_get("@event_#{property}")
57
+ elsif respond_to?("event_#{property}", true)
58
+ data[property] = send("event_#{property}")
59
+ elsif instance_variable_defined?("@#{property}")
60
+ data[property] = instance_variable_get("@#{property}")
61
+ elsif respond_to?(property, true)
62
+ data[property] = send(property)
59
63
  end
60
64
  end
61
65
 
62
66
  def haku_event_data_name(data, evt)
63
67
  key = Haku.event_property_for_name.to_sym
64
- data[key] = evt[key] || haku_process_value(Haku.event_name)
68
+ data[key] = haku_process_value(evt[key] || Haku.event_name)
65
69
  end
66
70
 
67
71
  def haku_event_data_values(data, evt)
@@ -70,6 +74,18 @@ module Haku
70
74
  end
71
75
  end
72
76
 
77
+ def haku_process_value(value)
78
+ if value.respond_to?(:call)
79
+ instance_exec(&value)
80
+ else
81
+ value.is_a?(Symbol) ? haku_process_symbol_value(value) : value
82
+ end
83
+ end
84
+
85
+ def haku_process_symbol_value(value)
86
+ instance_variable_defined?("@#{value}") ? instance_variable_get("@#{value}") : send(value)
87
+ end
88
+
73
89
  def haku_create_event(data)
74
90
  Haku.event_model.safe_constantize&.create(data)
75
91
  end
data/lib/haku/version.rb CHANGED
@@ -3,8 +3,8 @@
3
3
  module Haku
4
4
  module VERSION
5
5
  MAJOR = 1
6
- MINOR = 3
7
- TINY = 1
6
+ MINOR = 4
7
+ TINY = 0
8
8
  PRE = nil
9
9
 
10
10
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
data/lib/haku.rb CHANGED
@@ -4,6 +4,7 @@ require "active_support/core_ext/module/attribute_accessors"
4
4
  require "active_support/core_ext/string/inflections"
5
5
  require_relative "haku/controller"
6
6
  require_relative "haku/core"
7
+ require_relative "haku/delayable"
7
8
  require_relative "haku/eventable"
8
9
  require_relative "haku/resourceable"
9
10
  require_relative "haku/version"
@@ -16,6 +17,7 @@ module Haku
16
17
  chain = self.class.name.underscore.split("/")
17
18
  (chain[0...-1].map(&:singularize) + [chain.last]).join(":")
18
19
  }
20
+ mattr_accessor :job_queue, default: :default
19
21
 
20
22
  class << self
21
23
  def configure
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: haku
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Javier Aranda
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-04-27 00:00:00.000000000 Z
11
+ date: 2022-09-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -16,7 +16,7 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '6.0'
19
+ version: '6.1'
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
22
  version: '8.0'
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: '6.0'
29
+ version: '6.1'
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
32
  version: '8.0'
@@ -52,12 +52,12 @@ files:
52
52
  - LICENSE
53
53
  - README.md
54
54
  - Rakefile
55
- - gemfiles/activesupport_6.0.gemfile
56
- - gemfiles/activesupport_6.1.gemfile
57
- - gemfiles/activesupport_7.0.gemfile
55
+ - gemfiles/rails_6.1.gemfile
56
+ - gemfiles/rails_7.0.gemfile
58
57
  - lib/haku.rb
59
58
  - lib/haku/controller.rb
60
59
  - lib/haku/core.rb
60
+ - lib/haku/delayable.rb
61
61
  - lib/haku/eventable.rb
62
62
  - lib/haku/resourceable.rb
63
63
  - lib/haku/result.rb
@@ -67,8 +67,8 @@ licenses:
67
67
  - MIT
68
68
  metadata:
69
69
  homepage_uri: https://github.com/javierav/haku
70
- source_code_uri: https://github.com/javierav/haku/tree/v1.3.1
71
- changelog_uri: https://github.com/javierav/haku/blob/v1.3.1/CHANGELOG.md
70
+ source_code_uri: https://github.com/javierav/haku/tree/v1.4.0
71
+ changelog_uri: https://github.com/javierav/haku/blob/v1.4.0/CHANGELOG.md
72
72
  rubygems_mfa_required: 'true'
73
73
  post_install_message:
74
74
  rdoc_options:
@@ -79,7 +79,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
- version: 2.6.0
82
+ version: 2.7.0
83
83
  required_rubygems_version: !ruby/object:Gem::Requirement
84
84
  requirements:
85
85
  - - ">="
@@ -1,14 +0,0 @@
1
- # This file was generated by Appraisal
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "appraisal", "~> 2.4"
6
- gem "minitest", "~> 5.0"
7
- gem "pry", "~> 0.14"
8
- gem "rake", "~> 13.0"
9
- gem "rubocop", "~> 1.21"
10
- gem "rubocop-minitest", "~> 0.19"
11
- gem "rubocop-rake", "~> 0.6"
12
- gem "activesupport", "7.0"
13
-
14
- gemspec path: "../"