tonic-rails 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +52 -0
- data/Cargo.lock +1242 -0
- data/Cargo.toml +11 -0
- data/LICENSE +21 -0
- data/README.md +391 -0
- data/ext/tonic_rails_native/Cargo.toml +44 -0
- data/ext/tonic_rails_native/extconf.rb +9 -0
- data/ext/tonic_rails_native/src/body.rs +289 -0
- data/ext/tonic_rails_native/src/dispatch.rs +250 -0
- data/ext/tonic_rails_native/src/framing.rs +349 -0
- data/ext/tonic_rails_native/src/gvl.rs +473 -0
- data/ext/tonic_rails_native/src/lib.rs +90 -0
- data/ext/tonic_rails_native/src/net.rs +146 -0
- data/ext/tonic_rails_native/src/server.rs +507 -0
- data/lib/tonic/rails/base.rb +215 -0
- data/lib/tonic/rails/configuration.rb +149 -0
- data/lib/tonic/rails/railtie.rb +56 -0
- data/lib/tonic/rails/reflection.rb +50 -0
- data/lib/tonic/rails/router.rb +71 -0
- data/lib/tonic/rails/rpc_error.rb +65 -0
- data/lib/tonic/rails/server.rb +224 -0
- data/lib/tonic/rails/tasks/tonic-rails.rake +36 -0
- data/lib/tonic/rails/testing.rb +85 -0
- data/lib/tonic/rails/version.rb +7 -0
- data/lib/tonic-rails.rb +91 -0
- metadata +121 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Tonic
|
|
4
|
+
module Rails
|
|
5
|
+
# Runs the gRPC server: a tonic transport in Rust plus a pool of Ruby
|
|
6
|
+
# dispatch threads.
|
|
7
|
+
#
|
|
8
|
+
# server = Tonic::Rails::Server.new
|
|
9
|
+
# server.register(ProductService)
|
|
10
|
+
# server.start # blocks until SIGTERM / SIGINT or #stop
|
|
11
|
+
#
|
|
12
|
+
# Shutdown is graceful: health checks flip to NOT_SERVING, new
|
|
13
|
+
# connections are refused, in-flight RPCs get `drain_timeout` seconds
|
|
14
|
+
# to finish, then everything is torn down.
|
|
15
|
+
class Server
|
|
16
|
+
attr_reader :router, :configuration
|
|
17
|
+
|
|
18
|
+
def initialize(configuration: Tonic::Rails.configuration)
|
|
19
|
+
@configuration = configuration
|
|
20
|
+
@router = Router.new
|
|
21
|
+
@native = nil
|
|
22
|
+
@state = :stopped
|
|
23
|
+
@stop_reader = nil
|
|
24
|
+
@stop_writer = nil
|
|
25
|
+
@stop_requested = false
|
|
26
|
+
@lock = Mutex.new
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# @param service_class [Class, String] handler class (or its name)
|
|
30
|
+
def register(service_class)
|
|
31
|
+
service_class = Object.const_get(service_class) if service_class.is_a?(String)
|
|
32
|
+
service_class.register_on(@router)
|
|
33
|
+
self
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Start serving and block until stopped.
|
|
37
|
+
# @param install_signal_handlers [Boolean] trap TERM and INT to stop.
|
|
38
|
+
# Pass false when embedding in a process that owns its signals.
|
|
39
|
+
# @yield once the server is accepting connections
|
|
40
|
+
def start(install_signal_handlers: true)
|
|
41
|
+
@lock.synchronize do
|
|
42
|
+
raise Error, "server is already running" unless @state == :stopped
|
|
43
|
+
|
|
44
|
+
@state = :starting
|
|
45
|
+
@stop_requested = false
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
begin
|
|
49
|
+
raise Error, "no services registered" if @router.empty?
|
|
50
|
+
|
|
51
|
+
@configuration.validate!
|
|
52
|
+
Tonic::Rails.native_extension!
|
|
53
|
+
Tonic::Rails._set_shutting_down(false)
|
|
54
|
+
@stop_reader, @stop_writer = IO.pipe
|
|
55
|
+
stop if @stop_requested # requested while we were starting up
|
|
56
|
+
@native = build_native_server
|
|
57
|
+
@native.start_server
|
|
58
|
+
@state = :running
|
|
59
|
+
|
|
60
|
+
previous_handlers = install_signal_handlers ? trap_signals : {}
|
|
61
|
+
workers = Array.new(@configuration.dispatch_threads) { |i| spawn_worker(i) }
|
|
62
|
+
log_startup
|
|
63
|
+
yield self if block_given?
|
|
64
|
+
|
|
65
|
+
wait_for_stop(workers)
|
|
66
|
+
shutdown(workers)
|
|
67
|
+
ensure
|
|
68
|
+
previous_handlers&.each { |signal, handler| Signal.trap(signal, handler || "DEFAULT") }
|
|
69
|
+
@native&.stop_server
|
|
70
|
+
@native = nil
|
|
71
|
+
[@stop_reader, @stop_writer].each { |io| io.close if io && !io.closed? }
|
|
72
|
+
@stop_reader = @stop_writer = nil
|
|
73
|
+
Tonic::Rails._set_shutting_down(false)
|
|
74
|
+
@state = :stopped
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Request a graceful stop. Safe to call from signal handlers and
|
|
79
|
+
# other threads; returns immediately.
|
|
80
|
+
def stop
|
|
81
|
+
@stop_requested = true
|
|
82
|
+
@stop_writer&.write_nonblock(".", exception: false)
|
|
83
|
+
rescue IOError
|
|
84
|
+
# Already stopped.
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def running?
|
|
88
|
+
@state == :running
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# @return [Hash{Symbol=>Integer}] request and connection counters
|
|
92
|
+
def stats
|
|
93
|
+
native = @native
|
|
94
|
+
return native.stats if native
|
|
95
|
+
|
|
96
|
+
{ total: 0, successful: 0, errors: 0, cancelled: 0, deadline_exceeded: 0,
|
|
97
|
+
resource_exhausted: 0, unimplemented: 0, active_connections: 0, queued: 0 }
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
private
|
|
101
|
+
|
|
102
|
+
def build_native_server
|
|
103
|
+
config = @configuration
|
|
104
|
+
native = TonicRails::Native::Server.new(config.bind_address, config.max_message_size)
|
|
105
|
+
native.on_request(build_callback)
|
|
106
|
+
native.set_routes(@router.paths)
|
|
107
|
+
if config.tls?
|
|
108
|
+
native.set_tls(config.tls_cert_path, config.tls_key_path)
|
|
109
|
+
native.set_tls_client_ca(config.tls_client_ca_path) if config.tls_client_ca_path
|
|
110
|
+
end
|
|
111
|
+
native.set_max_connections(config.max_connections)
|
|
112
|
+
native.set_concurrency_limit_per_connection(config.concurrency_limit_per_connection)
|
|
113
|
+
native.set_worker_threads(config.worker_threads || 0)
|
|
114
|
+
native.set_keepalive(config.keepalive_interval.to_f, config.keepalive_timeout.to_f)
|
|
115
|
+
native.set_dispatch_queue_size(config.dispatch_queue_size)
|
|
116
|
+
native.set_log_requests(config.log_requests ? true : false)
|
|
117
|
+
native.set_compression(config.compression ? true : false)
|
|
118
|
+
native.set_reflection(config.reflection ? true : false)
|
|
119
|
+
native.set_reflection_descriptors(Reflection.file_descriptors(@router.service_names)) if config.reflection
|
|
120
|
+
native
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# The proc the transport calls for every RPC:
|
|
124
|
+
# (path, metadata, call) → encoded response String, or nil if streamed
|
|
125
|
+
def build_callback
|
|
126
|
+
router = @router
|
|
127
|
+
call = ->(path, metadata, transport) { router.dispatch(path, metadata, transport) }
|
|
128
|
+
|
|
129
|
+
call = @configuration.interceptors.reverse.reduce(call) do |inner, interceptor|
|
|
130
|
+
lambda do |path, metadata, transport|
|
|
131
|
+
interceptor.call(path, metadata, -> { inner.call(path, metadata, transport) })
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
guarded = lambda do |path, metadata, transport|
|
|
136
|
+
call.call(path, metadata, transport)
|
|
137
|
+
rescue RpcError
|
|
138
|
+
raise
|
|
139
|
+
rescue StandardError => e
|
|
140
|
+
Tonic::Rails.log_exception(e, path)
|
|
141
|
+
raise RpcError.new("internal error", code: :internal)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
wrap_with_rails_executor(guarded)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Each RPC runs inside the Rails executor (or reloader, when code
|
|
148
|
+
# reloading is on): ActiveRecord connections are returned, query
|
|
149
|
+
# caches and CurrentAttributes are reset — the same isolation Rack
|
|
150
|
+
# requests and Active Job get.
|
|
151
|
+
def wrap_with_rails_executor(callback)
|
|
152
|
+
return callback unless defined?(::Rails.application) && ::Rails.application
|
|
153
|
+
|
|
154
|
+
app = ::Rails.application
|
|
155
|
+
wrapper = @configuration.reload_handlers ? app.reloader : app.executor
|
|
156
|
+
->(path, metadata, transport) { wrapper.wrap { callback.call(path, metadata, transport) } }
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def spawn_worker(index)
|
|
160
|
+
native = @native
|
|
161
|
+
Thread.new do
|
|
162
|
+
Thread.current.name = "tonic-rails-dispatch-#{index}"
|
|
163
|
+
native.serve_worker
|
|
164
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
165
|
+
Tonic::Rails.log_exception(e, "dispatch thread #{index}")
|
|
166
|
+
stop
|
|
167
|
+
raise
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def trap_signals
|
|
172
|
+
%w[TERM INT].to_h { |signal| [signal, Signal.trap(signal) { stop }] }
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def wait_for_stop(workers)
|
|
176
|
+
loop do
|
|
177
|
+
break if @stop_reader.wait_readable(1)
|
|
178
|
+
|
|
179
|
+
dead = workers.reject(&:alive?)
|
|
180
|
+
unless dead.empty?
|
|
181
|
+
@configuration.logger&.error("tonic-rails: #{dead.size} dispatch thread(s) died; shutting down")
|
|
182
|
+
break
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def shutdown(workers)
|
|
188
|
+
logger = @configuration.logger
|
|
189
|
+
@state = :stopping
|
|
190
|
+
Tonic::Rails._set_shutting_down(true)
|
|
191
|
+
logger&.info("tonic-rails: shutting down, draining for up to #{@configuration.drain_timeout}s")
|
|
192
|
+
|
|
193
|
+
@native.begin_shutdown
|
|
194
|
+
unless @native.wait_for_drain(@configuration.drain_timeout.to_f)
|
|
195
|
+
logger&.warn("tonic-rails: drain timeout expired; closing remaining connections")
|
|
196
|
+
end
|
|
197
|
+
@native.stop_workers
|
|
198
|
+
workers.each do |worker|
|
|
199
|
+
worker.join
|
|
200
|
+
rescue Exception # rubocop:disable Lint/RescueException
|
|
201
|
+
# Already logged by the worker.
|
|
202
|
+
end
|
|
203
|
+
logger&.info("tonic-rails: stopped")
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def log_startup
|
|
207
|
+
config = @configuration
|
|
208
|
+
logger = config.logger
|
|
209
|
+
return unless logger
|
|
210
|
+
|
|
211
|
+
if config.tls?
|
|
212
|
+
logger.info("tonic-rails: TLS enabled#{' (mutual TLS)' if config.tls_client_ca_path}")
|
|
213
|
+
else
|
|
214
|
+
logger.warn("tonic-rails: TLS is not configured; gRPC traffic is plaintext")
|
|
215
|
+
end
|
|
216
|
+
logger.info("tonic-rails: reflection enabled") if config.reflection
|
|
217
|
+
logger.info(
|
|
218
|
+
"tonic-rails: serving #{@router.service_names.join(', ')} on #{config.bind_address} " \
|
|
219
|
+
"(#{config.dispatch_threads} dispatch threads)",
|
|
220
|
+
)
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
|
|
5
|
+
namespace :tonic_rails do
|
|
6
|
+
desc "Start the gRPC server (boots the Rails app with its registered services)"
|
|
7
|
+
task serve: :environment do
|
|
8
|
+
# Eager-load so handlers registered via config.to_prepare and
|
|
9
|
+
# autoloaded constants are in place, as they would be in production.
|
|
10
|
+
Rails.application.eager_load! unless Rails.application.config.eager_load
|
|
11
|
+
Tonic::Rails.server.start
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
desc "List registered gRPC methods"
|
|
15
|
+
task routes: :environment do
|
|
16
|
+
router = Tonic::Rails.server.router
|
|
17
|
+
if router.empty?
|
|
18
|
+
puts "No gRPC services registered."
|
|
19
|
+
else
|
|
20
|
+
router.paths.sort.each { |path| puts path }
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
desc "Generate Ruby protobuf classes (PROTO_DIR=app/protos RUBY_OUT=lib/grpc; requires protoc)"
|
|
25
|
+
task :compile_protos do
|
|
26
|
+
proto_dir = ENV.fetch("PROTO_DIR", "app/protos")
|
|
27
|
+
ruby_out = ENV.fetch("RUBY_OUT", "lib/grpc")
|
|
28
|
+
protos = Dir.glob(File.join(proto_dir, "**", "*.proto"))
|
|
29
|
+
abort "No .proto files found in #{proto_dir}/" if protos.empty?
|
|
30
|
+
abort "protoc not found on PATH. Install the Protocol Buffers compiler." unless system("protoc", "--version", out: File::NULL)
|
|
31
|
+
|
|
32
|
+
FileUtils.mkdir_p(ruby_out)
|
|
33
|
+
sh "protoc", "--ruby_out=#{ruby_out}", "-I#{proto_dir}", *protos
|
|
34
|
+
puts "Compiled #{protos.size} proto file(s) to #{ruby_out}/"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Tonic
|
|
4
|
+
module Rails
|
|
5
|
+
# Call handlers in-process, without a network or the native extension.
|
|
6
|
+
# Requests and responses go through real protobuf encoding, so tests
|
|
7
|
+
# exercise the same conversion the server does.
|
|
8
|
+
#
|
|
9
|
+
# require "tonic/rails/testing"
|
|
10
|
+
#
|
|
11
|
+
# response = Tonic::Rails::Testing.call(ProductService, :GetProduct, { id: 1 })
|
|
12
|
+
# response.title # => "Widget"
|
|
13
|
+
#
|
|
14
|
+
# expect { Tonic::Rails::Testing.call(ProductService, :GetProduct, { id: 0 }) }
|
|
15
|
+
# .to raise_error(Tonic::Rails::RpcError) { |e| expect(e.code_name).to eq(:not_found) }
|
|
16
|
+
module Testing
|
|
17
|
+
# Request side of a call: yields pre-encoded messages.
|
|
18
|
+
class Reader
|
|
19
|
+
def initialize(messages, error: nil)
|
|
20
|
+
@messages = messages.dup
|
|
21
|
+
@error = error
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def next
|
|
25
|
+
@messages.shift
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
attr_reader :error
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Response side of a call: collects encoded messages.
|
|
32
|
+
class Sender
|
|
33
|
+
attr_reader :messages
|
|
34
|
+
|
|
35
|
+
def initialize
|
|
36
|
+
@messages = []
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def push(bytes)
|
|
40
|
+
@messages << bytes
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
module_function
|
|
45
|
+
|
|
46
|
+
# @param service_class [Class] handler class
|
|
47
|
+
# @param rpc_name [Symbol, String] RPC name as in the .proto
|
|
48
|
+
# @param request [Google::Protobuf::AbstractMessage, Hash, Array] the
|
|
49
|
+
# request; an Array of them for client- and bidi-streaming RPCs
|
|
50
|
+
# @param metadata [Hash{String=>String}]
|
|
51
|
+
# @param timeout_ms [Integer, nil] simulated client deadline
|
|
52
|
+
# @return [Google::Protobuf::AbstractMessage, Array] the response, or
|
|
53
|
+
# an Array of responses for server- and bidi-streaming RPCs
|
|
54
|
+
# @raise [RpcError] when the handler ends the call with a status
|
|
55
|
+
def call(service_class, rpc_name, request, metadata: {}, timeout_ms: nil)
|
|
56
|
+
spec = service_class.grpc_rpcs.fetch(rpc_name.to_s) do
|
|
57
|
+
raise ArgumentError, "#{service_class} has no RPC #{rpc_name}"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
requests = spec[:request_stream] ? Array(request) : [request]
|
|
61
|
+
reader = Reader.new(requests.map { |r| encode(spec[:request], r) })
|
|
62
|
+
sender = Sender.new
|
|
63
|
+
|
|
64
|
+
result = service_class.new.call_rpc(
|
|
65
|
+
rpc_name.to_s,
|
|
66
|
+
metadata: metadata.transform_keys(&:to_s),
|
|
67
|
+
reader: reader,
|
|
68
|
+
sender: sender,
|
|
69
|
+
timeout_ms: timeout_ms,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
if spec[:response_stream]
|
|
73
|
+
sender.messages.map { |bytes| spec[:response].decode(bytes) }
|
|
74
|
+
else
|
|
75
|
+
spec[:response].decode(result)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def encode(klass, message)
|
|
80
|
+
message = klass.new(message) if message.is_a?(Hash)
|
|
81
|
+
klass.encode(message)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
data/lib/tonic-rails.rb
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "google/protobuf"
|
|
4
|
+
|
|
5
|
+
module Tonic
|
|
6
|
+
module Rails
|
|
7
|
+
class Error < StandardError; end
|
|
8
|
+
class NotConfiguredError < Error; end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
require "tonic/rails/version"
|
|
13
|
+
require "tonic/rails/rpc_error"
|
|
14
|
+
require "tonic/rails/configuration"
|
|
15
|
+
require "tonic/rails/router"
|
|
16
|
+
require "tonic/rails/base"
|
|
17
|
+
require "tonic/rails/reflection"
|
|
18
|
+
require "tonic/rails/server"
|
|
19
|
+
|
|
20
|
+
module Tonic
|
|
21
|
+
module Rails
|
|
22
|
+
@configuration = Configuration.new
|
|
23
|
+
@shutting_down = false
|
|
24
|
+
@native_load_error = nil
|
|
25
|
+
|
|
26
|
+
class << self
|
|
27
|
+
attr_reader :configuration
|
|
28
|
+
|
|
29
|
+
def configure
|
|
30
|
+
yield @configuration
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# The process-wide server used by the Railtie and rake tasks.
|
|
34
|
+
def server
|
|
35
|
+
@server ||= Server.new
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# True once shutdown has begun. Long-running handlers (especially
|
|
39
|
+
# streams) should check this and finish early.
|
|
40
|
+
def cancelled?
|
|
41
|
+
@shutting_down
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# @api private
|
|
45
|
+
def _set_shutting_down(value)
|
|
46
|
+
@shutting_down = value
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# @api private
|
|
50
|
+
# Log an unexpected exception (and hand it to the Rails error
|
|
51
|
+
# reporter, if present) without exposing it to the client.
|
|
52
|
+
def log_exception(error, context)
|
|
53
|
+
configuration.logger&.error(
|
|
54
|
+
"tonic-rails: #{context}: #{error.class}: #{error.message}\n" \
|
|
55
|
+
"#{Array(error.backtrace).first(20).join("\n")}",
|
|
56
|
+
)
|
|
57
|
+
if defined?(::Rails.error) && ::Rails.respond_to?(:error)
|
|
58
|
+
::Rails.error.report(error, handled: true, source: "tonic-rails", context: { rpc: context })
|
|
59
|
+
end
|
|
60
|
+
rescue StandardError
|
|
61
|
+
nil
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# @api private
|
|
65
|
+
def native_extension!
|
|
66
|
+
return if defined?(::TonicRails::Native::Server)
|
|
67
|
+
|
|
68
|
+
raise Error, "tonic-rails native extension is not available " \
|
|
69
|
+
"(#{@native_load_error&.message || 'unknown error'}). " \
|
|
70
|
+
"Reinstall the gem, or run `bundle exec rake compile` in a checkout."
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# @api private
|
|
74
|
+
def load_native_extension
|
|
75
|
+
# Precompiled platform gems ship one binary per Ruby minor version.
|
|
76
|
+
ruby_version = RUBY_VERSION[/\d+\.\d+/]
|
|
77
|
+
require "tonic/rails/#{ruby_version}/tonic_rails_native"
|
|
78
|
+
rescue LoadError
|
|
79
|
+
begin
|
|
80
|
+
require "tonic/rails/tonic_rails_native"
|
|
81
|
+
rescue LoadError => e
|
|
82
|
+
@native_load_error = e
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
load_native_extension
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
require "tonic/rails/railtie" if defined?(Rails::Railtie)
|
metadata
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: tonic-rails
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Matt Tanous
|
|
8
|
+
bindir: bin
|
|
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.1'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '7.1'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: google-protobuf
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '4.27'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '4.27'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: rb_sys
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: 0.9.110
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: 0.9.110
|
|
54
|
+
description: |
|
|
55
|
+
Tonic::Rails serves gRPC from Rails applications. The transport layer
|
|
56
|
+
(HTTP/2 framing, connection management, TLS, health checks, reflection)
|
|
57
|
+
is handled by tonic in Rust and exposed to Ruby through a native
|
|
58
|
+
extension built with Magnus. Protobuf (de)serialization and business
|
|
59
|
+
logic remain in Ruby, giving you Rails-grade handlers, routing,
|
|
60
|
+
interceptors, and the Rails executor around every RPC.
|
|
61
|
+
email:
|
|
62
|
+
- matt.tanous@hey.com
|
|
63
|
+
executables: []
|
|
64
|
+
extensions:
|
|
65
|
+
- ext/tonic_rails_native/extconf.rb
|
|
66
|
+
extra_rdoc_files: []
|
|
67
|
+
files:
|
|
68
|
+
- CHANGELOG.md
|
|
69
|
+
- Cargo.lock
|
|
70
|
+
- Cargo.toml
|
|
71
|
+
- LICENSE
|
|
72
|
+
- README.md
|
|
73
|
+
- ext/tonic_rails_native/Cargo.toml
|
|
74
|
+
- ext/tonic_rails_native/extconf.rb
|
|
75
|
+
- ext/tonic_rails_native/src/body.rs
|
|
76
|
+
- ext/tonic_rails_native/src/dispatch.rs
|
|
77
|
+
- ext/tonic_rails_native/src/framing.rs
|
|
78
|
+
- ext/tonic_rails_native/src/gvl.rs
|
|
79
|
+
- ext/tonic_rails_native/src/lib.rs
|
|
80
|
+
- ext/tonic_rails_native/src/net.rs
|
|
81
|
+
- ext/tonic_rails_native/src/server.rs
|
|
82
|
+
- lib/tonic-rails.rb
|
|
83
|
+
- lib/tonic/rails/base.rb
|
|
84
|
+
- lib/tonic/rails/configuration.rb
|
|
85
|
+
- lib/tonic/rails/railtie.rb
|
|
86
|
+
- lib/tonic/rails/reflection.rb
|
|
87
|
+
- lib/tonic/rails/router.rb
|
|
88
|
+
- lib/tonic/rails/rpc_error.rb
|
|
89
|
+
- lib/tonic/rails/server.rb
|
|
90
|
+
- lib/tonic/rails/tasks/tonic-rails.rake
|
|
91
|
+
- lib/tonic/rails/testing.rb
|
|
92
|
+
- lib/tonic/rails/version.rb
|
|
93
|
+
homepage: https://github.com/CodingAnarchy/tonic-rails
|
|
94
|
+
licenses:
|
|
95
|
+
- MIT
|
|
96
|
+
metadata:
|
|
97
|
+
allowed_push_host: https://rubygems.org
|
|
98
|
+
homepage_uri: https://github.com/CodingAnarchy/tonic-rails
|
|
99
|
+
source_code_uri: https://github.com/CodingAnarchy/tonic-rails
|
|
100
|
+
bug_tracker_uri: https://github.com/CodingAnarchy/tonic-rails/issues
|
|
101
|
+
changelog_uri: https://github.com/CodingAnarchy/tonic-rails/blob/main/CHANGELOG.md
|
|
102
|
+
rubygems_mfa_required: 'true'
|
|
103
|
+
rdoc_options: []
|
|
104
|
+
require_paths:
|
|
105
|
+
- lib
|
|
106
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - ">="
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '3.3'
|
|
111
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
112
|
+
requirements:
|
|
113
|
+
- - ">="
|
|
114
|
+
- !ruby/object:Gem::Version
|
|
115
|
+
version: 3.3.11
|
|
116
|
+
requirements: []
|
|
117
|
+
rubygems_version: 3.6.9
|
|
118
|
+
specification_version: 4
|
|
119
|
+
summary: High-performance gRPC endpoints for Rails, powered by tonic via a Rust native
|
|
120
|
+
extension
|
|
121
|
+
test_files: []
|