vert-core 1.0.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.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +14 -0
  3. data/README.md +126 -0
  4. data/lib/vert/authorization/controller_methods.rb +84 -0
  5. data/lib/vert/authorization/dynamic_policy.rb +156 -0
  6. data/lib/vert/authorization/permission_resolver.rb +253 -0
  7. data/lib/vert/authorization/policy_finder.rb +72 -0
  8. data/lib/vert/clients/document_service_client.rb +104 -0
  9. data/lib/vert/concerns/auditable.rb +24 -0
  10. data/lib/vert/concerns/company_scoped.rb +48 -0
  11. data/lib/vert/concerns/current_attributes.rb +53 -0
  12. data/lib/vert/concerns/document_storeable.rb +180 -0
  13. data/lib/vert/concerns/multi_tenant.rb +45 -0
  14. data/lib/vert/concerns/soft_deletable.rb +46 -0
  15. data/lib/vert/concerns/uuid_primary_key.rb +42 -0
  16. data/lib/vert/configuration.rb +65 -0
  17. data/lib/vert/generators/install_generator.rb +66 -0
  18. data/lib/vert/generators/rls_migration_generator.rb +57 -0
  19. data/lib/vert/generators/templates/application_record.rb.tt +8 -0
  20. data/lib/vert/generators/templates/create_outbox_events.rb.tt +24 -0
  21. data/lib/vert/generators/templates/create_rls_functions.rb.tt +27 -0
  22. data/lib/vert/generators/templates/current.rb.tt +10 -0
  23. data/lib/vert/generators/templates/enable_rls_on_tables.rb.tt +39 -0
  24. data/lib/vert/generators/templates/health_controller.rb.tt +5 -0
  25. data/lib/vert/generators/templates/initializer.rb.tt +39 -0
  26. data/lib/vert/generators/templates/outbox_event.rb.tt +11 -0
  27. data/lib/vert/health/checker.rb +119 -0
  28. data/lib/vert/health/routes.rb +44 -0
  29. data/lib/vert/outbox/event.rb +68 -0
  30. data/lib/vert/outbox/publisher.rb +105 -0
  31. data/lib/vert/outbox/publisher_job.rb +30 -0
  32. data/lib/vert/railtie.rb +54 -0
  33. data/lib/vert/rls/connection_handler.rb +56 -0
  34. data/lib/vert/rls/consumer_context.rb +31 -0
  35. data/lib/vert/rls/context_middleware.rb +37 -0
  36. data/lib/vert/rls/job_context.rb +56 -0
  37. data/lib/vert/version.rb +5 -0
  38. data/lib/vert.rb +58 -0
  39. data/vert.gemspec +43 -0
  40. metadata +223 -0
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Vert
4
+ class Railtie < Rails::Railtie
5
+ config.vert = ActiveSupport::OrderedOptions.new
6
+
7
+ initializer "vert.middleware" do |app|
8
+ if Vert.config.enable_rls
9
+ app.middleware.use Vert::Rls::ContextMiddleware
10
+ end
11
+ end
12
+
13
+ initializer "vert.sidekiq" do
14
+ if defined?(Sidekiq) && Vert.config.enable_rls
15
+ Sidekiq.configure_server do |config|
16
+ config.server_middleware do |chain|
17
+ chain.add Vert::Rls::JobMiddleware
18
+ end
19
+ end
20
+ Sidekiq.configure_client do |config|
21
+ config.client_middleware do |chain|
22
+ chain.add Vert::Rls::JobClientMiddleware
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ config.to_prepare do
29
+ if Vert.config.enable_rls && defined?(ApplicationController)
30
+ ApplicationController.include(Vert::Rls::ControllerContext)
31
+ end
32
+ end
33
+
34
+ initializer "vert.routes" do |app|
35
+ next unless Vert.config.enable_health && Vert.config.auto_mount_health_routes
36
+ next if app.routes.named_routes.key?(:health)
37
+
38
+ app.routes.append do
39
+ Vert::Health::Routes.mount(self)
40
+ end
41
+ end
42
+
43
+ generators do
44
+ require_relative "generators/install_generator"
45
+ require_relative "generators/rls_migration_generator"
46
+ end
47
+
48
+ console do
49
+ puts "Vert v#{Vert::VERSION} loaded"
50
+ puts " Config: Vert.config"
51
+ puts " Health: Vert::Health.check_all (when enable_health)"
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Vert
4
+ module Rls
5
+ class ConnectionHandler
6
+ class << self
7
+ def set_context(tenant_id:, company_id: nil, user_id: nil)
8
+ return unless tenant_id.present?
9
+ connection = ActiveRecord::Base.connection
10
+ connection.execute(ActiveRecord::Base.sanitize_sql(["SET LOCAL app.current_tenant_id = %s", tenant_id]))
11
+ connection.execute(ActiveRecord::Base.sanitize_sql(["SET LOCAL app.current_company_id = %s", company_id])) if company_id.present?
12
+ connection.execute(ActiveRecord::Base.sanitize_sql(["SET LOCAL app.current_user_id = %s", user_id])) if user_id.present?
13
+ Vert::Current.rls_configured = true
14
+ rescue StandardError => e
15
+ Rails.logger.error("[Vert::RLS] #{e.message}") if defined?(Rails)
16
+ raise
17
+ end
18
+
19
+ def reset_context
20
+ return unless Vert::Current.rls_configured
21
+ connection = ActiveRecord::Base.connection
22
+ connection.execute("RESET app.current_tenant_id")
23
+ connection.execute("RESET app.current_company_id")
24
+ connection.execute("RESET app.current_user_id")
25
+ Vert::Current.rls_configured = false
26
+ rescue StandardError => e
27
+ Rails.logger.error("[Vert::RLS] #{e.message}") if defined?(Rails)
28
+ end
29
+
30
+ def with_context(tenant_id:, company_id: nil, user_id: nil)
31
+ previous = Vert::Current.serialize
32
+ set_context(tenant_id: tenant_id, company_id: company_id, user_id: user_id)
33
+ Vert::Current.set_context(tenant_id: tenant_id, company_id: company_id, user_id: user_id)
34
+ yield
35
+ ensure
36
+ reset_context
37
+ Vert::Current.deserialize(previous)
38
+ end
39
+
40
+ def create_functions_sql
41
+ <<~SQL
42
+ CREATE OR REPLACE FUNCTION current_tenant_id() RETURNS uuid AS $$
43
+ SELECT NULLIF(current_setting('app.current_tenant_id', true), '')::uuid;
44
+ $$ LANGUAGE SQL STABLE;
45
+ CREATE OR REPLACE FUNCTION current_company_id() RETURNS uuid AS $$
46
+ SELECT NULLIF(current_setting('app.current_company_id', true), '')::uuid;
47
+ $$ LANGUAGE SQL STABLE;
48
+ CREATE OR REPLACE FUNCTION current_user_id() RETURNS uuid AS $$
49
+ SELECT NULLIF(current_setting('app.current_user_id', true), '')::uuid;
50
+ $$ LANGUAGE SQL STABLE;
51
+ SQL
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Vert
4
+ module Rls
5
+ module ConsumerContext
6
+ extend ActiveSupport::Concern
7
+
8
+ def work_with_params(message, delivery_info, metadata)
9
+ h = metadata[:headers] || {}
10
+ Vert::Current.set_context(
11
+ tenant_id: h["tenant_id"] || h[:tenant_id],
12
+ user_id: h["user_id"] || h[:user_id],
13
+ company_id: h["company_id"] || h[:company_id],
14
+ request_id: h["request_id"] || h[:request_id] || SecureRandom.uuid
15
+ )
16
+ if Vert.config.enable_rls && Vert::Current.tenant_id.present?
17
+ ConnectionHandler.set_context(tenant_id: Vert::Current.tenant_id, company_id: Vert::Current.company_id, user_id: Vert::Current.user_id)
18
+ end
19
+ super
20
+ ensure
21
+ Vert::Current.reset_all
22
+ ConnectionHandler.reset_context if Vert.config.enable_rls
23
+ end
24
+ end
25
+
26
+ class BaseConsumer
27
+ include Sneakers::Worker if defined?(Sneakers::Worker)
28
+ include ConsumerContext if defined?(Sneakers::Worker)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Vert
4
+ module Rls
5
+ class ContextMiddleware
6
+ def initialize(app)
7
+ @app = app
8
+ end
9
+
10
+ def call(env)
11
+ response = @app.call(env)
12
+ response
13
+ ensure
14
+ Vert::Rls::ConnectionHandler.reset_context if Vert.config.enable_rls
15
+ end
16
+ end
17
+
18
+ module ControllerContext
19
+ extend ActiveSupport::Concern
20
+
21
+ included do
22
+ after_action :configure_rls_context, if: -> { Vert.config.enable_rls }
23
+ end
24
+
25
+ private
26
+
27
+ def configure_rls_context
28
+ return unless Vert::Current.tenant_id.present?
29
+ Vert::Rls::ConnectionHandler.set_context(
30
+ tenant_id: Vert::Current.tenant_id,
31
+ company_id: Vert::Current.company_id,
32
+ user_id: Vert::Current.user_id
33
+ )
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Vert
4
+ module Rls
5
+ module JobContext
6
+ extend ActiveSupport::Concern
7
+
8
+ class_methods do
9
+ def perform_async(*args)
10
+ super(*args, __vert_context__: Vert::Current.serialize)
11
+ end
12
+
13
+ def perform_in(interval, *args)
14
+ super(interval, *args, __vert_context__: Vert::Current.serialize)
15
+ end
16
+
17
+ def perform_at(timestamp, *args)
18
+ super(timestamp, *args, __vert_context__: Vert::Current.serialize)
19
+ end
20
+ end
21
+
22
+ def perform(*args)
23
+ context = args.last.is_a?(Hash) && (args.last.delete(:__vert_context__) || args.last.delete("__vert_context__"))
24
+ Vert::Current.deserialize(context) if context
25
+ Vert::Rls::ConnectionHandler.set_context(tenant_id: Vert::Current.tenant_id, company_id: Vert::Current.company_id, user_id: Vert::Current.user_id) if Vert.config.enable_rls && Vert::Current.tenant_id.present?
26
+
27
+ clean = args.last.is_a?(Hash) ? (args[0..-2] + [args.last.except(:__vert_context__, "__vert_context__")]).reject { |a| a.respond_to?(:empty?) && a.empty? } : args
28
+ super(*clean)
29
+ ensure
30
+ Vert::Current.reset_all
31
+ Vert::Rls::ConnectionHandler.reset_context if Vert.config.enable_rls
32
+ end
33
+ end
34
+
35
+ class JobMiddleware
36
+ def call(_worker, job, _queue)
37
+ context = job.delete("__vert_context__")
38
+ if context
39
+ Vert::Current.deserialize(context)
40
+ Vert::Rls::ConnectionHandler.set_context(tenant_id: Vert::Current.tenant_id, company_id: Vert::Current.company_id, user_id: Vert::Current.user_id) if Vert.config.enable_rls && Vert::Current.tenant_id.present?
41
+ end
42
+ yield
43
+ ensure
44
+ Vert::Current.reset_all
45
+ Vert::Rls::ConnectionHandler.reset_context if Vert.config.enable_rls
46
+ end
47
+ end
48
+
49
+ class JobClientMiddleware
50
+ def call(_worker_class, job, _queue, _redis_pool)
51
+ job["__vert_context__"] = Vert::Current.serialize
52
+ yield
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Vert
4
+ VERSION = "1.0.0"
5
+ end
data/lib/vert.rb ADDED
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "vert/version"
4
+ require_relative "vert/configuration"
5
+
6
+ # Concerns (sempre carregados; inclua no model conforme necessário)
7
+ require_relative "vert/concerns/current_attributes"
8
+ require_relative "vert/concerns/uuid_primary_key"
9
+ require_relative "vert/concerns/auditable"
10
+ require_relative "vert/concerns/soft_deletable"
11
+ require_relative "vert/concerns/multi_tenant"
12
+ require_relative "vert/concerns/company_scoped"
13
+ require_relative "vert/concerns/document_storeable"
14
+
15
+ # Clients
16
+ require_relative "vert/clients/document_service_client"
17
+
18
+ # Outbox
19
+ require_relative "vert/outbox/event"
20
+ require_relative "vert/outbox/publisher"
21
+ require_relative "vert/outbox/publisher_job"
22
+
23
+ # RLS
24
+ require_relative "vert/rls/context_middleware"
25
+ require_relative "vert/rls/job_context"
26
+ require_relative "vert/rls/consumer_context"
27
+ require_relative "vert/rls/connection_handler"
28
+
29
+ # Health
30
+ require_relative "vert/health/checker"
31
+ require_relative "vert/health/routes"
32
+
33
+ # Authorization
34
+ require_relative "vert/authorization/permission_resolver"
35
+ require_relative "vert/authorization/dynamic_policy"
36
+ require_relative "vert/authorization/policy_finder"
37
+ require_relative "vert/authorization/controller_methods"
38
+
39
+ # Railtie
40
+ require_relative "vert/railtie" if defined?(Rails::Railtie)
41
+
42
+ module Vert
43
+ class Error < StandardError; end
44
+ class TenantNotSetError < Error; end
45
+ class CompanyNotSetError < Error; end
46
+
47
+ class << self
48
+ def configure
49
+ yield(configuration)
50
+ end
51
+
52
+ def configuration
53
+ @configuration ||= Vert::Configuration.new
54
+ end
55
+
56
+ alias config configuration
57
+ end
58
+ end
data/vert.gemspec ADDED
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/vert/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "vert-core"
7
+ spec.version = Vert::VERSION
8
+ spec.authors = ["Vert Team"]
9
+ spec.email = ["dev@vert.dev"]
10
+
11
+ spec.summary = "Generic core library for Rails microservices"
12
+ spec.description = "Optional patterns for Rails apps: multi-tenancy, RLS, outbox, health checks, auditing, soft delete, UUID primary keys, and document storage client. All features are configurable via initializer."
13
+ spec.homepage = "https://github.com/edinaldo-novti/vert"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 3.2.0"
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = spec.homepage
19
+ spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
20
+
21
+ spec.files = Dir.chdir(__dir__) do
22
+ Dir.glob("**/*").reject do |f|
23
+ File.directory?(f) ||
24
+ (File.expand_path(f) == __FILE__) ||
25
+ f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile])
26
+ end
27
+ end
28
+ spec.bindir = "exe"
29
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ["lib"]
31
+
32
+ spec.add_dependency "activesupport", ">= 7.0"
33
+ spec.add_dependency "activerecord", ">= 7.0"
34
+ spec.add_dependency "discard", "~> 1.3"
35
+ spec.add_dependency "bunny", "~> 2.22"
36
+ spec.add_dependency "sidekiq", ">= 7.0"
37
+
38
+ spec.add_development_dependency "rake", "~> 13.0"
39
+ spec.add_development_dependency "rspec", "~> 3.0"
40
+ spec.add_development_dependency "rubocop", "~> 1.21"
41
+ spec.add_development_dependency "rubocop-rails", "~> 2.0"
42
+ spec.add_development_dependency "rubocop-rspec", "~> 2.0"
43
+ end
metadata ADDED
@@ -0,0 +1,223 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vert-core
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Vert Team
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: activesupport
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '7.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '7.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: activerecord
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '7.0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '7.0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: discard
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.3'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1.3'
54
+ - !ruby/object:Gem::Dependency
55
+ name: bunny
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '2.22'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '2.22'
68
+ - !ruby/object:Gem::Dependency
69
+ name: sidekiq
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '7.0'
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '7.0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: rake
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '13.0'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '13.0'
96
+ - !ruby/object:Gem::Dependency
97
+ name: rspec
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '3.0'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '3.0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: rubocop
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '1.21'
117
+ type: :development
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '1.21'
124
+ - !ruby/object:Gem::Dependency
125
+ name: rubocop-rails
126
+ requirement: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: '2.0'
131
+ type: :development
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: '2.0'
138
+ - !ruby/object:Gem::Dependency
139
+ name: rubocop-rspec
140
+ requirement: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - "~>"
143
+ - !ruby/object:Gem::Version
144
+ version: '2.0'
145
+ type: :development
146
+ prerelease: false
147
+ version_requirements: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - "~>"
150
+ - !ruby/object:Gem::Version
151
+ version: '2.0'
152
+ description: 'Optional patterns for Rails apps: multi-tenancy, RLS, outbox, health
153
+ checks, auditing, soft delete, UUID primary keys, and document storage client. All
154
+ features are configurable via initializer.'
155
+ email:
156
+ - dev@vert.dev
157
+ executables: []
158
+ extensions: []
159
+ extra_rdoc_files: []
160
+ files:
161
+ - CHANGELOG.md
162
+ - README.md
163
+ - lib/vert.rb
164
+ - lib/vert/authorization/controller_methods.rb
165
+ - lib/vert/authorization/dynamic_policy.rb
166
+ - lib/vert/authorization/permission_resolver.rb
167
+ - lib/vert/authorization/policy_finder.rb
168
+ - lib/vert/clients/document_service_client.rb
169
+ - lib/vert/concerns/auditable.rb
170
+ - lib/vert/concerns/company_scoped.rb
171
+ - lib/vert/concerns/current_attributes.rb
172
+ - lib/vert/concerns/document_storeable.rb
173
+ - lib/vert/concerns/multi_tenant.rb
174
+ - lib/vert/concerns/soft_deletable.rb
175
+ - lib/vert/concerns/uuid_primary_key.rb
176
+ - lib/vert/configuration.rb
177
+ - lib/vert/generators/install_generator.rb
178
+ - lib/vert/generators/rls_migration_generator.rb
179
+ - lib/vert/generators/templates/application_record.rb.tt
180
+ - lib/vert/generators/templates/create_outbox_events.rb.tt
181
+ - lib/vert/generators/templates/create_rls_functions.rb.tt
182
+ - lib/vert/generators/templates/current.rb.tt
183
+ - lib/vert/generators/templates/enable_rls_on_tables.rb.tt
184
+ - lib/vert/generators/templates/health_controller.rb.tt
185
+ - lib/vert/generators/templates/initializer.rb.tt
186
+ - lib/vert/generators/templates/outbox_event.rb.tt
187
+ - lib/vert/health/checker.rb
188
+ - lib/vert/health/routes.rb
189
+ - lib/vert/outbox/event.rb
190
+ - lib/vert/outbox/publisher.rb
191
+ - lib/vert/outbox/publisher_job.rb
192
+ - lib/vert/railtie.rb
193
+ - lib/vert/rls/connection_handler.rb
194
+ - lib/vert/rls/consumer_context.rb
195
+ - lib/vert/rls/context_middleware.rb
196
+ - lib/vert/rls/job_context.rb
197
+ - lib/vert/version.rb
198
+ - vert.gemspec
199
+ homepage: https://github.com/edinaldo-novti/vert
200
+ licenses:
201
+ - MIT
202
+ metadata:
203
+ homepage_uri: https://github.com/edinaldo-novti/vert
204
+ source_code_uri: https://github.com/edinaldo-novti/vert
205
+ changelog_uri: https://github.com/edinaldo-novti/vert/blob/main/CHANGELOG.md
206
+ rdoc_options: []
207
+ require_paths:
208
+ - lib
209
+ required_ruby_version: !ruby/object:Gem::Requirement
210
+ requirements:
211
+ - - ">="
212
+ - !ruby/object:Gem::Version
213
+ version: 3.2.0
214
+ required_rubygems_version: !ruby/object:Gem::Requirement
215
+ requirements:
216
+ - - ">="
217
+ - !ruby/object:Gem::Version
218
+ version: '0'
219
+ requirements: []
220
+ rubygems_version: 3.6.9
221
+ specification_version: 4
222
+ summary: Generic core library for Rails microservices
223
+ test_files: []