gruf 2.14.1 → 2.15.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: a807b73598db4fb6caf6e0ed79d164342c1f6f0cfa4b0cf03f83f40cb71baabf
4
+ data.tar.gz: c750eac85c6739048498c5579dabbcf268bc796e998e26c14c7b6e2a96ad3d88
5
5
  SHA512:
6
- metadata.gz: bf230312d6315af4534e4bb92bbd7405a9850c1ec415895e7cb25f382239f887b8fadf0940207459d237c81c3fe2da0647244c3b1100f73a441c77e118a9bea6
7
- data.tar.gz: e505ca2046aa82164be4656aca0b9ad7f696c070abd5c7ea52f8d6afd6e803feaaef095cf96825a2b8dc8b186f4015b8430f68f0c1f9ce472d2764976d596a4f
6
+ metadata.gz: 0ab26cdfa7b95fc92694dc328ac66b32994f35ddfb5bf9f63fb235256853f77f1b4c382bb01870c1afd5d037a2e915cacc0d5c8c307c30d480b90f5d0b373736
7
+ data.tar.gz: 06b7a29c4f6bd167178e68d77c4bc68fa704233232b405fa193db8fb4721895391fce58ab70fd06c92ff6a31a4276f20ac8a00a834e0f4c8757ad03f153abeba
data/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@ Changelog for the gruf gem. This includes internal history before the gem was ma
2
2
 
3
3
  ### Pending release
4
4
 
5
+ ### 2.15.0
6
+
7
+ - Autoload Gruf Controllers with zeitwerk, allowing for code reloading in development environments
8
+ - Add `GRUF_CONTROLLERS_PATH` ENV to allow ENV-based runtime configuration of path to gruf controller files
9
+ - Move `ServiceBinder` from instance to static class for performance improvements
10
+ - Use zeitwerk for autoloading
11
+ - Update gem metadata
12
+ - Update `Gruf:Configuration#environment` to use `ENV.fetch`
13
+
5
14
  ### 2.14.1
6
15
 
7
16
  - Fix issue where the server object hits thread contention in certain race conditions
@@ -13,9 +22,9 @@ Changelog for the gruf gem. This includes internal history before the gem was ma
13
22
 
14
23
  ### 2.13.1
15
24
 
