gruf 2.14.1 → 2.16.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: a94efb294635ae84890e1f131927003b8cacb0306947109c96fd002e02c87fee
4
- data.tar.gz: 11983bf914472178ed2935d7ee0faf7c60d4fd2204fc6b7b3b0985449c7b5c37
3
+ metadata.gz: 3d83766bdf6739e3997891fd7b5e5ecd393aa17c8fab0ab839ff0789756726cf
4
+ data.tar.gz: b959a1644c29c5732438a60a079dc318136146eddb96d4060091f0f39fc7e5c6
5
5
  SHA512:
6
- metadata.gz: bf230312d6315af4534e4bb92bbd7405a9850c1ec415895e7cb25f382239f887b8fadf0940207459d237c81c3fe2da0647244c3b1100f73a441c77e118a9bea6
7
- data.tar.gz: e505ca2046aa82164be4656aca0b9ad7f696c070abd5c7ea52f8d6afd6e803feaaef095cf96825a2b8dc8b186f4015b8430f68f0c1f9ce472d2764976d596a4f
6
+ metadata.gz: ef7b011e2d693c2cb05b10697e0b67fcedb1d72ffc8adeac5f207557a30494860878a5b80e94fc8d512e7a4e5e6cb69b2bc8a854e72cd80f2e1507e0c1f379b6
7
+ data.tar.gz: a75845718660f4013015eff266c737315ce7bb00e147c83bc175390c87d890be525ee88059f5bb3ec224bbe30a96fb348143dfccd0c377a2dee8bd3eb674a6ea
data/CHANGELOG.md CHANGED
@@ -2,6 +2,29 @@ Changelog for the gruf gem. This includes internal history before the gem was ma
2
2
 
3
3
  ### Pending release
4
4
 
