rails_ops 1.2.3 → 1.3.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: 9aebb67a89b1cc32ee09838441c6fd5739fb40f44662d9f09519ffbb09a63066
4
- data.tar.gz: 16dde2b894f56aef4f774a8d7b421b4928ee566b058d54c5d4aa7446ee877270
3
+ metadata.gz: 365397242478d0f5dd89c5eadf97871c2e5d9a6a2f5b59dbfdbdb5a060bf56e0
4
+ data.tar.gz: 45f57e7a2abfe574b2c7caeaa135fb4896c364bdc9e36c5adc673640e6be1351
5
5
  SHA512:
6
- metadata.gz: 6bc197031923788967f7f972bef74c2f7d122d6aa23287911c9bcde5ed95434ac7612a3366a99641c2fca30acc069c8591a62af5c0edf603fb77fc5609799297
7
- data.tar.gz: 11a35f00a823959e1e0dd8859f32d404ca7a2b7c38c9f17695332e5a0bea40b0452be88f57a50e58d695264e3bae5f8ca7d88d2fc98f4649de28038705518a49
6
+ metadata.gz: c58cba8d71ca052e676323d98a0dbdd58d39db4f3c1c9a15ed95d4f8714ae3377bbd4e7a45c5b83bccf74dba34ac573a690cd839174a3600ea03d0c3abab5514
7
+ data.tar.gz: f3eebf9294ec8a7a84be52329964b6ee5a717099d52b10dcf408d2a7eaf92d19072b3b85b84affe33348117ed113a2cfeb10beed412c371270920ecbe86370a4
@@ -29,6 +29,8 @@ jobs:
29
29
  ruby-version: '3.0.1'
30
30
  - rails-version: '7.0'
31
31
  ruby-version: '3.1.0'
32
+ - rails-version: '7.0'
33
+ ruby-version: '3.2.0'
32
34
  name: Test against Ruby ${{ matrix.ruby-version }} / Rails ${{ matrix.rails-version }}
33
35
  env:
34
36
  BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/rails_${{ matrix.rails-version }}.gemfile
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.3.0 (2023-01-23)
4
+
5
+ * Add `lock_mode` DSL method
6
+ * Set `Load` operations to use shared locking, and `Update` and `Destroy`
7
+ operations to use exclusive locking. Please make sure your operations
8
+ inherit from the correct parent operation, and change the locking mode
9
+ if it is not the correct one for your operation. More info can be found
10
+ in the section "Locking" in the Readme
11
+
3
12
  ## 1.2.3 (2023-01-04)
4
13
 
5
14
  * Fix marshalling of operation models. This is especially useful for use in
data/README.md CHANGED
@@ -1133,6 +1133,32 @@ class Operations::User::Update < RailsOps::Operation::Model::Update
1133
1133
  end