16
- - Fix issue with race condition in server starts where servers may fail to bind connections and never reach
25
+ - Fix issue with race condition in server starts where servers may fail to bind connections and never reach
17
26
  serving state (fixes #147)
18
-
27
+
19
28
  ### 2.13.0
20
29
 
21
30
  - Remove server mutex handling in deference to core grpc signal handling
@@ -27,7 +36,7 @@ Changelog for the gruf gem. This includes internal history before the gem was ma
27
36
 
28
37
  ### 2.11.0
29
38
 
30
- - Restrict grpc gem to <= 1.41.0 due to regressions in grpc 1.42.x
39
+ - Restrict grpc gem to <= 1.41.0 due to regressions in grpc 1.42.x
31
40
  - Fallback to stdout logger at INFO if no logger is setup
32
41
  - Better handling of namespace collisions with Rails
33
42
  - Add `GRPC_SERVER_HOST` and `GRPC_SERVER_PORT` for ENV configuration of the server host+port
@@ -67,7 +76,7 @@ Changelog for the gruf gem. This includes internal history before the gem was ma
67
76
 
68
77
  ### 2.8.0
69
78
 
70
- - Pass the controller request object into the request logging formatters [#92]
79
+ - Pass the controller request object into the request logging formatters [#92]
71
80
 
72
81
  ### 2.7.1
73
82
 
@@ -80,12 +89,12 @@ Changelog for the gruf gem. This includes internal history before the gem was ma
80
89
  ### 2.6.1
81
90
 
82
91
  - Add frozen_string_literal: true to files, update rubocop to 0.68
83
-
92
+
84
93
  ### 2.6.0
85
94
 
86
95
  - 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
96
+ - Abstract gruf controller's send to make it usable in filters
97
+ - Adjusts configuration reset into a Railtie for Rails systems to ensure proper OOE
89
98
  - Bump rubocop to 0.64, address violations, update activesupport/concurrent-ruby dependencies to have a min version
90
99
 
91
100
  ### 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'
@@ -35,7 +34,14 @@ Gem::Specification.new do |spec|
35
34
 
36
35
  spec.required_ruby_version = '>= 2.6', '< 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
47
  # rubocop:disable Gemspec/RubyVersionGlobalsUsage
@@ -64,4 +70,5 @@ Gem::Specification.new do |spec|
64
70
  spec.add_runtime_dependency 'json', '>= 2.3'
65
71
  spec.add_runtime_dependency 'slop', '>= 4.6'
66
72
  spec.add_runtime_dependency 'thwait', '>= 0.1'
73
+ spec.add_runtime_dependency 'zeitwerk', '>= 2'
67
74
  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,6 +52,17 @@ module Gruf
46
52
  #
47
53
  def run
48
54
  exception = nil
55
+ # wait to load controllers until last possible second to allow late configuration
56
+ ::Gruf.autoloaders.load!(controllers_path: Gruf.controllers_path)
57
+ # allow lazy registering globally as late as possible, this allows more flexible binstub injections
58
+ @services = ::Gruf.services unless @services&.any?
59
+
60
+ unless @services.any?
61
+ raise NoServicesBoundError,
62
+ 'No services bound to this gruf process; please bind a service to a Gruf controller ' \
63
+ 'to start the server successfully'
64
+ end
65
+
49
66
  begin
50
67
  @services.each { |s| @server.add_service(s) }
51
68
  @hook_executor.call(:before_server_start, server: @server)
@@ -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
@@ -165,7 +165,8 @@ module Gruf
165
165
  determine_loggers
166
166
  self.ssl_crt_file = "#{root_path}config/ssl/#{environment}.crt"
167
167
  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"
168
+ cp = ::ENV.fetch('GRUF_CONTROLLERS_PATH', 'app/rpc').to_s
169
+ self.controllers_path = root_path.to_s.empty? ? cp : "#{root_path}/#{cp}"
169
170
  self.backtrace_on_error = ::ENV.fetch('GRPC_BACKTRACE_ON_ERROR', 0).to_i.positive?
170
171
  self.rpc_server_options = {
171
172
  max_waiting_requests: ::ENV.fetch('GRPC_SERVER_MAX_WAITING_REQUESTS',
@@ -183,6 +184,13 @@ module Gruf
183
184
  options
184
185
  end
185
186
 
187
+ ##
188
+ # @return [Boolean]
189
+ #
190
+ def development?
191
+ environment == 'development'
192
+ end
193
+
186
194
  private
187
195
 
188
196
  ##
@@ -194,7 +202,7 @@ module Gruf
194
202
  if defined?(::Rails)
195
203
  ::Rails.env.to_s
196
204
  else
197
- (ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development').to_s
205
+ ENV.fetch('RACK_ENV') { ENV.fetch('RAILS_ENV', 'development') }.to_s
198
206
  end
199
207
  end
200
208
 
@@ -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
@@ -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.15.0'
20
20
  end
data/lib/gruf.rb CHANGED
@@ -20,26 +20,16 @@ require 'active_support/core_ext/module/delegation'
20
20
  require 'active_support/concern'
21
21
  require 'active_support/inflector'
22
22
  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'
23
+
24
+ # use Zeitwerk to lazily autoload all the files in the lib directory
25
+ require 'zeitwerk'
26
+ loader = ::Zeitwerk::Loader.new
27
+ loader.tag = File.basename(__FILE__, '.rb')
28
+ loader.inflector = ::Zeitwerk::GemInflector.new(__FILE__)
29
+ loader.ignore("#{__dir__}/gruf/integrations/rails/railtie.rb")
30
+ loader.push_dir(__dir__)
31
+ loader.setup
32
+
43
33
  require_relative 'gruf/integrations/rails/railtie' if defined?(::Rails)
44
34
 
45
35
  ##
@@ -47,4 +37,10 @@ require_relative 'gruf/integrations/rails/railtie' if defined?(::Rails)
47
37
  #
48
38
  module Gruf
49
39
  extend Configuration
40
+
41
+ class << self
42
+ def autoloaders
43
+ Autoloaders
44
+ end
45
+ end
50
46
  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.15.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-06-30 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,11 +332,14 @@ 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
327
344
  - lib/gruf/controllers/request.rb
328
345
  - lib/gruf/controllers/service_binder.rb
@@ -330,6 +347,7 @@ files:
330
347
  - lib/gruf/errors/debug_info.rb
331
348
  - lib/gruf/errors/field.rb
332
349
  - lib/gruf/errors/helpers.rb
350
+ - lib/gruf/grpc_logger.rb
333
351
  - lib/gruf/hooks/base.rb
334
352
  - lib/gruf/hooks/executor.rb
335
353
  - lib/gruf/hooks/registry.rb
@@ -350,7 +368,7 @@ files:
350
368
  - lib/gruf/interceptors/server_interceptor.rb
351
369
  - lib/gruf/interceptors/timer.rb
352
370
  - lib/gruf/loggable.rb
353
- - lib/gruf/logging.rb
371
+ - lib/gruf/logger.rb
354
372
  - lib/gruf/outbound/request_context.rb
355
373
  - lib/gruf/response.rb
356
374
  - lib/gruf/serializers/errors/base.rb
@@ -363,7 +381,12 @@ homepage: https://github.com/bigcommerce/gruf
363
381
  licenses:
364
382
  - MIT
365
383
  metadata:
384
+ bug_tracker_uri: https://github.com/bigcommerce/gruf/issues
385
+ changelog_uri: https://github.com/bigcommerce/gruf/CHANGELOG.md
386
+ homepage_uri: https://github.com/bigcommerce/gruf
366
387
  rubygems_mfa_required: 'true'
388
+ source_code_uri: https://github.com/bigcommerce/gruf
389
+ wiki_uri: https://github.com/bigcommerce/gruf/wiki
367
390
  post_install_message:
368
391
  rdoc_options: []
369
392
  require_paths:
@@ -382,7 +405,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
382
405
  - !ruby/object:Gem::Version
383
406
  version: '0'
384
407
  requirements: []
385
- rubygems_version: 3.3.7
408
+ rubygems_version: 3.3.12
386
409
  signing_key:
387
410
  specification_version: 4
388
411
  summary: gRPC Ruby Framework