5
+ - Add opt-in ability to serve the official [gRPC health check](https://github.com/grpc/grpc/blob/master/src/ruby/pb/grpc/health/v1/health_services_pb.rb)
6
+ automatically via `health_check_enabled` configuration option (or `GRUF_HEALTH_CHECK_ENABLED` environment
7
+ variable).
8
+ - Add `health_check_hook` configuration option to implement a custom response for the above gRPC built-in health check
9
+ - [#156] Allow passing a specific list of services to run via the gruf binstub
10
+ - [#163] Add `context` hash attribute to `Gruf::Controllers::Request` to allow interceptors to pass information down
11
+ to a gruf controller
12
+ - Drop Ruby 2.6 support (EOL'ed on March 31st, 2022)
13
+
14
+ ### 2.15.1
15
+
16
+ - Fix issue where GRPC_SERVER_POOL_KEEP_ALIVE and GRPC_SERVER_POLL_PERIOD when set via ENV are not cast to int
17
+
18
+ ### 2.15.0
19
+
20
+ - NOTE: This changes the way that gruf controllers are autoloaded. See [UPGRADING.md] for more details.
21
+ - Autoload Gruf Controllers with zeitwerk, allowing for code reloading in development environments
22
+ - Add `GRUF_CONTROLLERS_PATH` ENV to allow ENV-based runtime configuration of path to gruf controller files
23
+ - Move `ServiceBinder` from instance to static class for performance improvements
24
+ - Use zeitwerk for autoloading
25
+ - Update gem metadata
26
+ - Update `Gruf:Configuration#environment` to use `ENV.fetch`
27
+
5
28
  ### 2.14.1
6
29
 
7
30
  - Fix issue where the server object hits thread contention in certain race conditions
@@ -13,9 +36,9 @@ Changelog for the gruf gem. This includes internal history before the gem was ma
13
36
 
14
37
  ### 2.13.1
15
38
 
16
- - Fix issue with race condition in server starts where servers may fail to bind connections and never reach
39
+ - Fix issue with race condition in server starts where servers may fail to bind connections and never reach
17
40
  serving state (fixes #147)
18
-
41
+
19
42
  ### 2.13.0
20
43
 
21
44
  - Remove server mutex handling in deference to core grpc signal handling
@@ -27,7 +50,7 @@ Changelog for the gruf gem. This includes internal history before the gem was ma
27
50
 
28
51
  ### 2.11.0
29
52
 
30
- - Restrict grpc gem to <= 1.41.0 due to regressions in grpc 1.42.x
53
+ - Restrict grpc gem to <= 1.41.0 due to regressions in grpc 1.42.x
31
54
  - Fallback to stdout logger at INFO if no logger is setup
32
55
  - Better handling of namespace collisions with Rails
33
56
  - Add `GRPC_SERVER_HOST` and `GRPC_SERVER_PORT` for ENV configuration of the server host+port
@@ -67,7 +90,7 @@ Changelog for the gruf gem. This includes internal history before the gem was ma
67
90
 
68
91
  ### 2.8.0
69
92
 
70
- - Pass the controller request object into the request logging formatters [#92]
93
+ - Pass the controller request object into the request logging formatters [#92]
71
94
 
72
95
  ### 2.7.1
73
96
 
@@ -80,12 +103,12 @@ Changelog for the gruf gem. This includes internal history before the gem was ma
80
103
  ### 2.6.1
81
104
 
82
105
  - Add frozen_string_literal: true to files, update rubocop to 0.68
83
-
106
+
84
107
  ### 2.6.0
85
108
 
86
109
  - Drop Ruby 2.2 support
87
- - Abstract gruf controller's send to make it usable in filters
88
- - Adjusts configuration reset into a Railtie for Rails systems to ensure proper OOE
110
+ - Abstract gruf controller's send to make it usable in filters
111
+ - Adjusts configuration reset into a Railtie for Rails systems to ensure proper OOE
89
112
  - Bump rubocop to 0.64, address violations, update activesupport/concurrent-ruby dependencies to have a min version
90
113
 
91
114
  ### 2.5.2
data/gruf.gemspec CHANGED
@@ -15,8 +15,7 @@
15
15
  # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16
16
  # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17
17
  #
18
- $LOAD_PATH.push File.expand_path('lib', __dir__)
19
- require 'gruf/version'
18
+ require_relative 'lib/gruf/version'
20
19
 
21
20
  Gem::Specification.new do |spec|
22
21
  spec.name = 'gruf'
@@ -33,17 +32,19 @@ Gem::Specification.new do |spec|
33
32
  spec.executables << 'gruf'
34
33
  spec.require_paths = ['lib']
35
34
 
36
- spec.required_ruby_version = '>= 2.6', '< 3.2'
35
+ spec.required_ruby_version = '>= 2.7', '< 3.2'
37
36
 
38
- spec.metadata['rubygems_mfa_required'] = 'true'
37
+ spec.metadata = {
38
+ 'bug_tracker_uri' => 'https://github.com/bigcommerce/gruf/issues',
39
+ 'changelog_uri' => 'https://github.com/bigcommerce/gruf/CHANGELOG.md',
40
+ 'homepage_uri' => 'https://github.com/bigcommerce/gruf',
41
+ 'rubygems_mfa_required' => 'true',
42
+ 'source_code_uri' => 'https://github.com/bigcommerce/gruf',
43
+ 'wiki_uri' => 'https://github.com/bigcommerce/gruf/wiki'
44
+ }
39
45
 
40
46
  spec.add_development_dependency 'bundler-audit', '>= 0.6'
41
- # rubocop:disable Gemspec/RubyVersionGlobalsUsage
42
- spec.add_development_dependency(
43
- 'factory_bot',
44
- (Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.5') ? '>= 6.1' : '~> 5.2')
45
- )
46
- # rubocop:enable Gemspec/RubyVersionGlobalsUsage
47
+ spec.add_development_dependency 'factory_bot', '>= 6.1'
47
48
  spec.add_development_dependency 'ffaker', '>= 2.15'
48
49
  spec.add_development_dependency 'pry', '~> 0.12'
49
50
  spec.add_development_dependency 'pry-byebug', '>= 3.9'
@@ -64,4 +65,5 @@ Gem::Specification.new do |spec|
64
65
  spec.add_runtime_dependency 'json', '>= 2.3'
65
66
  spec.add_runtime_dependency 'slop', '>= 4.6'
66
67
  spec.add_runtime_dependency 'thwait', '>= 0.1'
68
+ spec.add_runtime_dependency 'zeitwerk', '>= 2'
67
69
  end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2022-present, BigCommerce Pty. Ltd. All rights reserved
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
6
+ # documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
7
+ # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
8
+ # persons to whom the Software is furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
11
+ # Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
14
+ # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
15
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16
+ # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17
+ #
18
+ module Gruf
19
+ ##
20
+ # Module for accessing Gruf zeitwerk-based autoloaders
21
+ #
22
+ module Autoloaders
23
+ class << self
24
+ include Enumerable
25
+
26
+ ##
27
+ # Initialize the autoloaders with a given controllers path
28
+ #
29
+ # @param [String] controllers_path The path to Gruf Controllers
30
+ #
31
+ def load!(controllers_path:)
32
+ controllers(controllers_path: controllers_path)
33
+ end
34
+
35
+ ##
36
+ # Enumerate across the managed set of autoloaders
37
+ #
38
+ def each
39
+ yield controllers
40
+ end
41
+
42
+ ##
43
+ # Reload all files managed by the autoloader
44
+ #
45
+ def reload
46
+ each(&:reload)
47
+ end
48
+
49
+ ##
50
+ # Lazily instantiate and memoize the Gruf Controllers autoloader in a thread-safe manner
51
+ #
52
+ # @return [::Gruf::Controllers::Autoloader]
53
+ #
54
+ # rubocop:disable ThreadSafety/InstanceVariableInClassMethod
55
+ def controllers(controllers_path: nil)
56
+ controllers_mutex do
57
+ @controllers ||= ::Gruf::Controllers::Autoloader.new(path: controllers_path || ::Gruf.controllers_path)
58
+ end
59
+ end
60
+ # rubocop:enable ThreadSafety/InstanceVariableInClassMethod
61
+
62
+ ##
63
+ # Handle mutations to the controllers autoloader in a thread-safe manner
64
+ #
65
+ # rubocop:disable ThreadSafety/InstanceVariableInClassMethod
66
+ def controllers_mutex(&block)
67
+ @controllers_mutex ||= begin
68
+ require 'monitor'
69
+ Monitor.new
70
+ end
71
+ @controllers_mutex.synchronize(&block)
72
+ end
73
+ # rubocop:enable ThreadSafety/InstanceVariableInClassMethod
74
+ end
75
+ end
76
+ end
@@ -23,8 +23,14 @@ module Gruf
23
23
  # Handles execution of the gruf binstub, along with command-line arguments
24
24
  #
25
25
  class Executor
26
+ class NoServicesBoundError < StandardError; end
27
+
26
28
  ##
27
29
  # @param [Hash|ARGV]
30
+ # @param [::Gruf::Server|NilClass] server
31
+ # @param [Array<Class>|NilClass] services
32
+ # @param [Gruf::Hooks::Executor|NilClass] hook_executor
33
+ # @param [Logger|NilClass] logger
28
34
  #
29
35
  def initialize(
30
36
  args = ARGV,
@@ -35,7 +41,7 @@ module Gruf
35
41
  )
36
42
  @args = args
37
43
  setup! # ensure we set some defaults from CLI here so we can allow configuration
38
- @services = services || Gruf.services
44
+ @services = services.is_a?(Array) ? services : []
39
45
  @hook_executor = hook_executor || Gruf::Hooks::Executor.new(hooks: Gruf.hooks&.prepare)
40
46
  @server = server || Gruf::Server.new(Gruf.server_options)
41
47
  @logger = logger || Gruf.logger || ::Logger.new($stderr)
@@ -46,8 +52,11 @@ module Gruf
46
52
  #
47
53
  def run
48
54
  exception = nil
55
+
56
+ # allow lazy registering globally as late as possible, this allows more flexible binstub injections
57
+ register_services!
58
+
49
59
  begin
50
- @services.each { |s| @server.add_service(s) }
51
60
  @hook_executor.call(:before_server_start, server: @server)
52
61
  @server.start!
53
62
  rescue StandardError => e
@@ -67,14 +76,15 @@ module Gruf
67
76
  # Setup options for CLI execution and configure Gruf based on inputs
68
77
  #
69
78
  def setup!
70
- opts = parse_options
79
+ @options = parse_options
71
80
 
72
- Gruf.server_binding_url = opts[:host] if opts[:host]
73
- if opts.suppress_default_interceptors?
81
+ Gruf.server_binding_url = @options[:host] if @options[:host]
82
+ if @options.suppress_default_interceptors?
74
83
  Gruf.interceptors.remove(Gruf::Interceptors::ActiveRecord::ConnectionReset)
75
84
  Gruf.interceptors.remove(Gruf::Interceptors::Instrumentation::OutputMetadataTimer)
76
85
  end
77
- Gruf.backtrace_on_error = true if opts.backtrace_on_error?
86
+ Gruf.backtrace_on_error = true if @options.backtrace_on_error?
87
+ Gruf.health_check_enabled = true if @options.health_check?
78
88
  end
79
89
 
80
90
  ##
@@ -89,6 +99,8 @@ module Gruf
89
99
  exit(0)
90
100
  end
91
101
  o.string '--host', 'Specify the binding url for the gRPC service'
102
+ o.string '--services', 'Optional. Run gruf with only the passed gRPC service classes (comma-separated)'
103
+ o.bool '--health-check', 'Serve the default gRPC health check (defaults to false). '
92
104
  o.bool '--suppress-default-interceptors', 'Do not use the default interceptors'
93
105
  o.bool '--backtrace-on-error', 'Push backtraces on exceptions to the error serializer'
94
106
  o.null '-v', '--version', 'print gruf version' do
@@ -97,6 +109,85 @@ module Gruf
97
109
  end
98
110
  end
99
111
  end
112
+
113
+ ##
114
+ # Register services; note that this happens after gruf is initialized, and right before the server is run.
115
+ # This will interpret the services to run in the following precedence:
116
+ # 1. initializer arguments to the executor
117
+ # 2. ARGV options (the --services option)
118
+ # 3. services set to the global gruf configuration (Gruf.services)
119
+ #
120
+ def register_services!
121
+ # wait to load controllers until last possible second to allow late configuration
122
+ ::Gruf.autoloaders.load!(controllers_path: Gruf.controllers_path)
123
+
124
+ services = determine_services(@services)
125
+ services = bind_health_check!(services) if health_check_enabled?
126
+
127
+ services.map! { |s| s.is_a?(Class) ? s : s.constantize }
128
+
129
+ if services.any?
130
+ services.each { |s| @server.add_service(s) }
131
+ return
132
+ end
133
+
134
+ raise NoServicesBoundError
135
+ rescue NoServicesBoundError
136
+ @logger.fatal 'FATAL ERROR: No services bound to this gruf process; please bind a service to a Gruf ' \
137
+ 'controller to start the server successfully'
138
+ exit(1)
139
+ rescue NameError => e
140
+ @logger.fatal 'FATAL ERROR: Could not start server; passed services to run are not loaded or valid ' \
141
+ "constants: #{e.message}"
142
+ exit(1)
143
+ rescue StandardError => e
144
+ @logger.fatal "FATAL ERROR: Could not start server: #{e.message}"
145
+ exit(1)
146
+ end
147
+
148
+ ##
149
+ # @return [Boolean]
150
+ #
151
+ def health_check_enabled?
152
+ ::Gruf.health_check_enabled
153
+ end
154
+
155
+ ##
156
+ # Load the health check if enabled into the services array
157
+ #
158
+ # @param [Array<Class>] services
159
+ # @return [Array<Class>]
160
+ #
161
+ def bind_health_check!(services)
162
+ # do this here to trigger autoloading the controller in zeitwerk, since we don't explicitly load this
163
+ # controller. This binds the service and makes sure the method handlers are setup.
164
+ # rubocop:disable Lint/Void
165
+ Gruf::Controllers::HealthController
166
+ # rubocop:enable Lint/Void
167
+ # if we're already bound to the services array (say someone explicitly passes the health check in, skip)
168
+ return services if services.include?(::Grpc::Health::V1::Health::Service)
169
+
170
+ # otherwise, manually add the grpc service
171
+ services << ::Grpc::Health::V1::Health::Service
172
+ services
173
+ end
174
+
175
+ ##
176
+ # Determine how we load services (initializer -> ARGV -> Gruf.services)
177
+ #
178
+ # @return [Array<Class>]
179
+ #
180
+ def determine_services(services = [])
181
+ # first check initializer arguments
182
+ return services if services.any?
183
+
184
+ # next check CLI arguments
185
+ services = @options[:services].to_s.split(',').map(&:strip).uniq
186
+ # finally, if none, use global gruf autoloaded services
187
+ services = (::Gruf.services || []) unless services.any?
188
+
189
+ services
190
+ end
100
191
  end
101
192
  end
102
193
  end
@@ -37,33 +37,5 @@ module Gruf
37
37
  super
38
38
  end
39
39
  end
40
-
41
- ##
42
- # See https://github.com/grpc/grpc-go/blob/master/codes/codes.go for a detailed summary of each error type
43
- #
44
- module Errors
45
- class Base < Gruf::Client::Error; end
46
- class Error < Base; end
47
- class Validation < Base; end
48
-
49
- class Ok < Base; end
50
-
51
- class InvalidArgument < Validation; end
52
- class NotFound < Validation; end
53
- class AlreadyExists < Validation; end
54
- class OutOfRange < Validation; end
55
-
56
- class Cancelled < Error; end
57
- class DataLoss < Error; end
58
- class DeadlineExceeded < Error; end
59
- class FailedPrecondition < Error; end
60
- class Internal < Error; end
61
- class PermissionDenied < Error; end
62
- class ResourceExhausted < Error; end
63
- class Unauthenticated < Error; end
64
- class Unavailable < Error; end
65
- class Unimplemented < Error; end
66
- class Unknown < Error; end
67
- end
68
40
  end
69
41
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
6
+ # documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
7
+ # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
8
+ # persons to whom the Software is furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
11
+ # Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
14
+ # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
15
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16
+ # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17
+ #
18
+ module Gruf
19
+ class Client < SimpleDelegator
20
+ ##
21
+ # See https://github.com/grpc/grpc-go/blob/master/codes/codes.go for a detailed summary of each error type
22
+ #
23
+ module Errors
24
+ class Base < Gruf::Client::Error; end
25
+ class Error < Base; end
26
+ class Validation < Base; end
27
+
28
+ class Ok < Base; end
29
+
30
+ class InvalidArgument < Validation; end
31
+ class NotFound < Validation; end
32
+ class AlreadyExists < Validation; end
33
+ class OutOfRange < Validation; end
34
+
35
+ class Cancelled < Error; end
36
+ class DataLoss < Error; end
37
+ class DeadlineExceeded < Error; end
38
+ class FailedPrecondition < Error; end
39
+ class Internal < Error; end
40
+ class PermissionDenied < Error; end
41
+ class ResourceExhausted < Error; end
42
+ class Unauthenticated < Error; end
43
+ class Unavailable < Error; end
44
+ class Unimplemented < Error; end
45
+ class Unknown < Error; end
46
+ end
47
+ end
48
+ end
data/lib/gruf/client.rb CHANGED
@@ -15,9 +15,6 @@
15
15
  # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16
16
  # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17
17
  #
18
- require_relative 'client/error'
19
- require_relative 'client/error_factory'
20
-
21
18
  module Gruf
22
19
  ##
23
20
  # Abstracts out the calling interface for interacting with gRPC clients. Streamlines calling and provides
@@ -77,6 +77,13 @@ module Gruf
77
77
  # @return [Integer] Internal cache expiry period (in seconds) for the SynchronizedClient
78
78
  # @!attribute rpc_server_options
79
79
  # @return [Hash] A hash of RPC options for GRPC server configuration
80
+ # @!attribute health_check_enabled
81
+ # @return [Boolean] If true, will load and register `Gruf::Controllers::HealthController` with the default gRPC
82
+ # health check to the loaded gRPC server
83
+ # @!attribute health_check_hook
84
+ # @return [NilClass]
85
+ # @return [Proc] If set, will call this in the gRPC health check. It is required to return a
86
+ # `::Grpc::Health::V1::HealthCheckResponse` object in this proc to indicate the health of the server.
80
87
  VALID_CONFIG_KEYS = {
81
88
  root_path: '',
82
89
  server_binding_url: '0.0.0.0:9001',
@@ -101,6 +108,8 @@ module Gruf
101
108
  use_exception_message: true,
102
109
  internal_error_message: 'Internal Server Error',
103
110
  event_listener_proc: nil,
111
+ health_check_enabled: false,
112
+ health_check_hook: nil,
104
113
  synchronized_client_internal_cache_expiry: 60,
105
114
  rpc_server_options: {
106
115
  pool_size: GRPC::RpcServer::DEFAULT_POOL_SIZE,
@@ -165,14 +174,15 @@ module Gruf
165
174
  determine_loggers
166
175
  self.ssl_crt_file = "#{root_path}config/ssl/#{environment}.crt"
167
176
  self.ssl_key_file = "#{root_path}config/ssl/#{environment}.key"
168
- self.controllers_path = root_path.to_s.empty? ? 'app/rpc' : "#{root_path}/app/rpc"
177
+ cp = ::ENV.fetch('GRUF_CONTROLLERS_PATH', 'app/rpc').to_s
178
+ self.controllers_path = root_path.to_s.empty? ? cp : "#{root_path}/#{cp}"
169
179
  self.backtrace_on_error = ::ENV.fetch('GRPC_BACKTRACE_ON_ERROR', 0).to_i.positive?
170
180
  self.rpc_server_options = {
171
181
  max_waiting_requests: ::ENV.fetch('GRPC_SERVER_MAX_WAITING_REQUESTS',
172
182
  GRPC::RpcServer::DEFAULT_MAX_WAITING_REQUESTS).to_i,
173
183
  pool_size: ::ENV.fetch('GRPC_SERVER_POOL_SIZE', GRPC::RpcServer::DEFAULT_POOL_SIZE).to_i,
174
- pool_keep_alive: ::ENV.fetch('GRPC_SERVER_POOL_KEEP_ALIVE', GRPC::Pool::DEFAULT_KEEP_ALIVE),
175
- poll_period: ::ENV.fetch('GRPC_SERVER_POLL_PERIOD', GRPC::RpcServer::DEFAULT_POLL_PERIOD),
184
+ pool_keep_alive: ::ENV.fetch('GRPC_SERVER_POOL_KEEP_ALIVE', GRPC::Pool::DEFAULT_KEEP_ALIVE).to_i,
185
+ poll_period: ::ENV.fetch('GRPC_SERVER_POLL_PERIOD', GRPC::RpcServer::DEFAULT_POLL_PERIOD).to_i,
176
186
  connect_md_proc: nil,
177
187
  server_args: {}
178
188
  }
@@ -180,9 +190,17 @@ module Gruf
180
190
  interceptors.use(::Gruf::Interceptors::ActiveRecord::ConnectionReset)
181
191
  interceptors.use(::Gruf::Interceptors::Instrumentation::OutputMetadataTimer)
182
192
  end
193
+ self.health_check_enabled = ::ENV.fetch('GRUF_HEALTH_CHECK_ENABLED', 0).to_i.positive?
183
194
  options
184
195
  end
185
196
 
197
+ ##
198
+ # @return [Boolean]
199
+ #
200
+ def development?
201
+ environment == 'development'
202
+ end
203
+
186
204
  private
187
205
 
188
206
  ##
@@ -194,7 +212,7 @@ module Gruf
194
212
  if defined?(::Rails)
195
213
  ::Rails.env.to_s
196
214
  else
197
- (ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development').to_s
215
+ ENV.fetch('RACK_ENV') { ENV.fetch('RAILS_ENV', 'development') }.to_s
198
216
  end
199
217
  end
200
218
 
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
6
+ # documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
7
+ # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
8
+ # persons to whom the Software is furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
11
+ # Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
14
+ # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
15
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16
+ # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17
+ #
18
+ module Gruf
19
+ module Controllers
20
+ ##
21
+ # Handles autoloading of Gruf controllers in the application path. This allows for code reloading on Gruf
22
+ # controllers.
23
+ #
24
+ class Autoloader
25
+ include ::Gruf::Loggable
26
+
27
+ # @!attribute [r] path
28
+ # @return [String] The path for this autoloader
29
+ attr_reader :path
30
+
31
+ ##
32
+ # @param [String] path
33
+ # @param [Boolean] reloading
34
+ # @param [String] tag
35
+ #
36
+ def initialize(path:, reloading: nil, tag: nil)
37
+ super()
38
+ @path = path
39
+ @loader = ::Zeitwerk::Loader.new
40
+ @loader.tag = tag || 'gruf-controllers'
41
+ @setup = false
42
+ @reloading_enabled = reloading || ::Gruf.development?
43
+ setup!
44
+ end
45
+
46
+ ##
47
+ # Reload all files managed by the autoloader, if reloading is enabled
48
+ #
49
+ def reload
50
+ @loader.reload if @reloading_enabled
51
+ end
52
+
53
+ private
54
+
55
+ ##
56
+ # @return [Boolean]
57
+ #
58
+ def setup!
59
+ return true if @setup
60
+
61
+ return false unless File.directory?(@path)
62
+
63
+ @loader.enable_reloading if @reloading_enabled
64
+ @loader.ignore("#{@path}/**/*_pb.rb")
65
+ @loader.push_dir(@path)
66
+ @loader.setup
67
+ # always eager load RPC files, so that the service binder can bind the Gruf::Controller instantiation
68
+ # to the gRPC Service classes
69
+ @loader.eager_load
70
+ @setup = true
71
+ end
72
+ end
73
+ end
74
+ end
@@ -15,9 +15,6 @@
15
15
  # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16
16
  # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17
17
  #
18
- require_relative 'request'
19
- require_relative 'service_binder'
20
-
21
18
  module Gruf
22
19
  module Controllers
23
20
  ##
@@ -67,11 +64,12 @@ module Gruf
67
64
  #
68
65
  def self.bind(service)
69
66
  service_class = service.name.constantize
70
- Gruf.services << service_class
67
+ ::Gruf.logger.debug "[gruf] Binding #{service_class} to #{name}"
68
+ ::Gruf.services << service_class
71
69
  # rubocop:disable ThreadSafety/InstanceVariableInClassMethod
72
70
  @bound_service = service_class
73
71
  # rubocop:enable ThreadSafety/InstanceVariableInClassMethod
74
- ServiceBinder.new(service_class).bind!(self)
72
+ ServiceBinder.bind!(service: service_class, controller: self)
75
73
  end
76
74
 
77
75
  ##
@@ -92,6 +90,7 @@ module Gruf
92
90
  # @param [block] &block The passed block for executing the method
93
91
  #
94
92
  def call(method_key, &block)
93
+ ::Gruf.autoloaders.reload if ::Gruf.development?
95
94
  Interceptors::Context.new(@interceptors).intercept! do
96
95
  process_action(method_key, &block)
97
96
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
6
+ # documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
7
+ # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
8
+ # persons to whom the Software is furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
11
+ # Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
14
+ # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
15
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16
+ # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17
+ #
18
+ module Gruf
19
+ module Controllers
20
+ ##
21
+ # Dynamic standard grpc health check controller. Can be used as-is, or can use ::Gruf.health_check_hook to
22
+ # provide custom responses.
23
+ #
24
+ class HealthController < Gruf::Controllers::Base
25
+ bind ::Grpc::Health::V1::Health::Service
26
+
27
+ def check
28
+ health_proc = ::Gruf.health_check_hook
29
+ return health_proc.call(request, error) if !health_proc.nil? && health_proc.respond_to?(:call)
30
+
31
+ ::Grpc::Health::V1::HealthCheckResponse.new(
32
+ status: ::Grpc::Health::V1::HealthCheckResponse::ServingStatus::SERVING
33
+ )
34
+ end
35
+ end
36
+ end
37
+ end
@@ -36,6 +36,11 @@ module Gruf
36
36
  # @!attribute [r] service
37
37
  # @return [Class] The GRPC service class for this request
38
38
  attr_reader :service
39
+ # @!attribute [r] context
40
+ # @return [::ActiveSupport::HashWithIndifferentAccess] An arbitrary hash of key/value entries that are
41
+ # accessible for interceptors, that can be used to shared information between interceptors and pass down into
42
+ # the controller.
43
+ attr_reader :context
39
44
 
40
45
  delegate :metadata, to: :active_call
41
46
  delegate :messages, :client_streamer?, :server_streamer?, :bidi_streamer?, :request_response?, to: :type
@@ -72,6 +77,7 @@ module Gruf
72
77
  @message = message
73
78
  @rpc_desc = rpc_desc
74
79
  @type = Type.new(rpc_desc)
80
+ @context = ::ActiveSupport::HashWithIndifferentAccess.new
75
81
  end
76
82
 
77
83
  ##
@@ -26,92 +26,79 @@ module Gruf
26
26
  #
27
27
  class BoundDesc < SimpleDelegator; end
28
28
 
29
- ##
30
- # Initialize a service binder instance with the given service
31
- #
32
- # @param [GRPC::GenericService] service The gRPC service stub to bind
33
- #
34
- def initialize(service)
35
- @service = service
36
- end
37
-
38
- ##
39
- # Bind all methods on the service to the passed controller
40
- #
41
- # @param [Class<Gruf::Controllers::Base>] controller
42
- #
43
- def bind!(controller)
44
- rpc_methods.each { |name, desc| bind_method(controller, name, desc) }
45
- end
46
-
47
- private
48
-
49
- ##
50
- # Bind the grpc methods to the service, allowing for server interception and execution control
51
- #
52
- # @param [Gruf::Controllers::Base] controller
53
- # @param [Symbol] method_name
54
- # @param [BoundDesc] desc
55
- #
56
- def bind_method(controller, method_name, desc)
57
- method_key = method_name.to_s.underscore.to_sym
58
- service_ref = @service
29
+ class << self
30
+ ##
31
+ # Bind all methods on the service to the passed controller
32
+ #
33
+ # @param [Class<Gruf::Controllers::Base>] controller
34
+ #
35
+ def bind!(service:, controller:)
36
+ rpc_methods = service.rpc_descs.map { |rd| BoundDesc.new(rd) }
37
+ rpc_methods.each { |name, desc| bind_method(service, controller, name, desc) }
38
+ end
59
39
 
60
- @service.class_eval do
61
- if desc.request_response?
62
- define_method(method_key) do |message, active_call|
63
- c = controller.new(
64
- method_key: method_key,
65
- service: service_ref,
66
- message: message,
67
- active_call: active_call,
68
- rpc_desc: desc
69
- )
70
- c.call(method_key)
71
- end
72
- elsif desc.client_streamer?
73
- define_method(method_key) do |active_call|
74
- c = controller.new(
75
- method_key: method_key,
76
- service: service_ref,
77
- message: proc { |&block| active_call.each_remote_read(&block) },
78
- active_call: active_call,
79
- rpc_desc: desc
80
- )
81
- c.call(method_key)
82
- end
83
- elsif desc.server_streamer?
84
- define_method(method_key) do |message, active_call, &block|
85
- c = controller.new(
86
- method_key: method_key,
87
- service: service_ref,
88
- message: message,
89
- active_call: active_call,
90
- rpc_desc: desc
91
- )
92
- c.call(method_key, &block)
93
- end
94
- else # bidi
95
- define_method(method_key) do |messages, active_call, &block|
96
- c = controller.new(
97
- method_key: method_key,
98
- service: service_ref,
99
- message: messages,
100
- active_call: active_call,
101
- rpc_desc: desc
102
- )
103
- c.call(method_key, &block)
40
+ ##
41
+ # Bind the grpc methods to the service, allowing for server interception and execution control
42
+ #
43
+ # @param [Gruf::Controllers::Base] controller
44
+ # @param [Symbol] method_name
45
+ # @param [BoundDesc] desc
46
+ #
47
+ def bind_method(service_ref, controller, method_name, desc)
48
+ method_key = method_name.to_s.underscore.to_sym
49
+ service_ref.class_eval do
50
+ if desc.request_response?
51
+ define_method(method_key) do |message, active_call|
52
+ controller = controller.name.constantize
53
+ c = controller.new(
54
+ method_key: method_key,
55
+ service: service_ref,
56
+ message: message,
57
+ active_call: active_call,
58
+ rpc_desc: desc
59
+ )
60
+ c.call(method_key)
61
+ end
62
+ elsif desc.client_streamer?
63
+ define_method(method_key) do |active_call|
64
+ controller = controller.name.constantize
65
+ c = controller.new(
66
+ method_key: method_key,
67
+ service: service_ref,
68
+ message: proc { |&block| active_call.each_remote_read(&block) },
69
+ active_call: active_call,
70
+ rpc_desc: desc
71
+ )
72
+ c.call(method_key)
73
+ end
74
+ elsif desc.server_streamer?
75
+ define_method(method_key) do |message, active_call, &block|
76
+ controller = controller.name.constantize
77
+ c = controller.new(
78
+ method_key: method_key,
79
+ service: service_ref,
80
+ message: message,
81
+ active_call: active_call,
82
+ rpc_desc: desc
83
+ )
84
+ c.call(method_key, &block)
85
+ end
86
+ else # bidi
87
+ define_method(method_key) do |messages, active_call, &block|
88
+ controller = controller.name.constantize
89
+ c = controller.new(
90
+ method_key: method_key,
91
+ service: service_ref,
92
+ message: messages,
93
+ active_call: active_call,
94
+ rpc_desc: desc
95
+ )
96
+ c.call(method_key, &block)
97
+ end
104
98
  end
105
99
  end
106
100
  end
107
101
  end
108
-
109
- ##
110
- # @return Array<Gruf::Controllers::ServiceBinder::BoundDesc>
111
- #
112
- def rpc_methods
113
- @service.rpc_descs.map { |rd| BoundDesc.new(rd) }
114
- end
115
102
  end
116
103
  end
117
104
  end
data/lib/gruf/error.rb CHANGED
@@ -15,10 +15,6 @@
15
15
  # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16
16
  # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17
17
  #
18
- require_relative 'errors/field'
19
- require_relative 'errors/debug_info'
20
- require_relative 'serializers/errors/base'
21
- require_relative 'serializers/errors/json'
22
18
 
23
19
  module Gruf
24
20
  ##
@@ -16,20 +16,6 @@
16
16
  # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17
17
  #
18
18
  module Gruf
19
- ##
20
- # Handles internal gruf logging requests
21
- #
22
- module Logger
23
- ##
24
- # Return the current Gruf logger
25
- #
26
- # @return [Logger]
27
- #
28
- def logger
29
- Gruf.logger
30
- end
31
- end
32
-
33
19
  ##
34
20
  # Handles grpc internal logging requests
35
21
  #
@@ -1,9 +1,39 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
6
+ # documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
7
+ # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
8
+ # persons to whom the Software is furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
11
+ # Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
14
+ # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
15
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16
+ # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17
+ #
3
18
  module Gruf
4
19
  module Integrations
5
20
  module Rails
21
+ ##
22
+ # Rails integration for Gruf, that currently only manages code autoloading in a Rails context
23
+ #
6
24
  class Railtie < ::Rails::Railtie
25
+ initializer 'gruf.initializer' do |app|
26
+ config.before_configuration do
27
+ # Remove autoloading of the controllers path from Rails' zeitwerk, so that we ensure Gruf's zeitwerk
28
+ # properly manages them itself. This allows us to manage code reloading and logging in Gruf specifically
29
+ app.config.eager_load_paths -= [::Gruf.controllers_path] if app.config.respond_to?(:eager_load_paths)
30
+ if ::Rails.respond_to?(:autoloaders) # if we're on a late enough version of rails
31
+ ::Rails.autoloaders.each do |autoloader|
32
+ autoloader.ignore(Gruf.controllers_path)
33
+ end
34
+ end
35
+ end
36
+ end
7
37
  end
8
38
  end
9
39
  end
@@ -44,13 +44,3 @@ module Gruf
44
44
  end
45
45
  end
46
46
  end
47
-
48
- require_relative 'client_interceptor'
49
- require_relative 'server_interceptor'
50
- require_relative 'context'
51
- require_relative 'timer'
52
- require_relative 'active_record/connection_reset'
53
- require_relative 'authentication/basic'
54
- require_relative 'instrumentation/statsd'
55
- require_relative 'instrumentation/output_metadata_timer'
56
- require_relative 'instrumentation/request_logging/interceptor'
@@ -16,9 +16,6 @@
16
16
  # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17
17
  #
18
18
  require 'socket'
19
- require_relative 'formatters/base'
20
- require_relative 'formatters/logstash'
21
- require_relative 'formatters/plain'
22
19
 
23
20
  module Gruf
24
21
  module Interceptors
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
6
+ # documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
7
+ # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
8
+ # persons to whom the Software is furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
11
+ # Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
14
+ # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
15
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16
+ # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17
+ #
18
+ module Gruf
19
+ ##
20
+ # Handles internal gruf logging requests
21
+ #
22
+ module Logger
23
+ ##
24
+ # Return the current Gruf logger
25
+ #
26
+ # @return [Logger]
27
+ #
28
+ def logger
29
+ Gruf.logger
30
+ end
31
+ end
32
+ end
data/lib/gruf/server.rb CHANGED
@@ -43,11 +43,10 @@ module Gruf
43
43
  @options = opts || {}
44
44
  @interceptors = opts.fetch(:interceptor_registry, Gruf.interceptors)
45
45
  @interceptors = Gruf::Interceptors::Registry.new unless @interceptors.is_a?(Gruf::Interceptors::Registry)
46
- @services = []
46
+ @services = nil
47
47
  @started = false
48
48
  @hostname = opts.fetch(:hostname, Gruf.server_binding_url)
49
49
  @event_listener_proc = opts.fetch(:event_listener_proc, Gruf.event_listener_proc)
50
- setup
51
50
  end
52
51
 
53
52
  ##
@@ -75,7 +74,12 @@ module Gruf
75
74
  end
76
75
 
77
76
  @port = server.add_http2_port(@hostname, ssl_credentials)
78
- @services.each { |s| server.handle(s) }
77
+ # do not reference `services` any earlier than this method, as it allows autoloading to take effect
78
+ # and load services into `Gruf.services` as late as possible, which gives us flexibility with different
79
+ # execution paths (such as vanilla ruby, grape, multiple Rails versions, etc). The autoloaders are
80
+ # initially loaded in `Gruf::Cli::Executor` _directly_ before the gRPC services are loaded into the gRPC
81
+ # server, to allow for loading services as late as possible in the execution chain.
82
+ services.each { |s| server.handle(s) }
79
83
  server
80
84
  end
81
85
  end
@@ -89,7 +93,7 @@ module Gruf
89
93
  update_proc_title(:starting)
90
94
 
91
95
  server_thread = Thread.new do
92
- logger.info { "Starting gruf server at #{@hostname}..." }
96
+ logger.info { "[gruf] Starting gruf server at #{@hostname}..." }
93
97
  server.run_till_terminated_or_interrupted(KILL_SIGNALS)
94
98
  end
95
99
  @started = true
@@ -98,7 +102,7 @@ module Gruf
98
102
  @started = false
99
103
 
100
104
  update_proc_title(:stopped)
101
- logger.info { 'Goodbye!' }
105
+ logger.info { '[gruf] Goodbye!' }
102
106
  end
103
107
  # :nocov:
104
108
 
@@ -111,7 +115,7 @@ module Gruf
111
115
  def add_service(klass)
112
116
  raise ServerAlreadyStartedError if @started
113
117
 
114
- @services << klass unless @services.include?(klass)
118
+ @services << klass unless services.include?(klass)
115
119
  end
116
120
 
117
121
  ##
@@ -185,31 +189,11 @@ module Gruf
185
189
  private
186
190
 
187
191
  ##
188
- # Setup server
189
- #
190
- # :nocov:
191
- def setup
192
- load_controllers
193
- end
194
- # :nocov:
195
-
196
- ##
197
- # Auto-load all gRPC handlers
192
+ # @return [Array<Class>]
198
193
  #
199
- # :nocov:
200
- def load_controllers
201
- return unless File.directory?(controllers_path)
202
-
203
- path = File.realpath(controllers_path)
204
- $LOAD_PATH.unshift(path)
205
- Dir["#{path}/**/*.rb"].each do |f|
206
- next if f.include?('_pb') # exclude if people include proto generated files in app/rpc
207
-
208
- logger.info "- Loading gRPC service file: #{f}"
209
- load File.realpath(f)
210
- end
194
+ def services
195
+ @services ||= (::Gruf.services || (options.fetch(:services, nil) || []))
211
196
  end
212
- # :nocov:
213
197
 
214
198
  ##
215
199
  # @param [String]
data/lib/gruf/version.rb CHANGED
@@ -16,5 +16,5 @@
16
16
  # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17
17
  #
18
18
  module Gruf
19
- VERSION = '2.14.1'
19
+ VERSION = '2.16.0'
20
20
  end
data/lib/gruf.rb CHANGED
@@ -17,29 +17,21 @@
17
17
  #
18
18
  require 'grpc'
19
19
  require 'active_support/core_ext/module/delegation'
20
+ require 'active_support/hash_with_indifferent_access'
20
21
  require 'active_support/concern'
21
22
  require 'active_support/inflector'
23
+ require 'grpc/health/v1/health_services_pb'
22
24
  require 'base64'
23
- require_relative 'gruf/version'
24
- require_relative 'gruf/logging'
25
- require_relative 'gruf/loggable'
26
- require_relative 'gruf/configuration'
27
- require_relative 'gruf/errors/helpers'
28
- require_relative 'gruf/cli/executor'
29
- require_relative 'gruf/controllers/base'
30
- require_relative 'gruf/outbound/request_context'
31
- require_relative 'gruf/interceptors/registry'
32
- require_relative 'gruf/interceptors/base'
33
- require_relative 'gruf/hooks/registry'
34
- require_relative 'gruf/hooks/executor'
35
- require_relative 'gruf/hooks/base'
36
- require_relative 'gruf/timer'
37
- require_relative 'gruf/response'
38
- require_relative 'gruf/error'
39
- require_relative 'gruf/client'
40
- require_relative 'gruf/synchronized_client'
41
- require_relative 'gruf/instrumentable_grpc_server'
42
- require_relative 'gruf/server'
25
+
26
+ # use Zeitwerk to lazily autoload all the files in the lib directory
27
+ require 'zeitwerk'
28
+ loader = ::Zeitwerk::Loader.new
29
+ loader.tag = File.basename(__FILE__, '.rb')
30
+ loader.inflector = ::Zeitwerk::GemInflector.new(__FILE__)
31
+ loader.ignore("#{__dir__}/gruf/integrations/rails/railtie.rb")
32
+ loader.push_dir(__dir__)
33
+ loader.setup
34
+
43
35
  require_relative 'gruf/integrations/rails/railtie' if defined?(::Rails)
44
36
 
45
37
  ##
@@ -47,4 +39,10 @@ require_relative 'gruf/integrations/rails/railtie' if defined?(::Rails)
47
39
  #
48
40
  module Gruf
49
41
  extend Configuration
42
+
43
+ class << self
44
+ def autoloaders
45
+ Autoloaders
46
+ end
47
+ end
50
48
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gruf
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.14.1
4
+ version: 2.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shaun McCormick
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-04-14 00:00:00.000000000 Z
11
+ date: 2022-08-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler-audit
@@ -304,6 +304,20 @@ dependencies:
304
304
  - - ">="
305
305
  - !ruby/object:Gem::Version
306
306
  version: '0.1'
307
+ - !ruby/object:Gem::Dependency
308
+ name: zeitwerk
309
+ requirement: !ruby/object:Gem::Requirement
310
+ requirements:
311
+ - - ">="
312
+ - !ruby/object:Gem::Version
313
+ version: '2'
314
+ type: :runtime
315
+ prerelease: false
316
+ version_requirements: !ruby/object:Gem::Requirement
317
+ requirements:
318
+ - - ">="
319
+ - !ruby/object:Gem::Version
320
+ version: '2'
307
321
  description: gRPC Ruby Framework for building complex gRPC applications at scale
308
322
  email:
309
323
  - splittingred@gmail.com
@@ -318,18 +332,23 @@ files:
318
332
  - bin/gruf
319
333
  - gruf.gemspec
320
334
  - lib/gruf.rb
335
+ - lib/gruf/autoloaders.rb
321
336
  - lib/gruf/cli/executor.rb
322
337
  - lib/gruf/client.rb
323
338
  - lib/gruf/client/error.rb
324
339
  - lib/gruf/client/error_factory.rb
340
+ - lib/gruf/client/errors.rb
325
341
  - lib/gruf/configuration.rb
342
+ - lib/gruf/controllers/autoloader.rb
326
343
  - lib/gruf/controllers/base.rb
344
+ - lib/gruf/controllers/health_controller.rb
327
345
  - lib/gruf/controllers/request.rb
328
346
  - lib/gruf/controllers/service_binder.rb
329
347
  - lib/gruf/error.rb
330
348
  - lib/gruf/errors/debug_info.rb
331
349
  - lib/gruf/errors/field.rb
332
350
  - lib/gruf/errors/helpers.rb
351
+ - lib/gruf/grpc_logger.rb
333
352
  - lib/gruf/hooks/base.rb
334
353
  - lib/gruf/hooks/executor.rb
335
354
  - lib/gruf/hooks/registry.rb
@@ -350,7 +369,7 @@ files:
350
369
  - lib/gruf/interceptors/server_interceptor.rb
351
370
  - lib/gruf/interceptors/timer.rb
352
371
  - lib/gruf/loggable.rb
353
- - lib/gruf/logging.rb
372
+ - lib/gruf/logger.rb
354
373
  - lib/gruf/outbound/request_context.rb
355
374
  - lib/gruf/response.rb
356
375
  - lib/gruf/serializers/errors/base.rb
@@ -363,7 +382,12 @@ homepage: https://github.com/bigcommerce/gruf
363
382
  licenses:
364
383
  - MIT
365
384
  metadata:
385
+ bug_tracker_uri: https://github.com/bigcommerce/gruf/issues
386
+ changelog_uri: https://github.com/bigcommerce/gruf/CHANGELOG.md
387
+ homepage_uri: https://github.com/bigcommerce/gruf
366
388
  rubygems_mfa_required: 'true'
389
+ source_code_uri: https://github.com/bigcommerce/gruf
390
+ wiki_uri: https://github.com/bigcommerce/gruf/wiki
367
391
  post_install_message:
368
392
  rdoc_options: []
369
393
  require_paths:
@@ -372,7 +396,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
372
396
  requirements:
373
397
  - - ">="
374
398
  - !ruby/object:Gem::Version
375
- version: '2.6'
399
+ version: '2.7'
376
400
  - - "<"
377
401
  - !ruby/object:Gem::Version
378
402
  version: '3.2'
@@ -382,7 +406,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
382
406
  - !ruby/object:Gem::Version
383
407
  version: '0'
384
408
  requirements: []
385
- rubygems_version: 3.3.7
409
+ rubygems_version: 3.3.3
386
410
  signing_key:
387
411
  specification_version: 4
388
412
  summary: gRPC Ruby Framework