1134
1134
  ```
1135
1135
 
1136
+ Please note that for performance reasons, the `Load` operation (and any
1137
+ operations inheriting from it) use a shared lock, i.e. it issues
1138
+ an `LOCK IN SHARE MODE` / `FOR SHARE` statement. The `Update` and `Destroy`
1139
+ operations (as well as operations inheriting from it) however use the default
1140
+ `lock` method of ActiveRecord, which will issue an exclusive lock.
1141
+
1142
+ If you want to change the mode, you can use the `lock_mode` DSL method, which
1143
+ has two possible modes:
1144
+
1145
+ * `:shared` for the shared lock mode
1146
+ * `:exclusive` for the exclusive lock mode
1147
+
1148
+ For example, if you have an operation loading a record which you'd want to
1149
+ lock exclusively, you'd need to write the following:
1150
+
1151
+ ```ruby
1152
+ class Operations::User::Update < RailsOps::Operation::Model::Load
1153
+ model ::User
1154
+ lock_mode :exclusive
1155
+ end
1156
+ ```
1157
+
1158
+ One caveat is, that shared locking is only supported for MySQl (MariaDB),
1159
+ Postgresql and Oracle DB databases, any other database will always use an
1160
+ exlusive lock.
1161
+
1136
1162
  ### Creating models
1137
1163
 
1138
1164
  For creating models, you can use the base class
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.2.3
1
+ 1.3.0
@@ -5,14 +5,13 @@ module RailsOps
5
5
  # with default procs which is not supported by `Marshal.dump`. This mixin
6
6
  # therefore excludes this instance variable from being dumped and loaded.
7
7
  module Marshalling
8
- UNMARSHALED_VARIABLES = %i[@parent_op].freeze
8
+ UNMARSHALED_VARIABLES = %i(@parent_op).freeze
9
9
 
10
10
  extend ActiveSupport::Concern
11
11
 
12
12
  def marshal_dump
13
- instance_variables.reject{|m| UNMARSHALED_VARIABLES.include? m}.inject({}) do |vars, attr|
13
+ instance_variables.reject { |m| UNMARSHALED_VARIABLES.include? m }.each_with_object({}) do |attr, vars|
14
14
  vars[attr] = instance_variable_get(attr)
15
- vars
16
15
  end
17
16
  end
18
17
 
@@ -1,5 +1,6 @@
1
1
  class RailsOps::Operation::Model::Destroy < RailsOps::Operation::Model::Load
2
2
  model_authorization_action :destroy
3
+ lock_mode :exclusive
3
4
 
4
5
  def model_authorization
5
6
  return unless authorization_enabled?
@@ -1,6 +1,7 @@
1
1
  class RailsOps::Operation::Model::Load < RailsOps::Operation::Model
2
2
  class_attribute :_lock_model_at_build
3
3
  class_attribute :_load_model_authorization_action
4
+ class_attribute :_lock_mode
4
5
 
5
6
  policy :on_init do
6
7
  model_authorization
@@ -39,6 +40,24 @@ class RailsOps::Operation::Model::Load < RailsOps::Operation::Model
39
40
  _lock_model_at_build.nil? ? RailsOps.config.lock_models_at_build? : _lock_model_at_build
40
41
  end
41
42
 
43
+ # Method to set the lock mode, which can either be :exclusive to use
44
+ # an exclusive write lock, or :shared to use a shared lock. Please note
45
+ # that currently, :shared only works for MySql, Postgresql and Oracle DB,
46
+ # other adapters always use the exclusive lock.
47
+ def self.lock_mode(lock_mode)
48
+ fail "Unknown lock mode #{lock_mode}" unless %i(shared exclusive).include?(lock_mode)
49
+
50
+ self._lock_mode = lock_mode
51
+ end
52
+
53
+ # Get the lock_mode or the default of :exclusive
54
+ def self.lock_mode_or_default
55
+ _lock_mode.presence || :exclusive
56
+ end
57
+
58
+ # Set the lock mode for load operations (and it's children) to :shared
59
+ lock_mode :shared
60
+
42
61
  def model_id_field
43
62
  :id
44
63
  end
@@ -52,7 +71,7 @@ class RailsOps::Operation::Model::Load < RailsOps::Operation::Model
52
71
  relation = self.class.model
53
72
 
54
73
  # Express intention to lock if required
55
- relation = relation.lock if self.class.lock_model_at_build?
74
+ relation = lock_relation(relation)
56
75
 
57
76
  # Fetch (and possibly lock) model
58
77
  return relation.find_by!(model_id_field => params[model_id_field])
@@ -69,4 +88,38 @@ class RailsOps::Operation::Model::Load < RailsOps::Operation::Model
69
88
  def extract_id_from_params
70
89
  params[model_id_field]
71
90
  end
91
+
92
+ private
93
+
94
+ def lock_relation(relation)
95
+ # Directly return the relation if we don't want to lock the relation
96
+ return relation unless self.class.lock_model_at_build?
97
+
98
+ if self.class.lock_mode_or_default == :shared
99
+ # Lock the relation in shared mode
100
+ return relation.lock(shared_lock_statement)
101
+ else
102
+ # Lock the relation in exclusive mode
103
+ return relation.lock
104
+ end
105
+ end
106
+
107
+ def shared_lock_statement
108
+ adapter_type = ActiveRecord::Base.connection.adapter_name.downcase.to_sym
109
+
110
+ case adapter_type
111
+ when :mysql, :mysql2
112
+ return 'LOCK IN SHARE MODE'
113
+ when :postgresql
114
+ return 'FOR SHARE'
115
+ when :oracleenhanced
116
+ return 'LOCK IN SHARE MODE'
117
+ end
118
+
119
+ # Don't return anything, which will make the `lock` statement
120
+ # use the normal, exclusive lock. This might be suboptimal for other
121
+ # database adapters, but we'd rather lock too restrictive, such that
122
+ # no race-conditions may occur.
123
+ return nil
124
+ end
72
125
  end
@@ -1,5 +1,6 @@
1
1
  class RailsOps::Operation::Model::Update < RailsOps::Operation::Model::Load
2
2
  model_authorization_action :update
3
+ lock_mode :exclusive
3
4
 
4
5
  # As this operation might extend the model class, we need to make sure that
5
6
  # the operation works using an extended 'copy' of the given model class.
data/rails_ops.gemspec CHANGED
@@ -1,16 +1,16 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: rails_ops 1.2.3 ruby lib
2
+ # stub: rails_ops 1.3.0 ruby lib
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "rails_ops".freeze
6
- s.version = "1.2.3"
6
+ s.version = "1.3.0"
7
7
 
8
8
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
9
9
  s.require_paths = ["lib".freeze]
10
10
  s.authors = ["Sitrox".freeze]
11
- s.date = "2023-01-04"
11
+ s.date = "2023-01-23"
12
12
  s.files = [".github/workflows/rubocop.yml".freeze, ".github/workflows/ruby.yml".freeze, ".gitignore".freeze, ".releaser_config".freeze, ".rubocop.yml".freeze, "Appraisals".freeze, "CHANGELOG.md".freeze, "Gemfile".freeze, "LICENSE".freeze, "README.md".freeze, "Rakefile".freeze, "VERSION".freeze, "gemfiles/rails_6.0.gemfile".freeze, "gemfiles/rails_6.1.gemfile".freeze, "gemfiles/rails_7.0.gemfile".freeze, "lib/generators/operation/USAGE".freeze, "lib/generators/operation/operation_generator.rb".freeze, "lib/generators/operation/templates/controller.erb".freeze, "lib/generators/operation/templates/create.erb".freeze, "lib/generators/operation/templates/destroy.erb".freeze, "lib/generators/operation/templates/load.erb".freeze, "lib/generators/operation/templates/update.erb".freeze, "lib/generators/operation/templates/view.erb".freeze, "lib/rails_ops.rb".freeze, "lib/rails_ops/authorization_backend/abstract.rb".freeze, "lib/rails_ops/authorization_backend/can_can_can.rb".freeze, "lib/rails_ops/configuration.rb".freeze, "lib/rails_ops/context.rb".freeze, "lib/rails_ops/controller_mixin.rb".freeze, "lib/rails_ops/exceptions.rb".freeze, "lib/rails_ops/hooked_job.rb".freeze, "lib/rails_ops/hookup.rb".freeze, "lib/rails_ops/hookup/dsl.rb".freeze, "lib/rails_ops/hookup/dsl_validator.rb".freeze, "lib/rails_ops/hookup/hook.rb".freeze, "lib/rails_ops/log_subscriber.rb".freeze, "lib/rails_ops/mixins.rb".freeze, "lib/rails_ops/mixins/authorization.rb".freeze, "lib/rails_ops/mixins/log_settings.rb".freeze, "lib/rails_ops/mixins/model.rb".freeze, "lib/rails_ops/mixins/model/authorization.rb".freeze, "lib/rails_ops/mixins/model/nesting.rb".freeze, "lib/rails_ops/mixins/param_authorization.rb".freeze, "lib/rails_ops/mixins/policies.rb".freeze, "lib/rails_ops/mixins/require_context.rb".freeze, "lib/rails_ops/mixins/routes.rb".freeze, "lib/rails_ops/mixins/schema_validation.rb".freeze, "lib/rails_ops/mixins/sub_ops.rb".freeze, "lib/rails_ops/model_mixins.rb".freeze, "lib/rails_ops/model_mixins/ar_extension.rb".freeze, "lib/rails_ops/model_mixins/marshalling.rb".freeze, "lib/rails_ops/model_mixins/parent_op.rb".freeze, "lib/rails_ops/model_mixins/sti_fixes.rb".freeze, "lib/rails_ops/model_mixins/virtual_attributes.rb".freeze, "lib/rails_ops/model_mixins/virtual_attributes/virtual_column_wrapper.rb".freeze, "lib/rails_ops/model_mixins/virtual_has_one.rb".freeze, "lib/rails_ops/model_mixins/virtual_model_name.rb".freeze, "lib/rails_ops/operation.rb".freeze, "lib/rails_ops/operation/model.rb".freeze, "lib/rails_ops/operation/model/create.rb".freeze, "lib/rails_ops/operation/model/destroy.rb".freeze, "lib/rails_ops/operation/model/load.rb".freeze, "lib/rails_ops/operation/model/update.rb".freeze, "lib/rails_ops/profiler.rb".freeze, "lib/rails_ops/profiler/node.rb".freeze, "lib/rails_ops/railtie.rb".freeze, "lib/rails_ops/scoped_env.rb".freeze, "lib/rails_ops/virtual_model.rb".freeze, "rails_ops.gemspec".freeze, "test/db/models.rb".freeze, "test/db/schema.rb".freeze, "test/dummy/Rakefile".freeze, "test/dummy/app/assets/config/manifest.js".freeze, "test/dummy/app/assets/images/.keep".freeze, "test/dummy/app/assets/javascripts/application.js".freeze, "test/dummy/app/assets/javascripts/cable.js".freeze, "test/dummy/app/assets/javascripts/channels/.keep".freeze, "test/dummy/app/assets/stylesheets/application.css".freeze, "test/dummy/app/channels/application_cable/channel.rb".freeze, "test/dummy/app/channels/application_cable/connection.rb".freeze, "test/dummy/app/controllers/application_controller.rb".freeze, "test/dummy/app/controllers/concerns/.keep".freeze, "test/dummy/app/helpers/application_helper.rb".freeze, "test/dummy/app/jobs/application_job.rb".freeze, "test/dummy/app/mailers/application_mailer.rb".freeze, "test/dummy/app/models/animal.rb".freeze, "test/dummy/app/models/application_record.rb".freeze, "test/dummy/app/models/bird.rb".freeze, "test/dummy/app/models/cat.rb".freeze, "test/dummy/app/models/concerns/.keep".freeze, "test/dummy/app/models/dog.rb".freeze, "test/dummy/app/models/flower.rb".freeze, "test/dummy/app/models/group.rb".freeze, "test/dummy/app/models/nightingale.rb".freeze, "test/dummy/app/models/phoenix.rb".freeze, "test/dummy/app/models/user.rb".freeze, "test/dummy/app/views/layouts/application.html.erb".freeze, "test/dummy/app/views/layouts/mailer.html.erb".freeze, "test/dummy/app/views/layouts/mailer.text.erb".freeze, "test/dummy/bin/bundle".freeze, "test/dummy/bin/rails".freeze, "test/dummy/bin/rake".freeze, "test/dummy/bin/setup".freeze, "test/dummy/bin/update".freeze, "test/dummy/bin/yarn".freeze, "test/dummy/config.ru".freeze, "test/dummy/config/application.rb".freeze, "test/dummy/config/boot.rb".freeze, "test/dummy/config/cable.yml".freeze, "test/dummy/config/database.yml".freeze, "test/dummy/config/environment.rb".freeze, "test/dummy/config/environments/development.rb".freeze, "test/dummy/config/environments/production.rb".freeze, "test/dummy/config/environments/test.rb".freeze, "test/dummy/config/initializers/application_controller_renderer.rb".freeze, "test/dummy/config/initializers/assets.rb".freeze, "test/dummy/config/initializers/backtrace_silencers.rb".freeze, "test/dummy/config/initializers/cookies_serializer.rb".freeze, "test/dummy/config/initializers/filter_parameter_logging.rb".freeze, "test/dummy/config/initializers/inflections.rb".freeze, "test/dummy/config/initializers/mime_types.rb".freeze, "test/dummy/config/initializers/rails_ops.rb".freeze, "test/dummy/config/initializers/wrap_parameters.rb".freeze, "test/dummy/config/locales/en.yml".freeze, "test/dummy/config/puma.rb".freeze, "test/dummy/config/routes.rb".freeze, "test/dummy/config/secrets.yml".freeze, "test/dummy/config/spring.rb".freeze, "test/dummy/db/schema.rb".freeze, "test/dummy/lib/assets/.keep".freeze, "test/dummy/log/.keep".freeze, "test/dummy/package.json".freeze, "test/dummy/public/404.html".freeze, "test/dummy/public/422.html".freeze, "test/dummy/public/500.html".freeze, "test/dummy/public/apple-touch-icon-precomposed.png".freeze, "test/dummy/public/apple-touch-icon.png".freeze, "test/dummy/public/favicon.ico".freeze, "test/dummy/tmp/.keep".freeze, "test/test_helper.rb".freeze, "test/unit/rails_ops/generators/operation_generator_test.rb".freeze, "test/unit/rails_ops/mixins/model/nesting.rb".freeze, "test/unit/rails_ops/mixins/param_authorization_test.rb".freeze, "test/unit/rails_ops/mixins/policies_test.rb".freeze, "test/unit/rails_ops/operation/model/create_test.rb".freeze, "test/unit/rails_ops/operation/model/load_test.rb".freeze, "test/unit/rails_ops/operation/model/sti_test.rb".freeze, "test/unit/rails_ops/operation/model/update_test.rb".freeze, "test/unit/rails_ops/operation/model_test.rb".freeze, "test/unit/rails_ops/operation/update_auth_test.rb".freeze, "test/unit/rails_ops/operation/update_lazy_auth_test.rb".freeze, "test/unit/rails_ops/operation_test.rb".freeze]
13
- s.rubygems_version = "3.3.11".freeze
13
+ s.rubygems_version = "3.2.15".freeze
14
14
  s.summary = "An operations service layer for rails projects.".freeze
15
15
  s.test_files = ["test/db/models.rb".freeze, "test/db/schema.rb".freeze, "test/dummy/Rakefile".freeze, "test/dummy/app/assets/config/manifest.js".freeze, "test/dummy/app/assets/images/.keep".freeze, "test/dummy/app/assets/javascripts/application.js".freeze, "test/dummy/app/assets/javascripts/cable.js".freeze, "test/dummy/app/assets/javascripts/channels/.keep".freeze, "test/dummy/app/assets/stylesheets/application.css".freeze, "test/dummy/app/channels/application_cable/channel.rb".freeze, "test/dummy/app/channels/application_cable/connection.rb".freeze, "test/dummy/app/controllers/application_controller.rb".freeze, "test/dummy/app/controllers/concerns/.keep".freeze, "test/dummy/app/helpers/application_helper.rb".freeze, "test/dummy/app/jobs/application_job.rb".freeze, "test/dummy/app/mailers/application_mailer.rb".freeze, "test/dummy/app/models/animal.rb".freeze, "test/dummy/app/models/application_record.rb".freeze, "test/dummy/app/models/bird.rb".freeze, "test/dummy/app/models/cat.rb".freeze, "test/dummy/app/models/concerns/.keep".freeze, "test/dummy/app/models/dog.rb".freeze, "test/dummy/app/models/flower.rb".freeze, "test/dummy/app/models/group.rb".freeze, "test/dummy/app/models/nightingale.rb".freeze, "test/dummy/app/models/phoenix.rb".freeze, "test/dummy/app/models/user.rb".freeze, "test/dummy/app/views/layouts/application.html.erb".freeze, "test/dummy/app/views/layouts/mailer.html.erb".freeze, "test/dummy/app/views/layouts/mailer.text.erb".freeze, "test/dummy/bin/bundle".freeze, "test/dummy/bin/rails".freeze, "test/dummy/bin/rake".freeze, "test/dummy/bin/setup".freeze, "test/dummy/bin/update".freeze, "test/dummy/bin/yarn".freeze, "test/dummy/config.ru".freeze, "test/dummy/config/application.rb".freeze, "test/dummy/config/boot.rb".freeze, "test/dummy/config/cable.yml".freeze, "test/dummy/config/database.yml".freeze, "test/dummy/config/environment.rb".freeze, "test/dummy/config/environments/development.rb".freeze, "test/dummy/config/environments/production.rb".freeze, "test/dummy/config/environments/test.rb".freeze, "test/dummy/config/initializers/application_controller_renderer.rb".freeze, "test/dummy/config/initializers/assets.rb".freeze, "test/dummy/config/initializers/backtrace_silencers.rb".freeze, "test/dummy/config/initializers/cookies_serializer.rb".freeze, "test/dummy/config/initializers/filter_parameter_logging.rb".freeze, "test/dummy/config/initializers/inflections.rb".freeze, "test/dummy/config/initializers/mime_types.rb".freeze, "test/dummy/config/initializers/rails_ops.rb".freeze, "test/dummy/config/initializers/wrap_parameters.rb".freeze, "test/dummy/config/locales/en.yml".freeze, "test/dummy/config/puma.rb".freeze, "test/dummy/config/routes.rb".freeze, "test/dummy/config/secrets.yml".freeze, "test/dummy/config/spring.rb".freeze, "test/dummy/db/schema.rb".freeze, "test/dummy/lib/assets/.keep".freeze, "test/dummy/log/.keep".freeze, "test/dummy/package.json".freeze, "test/dummy/public/404.html".freeze, "test/dummy/public/422.html".freeze, "test/dummy/public/500.html".freeze, "test/dummy/public/apple-touch-icon-precomposed.png".freeze, "test/dummy/public/apple-touch-icon.png".freeze, "test/dummy/public/favicon.ico".freeze, "test/dummy/tmp/.keep".freeze, "test/test_helper.rb".freeze, "test/unit/rails_ops/generators/operation_generator_test.rb".freeze, "test/unit/rails_ops/mixins/model/nesting.rb".freeze, "test/unit/rails_ops/mixins/param_authorization_test.rb".freeze, "test/unit/rails_ops/mixins/policies_test.rb".freeze, "test/unit/rails_ops/operation/model/create_test.rb".freeze, "test/unit/rails_ops/operation/model/load_test.rb".freeze, "test/unit/rails_ops/operation/model/sti_test.rb".freeze, "test/unit/rails_ops/operation/model/update_test.rb".freeze, "test/unit/rails_ops/operation/model_test.rb".freeze, "test/unit/rails_ops/operation/update_auth_test.rb".freeze, "test/unit/rails_ops/operation/update_lazy_auth_test.rb".freeze, "test/unit/rails_ops/operation_test.rb".freeze]
16
16
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_ops
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.3
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sitrox
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-04 00:00:00.000000000 Z
11
+ date: 2023-01-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: appraisal
@@ -212,8 +212,8 @@ dependencies:
212
212
  - - "<="
213
213
  - !ruby/object:Gem::Version
214
214
  version: '3.1'
215
- description:
216
- email:
215
+ description:
216
+ email:
217
217
  executables: []
218
218
  extensions: []
219
219
  extra_rdoc_files: []
@@ -370,10 +370,10 @@ files:
370
370
  - test/unit/rails_ops/operation/update_auth_test.rb
371
371
  - test/unit/rails_ops/operation/update_lazy_auth_test.rb
372
372
  - test/unit/rails_ops/operation_test.rb
373
- homepage:
373
+ homepage:
374
374
  licenses: []
375
375
  metadata: {}
376
- post_install_message:
376
+ post_install_message:
377
377
  rdoc_options: []
378
378
  require_paths:
379
379
  - lib
@@ -388,8 +388,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
388
388
  - !ruby/object:Gem::Version
389
389
  version: '0'
390
390
  requirements: []
391
- rubygems_version: 3.3.11
392
- signing_key:
391
+ rubygems_version: 3.2.15
392
+ signing_key:
393
393
  specification_version: 4
394
394
  summary: An operations service layer for rails projects.
395
395
  test_files: