connectrpc-on-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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: bf9035d7ed20c0a0fa34ca686bfebec9849f32a2826905050af3912ffd5ec8f1
4
+ data.tar.gz: 5458391b5e2eceb1238a7caaf27b5b92d1f16857cbccb83d882925adae706297
5
+ SHA512:
6
+ metadata.gz: a790bb7f79b68215639bc012e66c26472505454cad2e30ec171bf346eb1163b700c3627135bb67d4fe5013dffd1bb41fa65cebce06f1dfcff82ee6b75589c113
7
+ data.tar.gz: 5ed934edc8ce7a0cc60a3b93d5299a05efc25c6fe11589c9180bbc1c03b9f0c0b694d1f7781483193172ec6906fd68ca1175ad33737143c85326448afdecb6f7
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jim Myhrberg
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,256 @@
1
+ # connectrpc-on-rails
2
+
3
+ Optional native Rails controller execution for the experimental, unofficial
4
+ ConnectRPC unary Ruby gem. The core gem remains independent of Rails and has
5
+ zero runtime dependencies. This adapter reuses its Server invoker and the
6
+ optional [connectrpc-rack](../connectrpc-rack/README.md) translation. It does not add another protocol implementation.
7
+
8
+ See the [supported 0.x contract](../../docs/support.md) for the target support
9
+ profile, matched package versions, compatibility policy and adoption gates.
10
+
11
+ ## Use a controller
12
+
13
+ Add `connectrpc-on-rails` and your chosen protobuf binding dependency to your
14
+ application Gemfile. Runtime dependencies are `connectrpc ~> 0.1.0`,
15
+ `connectrpc-rack ~> 0.1.0`, and `actionpack >= 8.0.5.1, < 8.2`.
16
+ The Rack package supplies the `rack >= 3.2.7, < 4` requirement. The application
17
+ chooses its controller base, binding, authentication, and security policy.
18
+ Requiring `connectrpc/on_rails` loads neither `ActionController::Base` nor
19
+ `ActionController::API`, and does not load Google protobuf. `Bundler.require`
20
+ loads the same entry point through `connectrpc-on-rails`.
21
+
22
+ Generate official protobuf messages and Connect service glue as described in
23
+ the [root README](../../README.md). Keep generated files outside Zeitwerk's
24
+ managed paths. For example, use `generated/rpc`, add that directory to
25
+ `$LOAD_PATH`, and require `echo_connect` from an initializer. If placing generated
26
+ files under `lib`, explicitly ignore their directory in the application's
27
+ Zeitwerk configuration before eager loading. Generated filenames do not match
28
+ the constants they define, generated glue requires its message files, and it
29
+ intentionally rejects `load`/redefinition. These files are non-reloadable.
30
+ Ordinary application controllers retain normal Rails reload behavior.
31
+
32
+ For the repository's generated `Example::V1::EchoService` fixture:
33
+
34
+ ```ruby
35
+ require "connectrpc/on_rails"
36
+ require "connectrpc/bindings/google_protobuf"
37
+ require "echo_connect"
38
+
39
+ class EchoController < ApplicationController
40
+ include ConnectRPC::OnRails::Controller
41
+
42
+ private
43
+
44
+ def connect_service = Example::V1::EchoService::SERVICE
45
+ def connect_binding = ConnectRPC::Bindings::GoogleProtobuf.new
46
+ def connect_handlers
47
+ Example::V1::EchoService.handlers(echo: method(:rpc_echo), fail: method(:rpc_fail))
48
+ end
49
+ def connect_limits = { request_limit: 1_048_576 }
50
+
51
+ def rpc_echo(message, context)
52
+ context.response_metadata.add("x-worker", "rails")
53
+ context.response_trailers.add("x-finished", "yes")
54
+ message
55
+ end
56
+
57
+ def rpc_fail(_message, _context)
58
+ raise ConnectRPC::Error.new(:invalid_argument, "safe application message")
59
+ end
60
+ end
61
+ ```
62
+
63
+ Use normal named controller routing, optionally under a literal scope:
64
+
65
+ ```ruby
66
+ scope "/rpc" do
67
+ match "/example.v1.EchoService(/*rpc)", to: "echo#connect", via: :all, format: false
68
+ end
69
+ ```
70
+
71
+ `via: :all` lets core return the protocol's unsupported-verb response.
72
+ `format: false` preserves dots in service names. Dynamic scope segments are
73
+ not supported. Route/service mismatches raise a configuration error.
74
+ The adapter strips only the matched route's literal prefix. Unknown RPC names,
75
+ deeper suffixes, method case and raw escapes reach core unchanged after that
76
+ boundary. Rails itself normalizes duplicate/trailing slashes and escape case
77
+ before controller dispatch, so path acceptance differs from a direct Rack mount.
78
+
79
+ Only `connect` is an action supplied by the concern. Keep the three required
80
+ configuration hooks and typed handlers private, using prefixed handler names to
81
+ avoid controller/Kernel method collisions. `connect_limits` is optional and
82
+ accepts core `request_limit:`, `response_limit:`, `request_wire_limit:`,
83
+ `response_wire_limit:`, `get_query_limit:` and `encoding:` keywords; omitted options
84
+ use core's 4 MiB defaults. Private `connect_request`, `connect_context`, and
85
+ `connect_method` expose the same typed objects to callbacks and handlers.
86
+ Handlers return typed messages. There is no render helper or routing DSL.
87
+
88
+ ## Lifecycle and errors
89
+
90
+ Core negotiates, validates deadlines and bounds, reads, and decodes before
91
+ running native Rails callbacks. Authentication callbacks therefore run after
92
+ up to 4 MiB of input has been decoded by default. Preflight failures do not run
93
+ controller callbacks or emit `process_action.action_controller` events.
94
+ Callbacks, `rescue_from`, around-action cleanup and instrumentation use Rails'
95
+ real `process_action` chain. Native completion occurs before core serializes
96
+ the typed result. Configuration, handlers, server and context are evaluated
97
+ fresh for each request. Synchronize any application-owned shared state.
98
+
99
+ Raise `ConnectRPC::Error` to reject a request or map an application exception.
100
+ Its deliberate code, safe message, typed details, metadata and trailers survive.
101
+ If the application has a catch-all rescue, declare a reraising handler after
102
+ it because native Rails rescue precedence favors later declarations:
103
+
104
+ ```ruby
105
+ rescue_from StandardError, with: :handle_application_error
106
+ rescue_from ConnectRPC::Error do |error|
107
+ raise error
108
+ end
109
+ ```
110
+
111
+ Raw native render/head halts and exceptions covered by Rails'
112
+ `rescue_responses` use only the unambiguous inverse of Connect HTTP statuses:
113
+ 401/403/404/429/499/501/503/504 become unauthenticated/permission_denied/
114
+ not_found/resource_exhausted/canceled/unimplemented/unavailable/
115
+ deadline_exceeded. Messages are generic. Ambiguous 400/409/500, redirects,
116
+ unmapped statuses, and success without a typed result become `internal`.
117
+ A non-200 native halt after the handler also prevents typed success. Raw Rails
118
+ bodies and exception messages never become protocol error messages.
119
+ Exceptions explicitly registered in Rails' `rescue_responses` remain expected
120
+ outcomes and do not produce adapter error reports or exception logs. Unexpected
121
+ exceptions are logged with their original backtraces and reported through
122
+ `Rails.error` before safe core normalization. Request preparation uses the same
123
+ classification, including application overrides to `rescue_responses`.
124
+
125
+ Controller instrumentation describes controller execution: a deliberate Connect
126
+ exception can appear as a Rails 500, while successful controller execution can
127
+ precede a core encode or response-limit failure. Deadlines remain cooperative;
128
+ they cannot interrupt arbitrary application code or blocked synchronous reads.
129
+ Warden/Devise throws and failure-app responses remain host-owned non-Connect
130
+ responses unless the application explicitly adapts them.
131
+
132
+ ## Request and security policy
133
+
134
+ During Connect execution, Rails receives an empty request-local indifferent body
135
+ parameter cache and an explicit JSON format. This prevents ParamsWrapper and
136
+ instrumentation from rereading ProtoJSON scalar/array/object roots or parsing
137
+ malformed Accept headers. Use typed `connect_request` for message fields;
138
+ query and route parameters remain accessible by string and symbol. Ordinary
139
+ non-Connect actions retain normal body parsing and format negotiation.
140
+
141
+ The adapter does not skip CSRF protection, disable sessions, or alter cookie
142
+ policy. A dedicated API controller suits token-authenticated RPC. Base
143
+ controllers use Rails' native safe-verb policy: GET does not require a CSRF
144
+ token; POST retains its configured token/session checks. The application must
145
+ explicitly route desired GET calls and preserve authentication/authorization. An
146
+ application may choose a narrowly scoped exemption according to its security
147
+ model; inclusion alone grants none.
148
+
149
+ Final core status, wire content type and emitted headers replace the interim
150
+ Rails representation. Stale content-length, content-encoding, ETag,
151
+ Last-Modified and Location are removed. Other host headers, default security
152
+ headers, CSP, authentication headers and cookies survive; repeated Rack 3
153
+ values remain arrays. Downstream Rails/Rack middleware retains its normal
154
+ ownership of request ids, ETags, content length and other final host behavior.
155
+
156
+ ## Verification and tested combinations
157
+
158
+ The target supported Rails profile is 8.1. Rails 8.0 remains compatibility
159
+ evidence outside that profile. The tested combinations use Linux x64 and
160
+ Bundler 4.0.23:
161
+
162
+ | Rails | Ruby | Target profile status |
163
+ | --- | --- | --- |
164
+ | 8.1.4 | 3.4.12 and 4.0.9 | Supported framework profile |
165
+ | 8.0.5.1 | 3.4.12 | Compatibility evidence only |
166
+
167
+ These rows do not promise every framework/runtime combination. As checked on
168
+ 2026-11-02, Rails'
169
+ [maintenance policy](https://rubyonrails.org/maintenance) lists security support
170
+ ending 2026-11-07 for 8.0 and 2027-10-10 for 8.1. The 8.0 compatibility lane
171
+ is not a promise to extend upstream maintenance.
172
+
173
+ From the repository root:
174
+
175
+ ```sh
176
+ mise tasks
177
+ RAILS_LANE=8.0 mise run test:rails
178
+ RAILS_LANE=8.1 mise run verify:rails
179
+ MISE_RUBY_VERSION=4.0.9 RAILS_LANE=8.1 mise run test:rails
180
+ mise run generation:check:rails
181
+ mise run check
182
+ mise run verify
183
+ ```
184
+
185
+ `setup:rails`, `test:rails`, `generation:check:rails`, `build:rails`,
186
+ `check:package:rails`, and `verify:rails` select `RAILS_LANE=8.1` by default.
187
+ Tests accept RSpec paths/options relative to this adapter directory.
188
+ Actual Appraisal 2.5.0 runs under the selected frozen lane bundle, with no extra
189
+ base lock. `BUNDLE_COOLDOWN=7` applies to every adapter bundle process; the plain
190
+ Gemfile source is necessary because Appraisal cannot parse source keywords.
191
+ Pinned Bundler must exist before Appraisal can run, preventing its legacy
192
+ installer fallback. Never use `appraisal install` in CI. Generation is checked
193
+ in a temporary relative-layout copy, without rewriting tracked files.
194
+
195
+ Both lane locks include checksums and `ruby`/`x86_64-linux` platforms. Native
196
+ variants recorded by Bundler remain checksum-locked. New resolved artifacts
197
+ require a primary registry release-age and archive SHA256 audit. Runtime cache
198
+ uses root `vendor/bundle` by ABI; adapter artifacts and coverage live under
199
+ `vendor/rails`. SimpleCov starts before adapter requires and includes every
200
+ adapter library file, independently of core coverage.
201
+
202
+ `check:package:rails` builds all three core/Rack/Rails archives, checks exact manifests and metadata,
203
+ and installs them with conventional offline dependency resolution into fresh
204
+ GEM_HOME/GEM_PATH outside the checkout. Installed generated clients perform
205
+ binary/JSON success and structured error calls through a real Rails application
206
+ and Rack::Lint, loading integration code only from the installed archive.
207
+ See [development](../../docs/development.md) for the separate core protocol,
208
+ generation, Go interoperability and official conformance evidence.
209
+
210
+ ## Status and licensing
211
+
212
+ Experimental and not production ready. Streaming, native gRPC and production
213
+ hosting are outside this adapter. Explicit binary/text unary gRPC-Web uses the
214
+ private hooks described below.
215
+ Original project work is [MIT licensed](LICENSE), copyright 2026 Jim Myhrberg.
216
+ Both gem archives include the complete license and pin `allowed_push_host` to
217
+ `https://rubygems.org`; only the [release workflow](../../docs/releasing.md)
218
+ publishes them. Verification authorizes no publication.
219
+
220
+
221
+ To enable unary gzip, return `encoding: :gzip` from the private `connect_limits`
222
+ hook. Core validates wire length, inflates under the decoded request limit, and
223
+ decodes before callbacks and native instrumentation. JSON scalar bodies still
224
+ bypass Rails body-parameter parsing. Negotiated success responses are compressed
225
+ after native execution; errors remain identity. A compression/size failure after
226
+ execution does not rerun callbacks, and native process-action instrumentation can
227
+ record 200 before core replaces the response with its final error. Wire limits
228
+ default to the corresponding message limits; gzip framing overhead must fit too.
229
+
230
+ ## RPC observation
231
+
232
+ Configure `instrumentation:` on the core Client/Server to enable the fixed
233
+ neutral per-call lifecycle or the optional [OpenTelemetry adapter](../connectrpc-otel/README.md).
234
+ Native controllers override private `connect_instrumentation` (default nil),
235
+ separately from `connect_limits`. Escaping original failures are captured before
236
+ reporting/normalization; successfully handled `rescue_from` exceptions are not
237
+ automatically failures. Configuration before Server construction is outside it.
238
+
239
+ ## Static signatures
240
+
241
+ This package owns `sig/` and its discovery manifest. Add
242
+ `library "connectrpc-on-rails"` to a strict Steep target with this gem installed.
243
+ See the [RBS guide](../../docs/rbs.md) for supported APIs, extension contracts,
244
+ partial records, runtime validation and framework-signature limits.
245
+ `mise run rbs:check` checks application callers for this package; strict core
246
+ source checking is a separate target from backend implementation checking.
247
+
248
+ The finite upstream compatibility signatures in `rbs/action_controller_8` are explicit
249
+ opt-ins, separate from normal gem discovery. See the [RBS guide](../../docs/rbs.md#opt-in-framework-compatibility)
250
+ for tested versions, supported framework forms and how to avoid duplicate
251
+ application-owned declarations.
252
+
253
+ ## gRPC-Web
254
+
255
+ Private connect_protocol, connect_status_codec and connect_cors hooks enable binary/text unary gRPC-Web through the real controller invoker. POST/OPTIONS routes are explicit; auth, callbacks, cookies, security headers and Rails CSRF/origin controls remain active.
256
+ See the [Web guide](../../docs/grpc-web.md) for protocol, CORS, bounds and completion rules.
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConnectRPC
4
+ module OnRails
5
+ module Controller # rubocop:disable Metrics/ModuleLength
6
+ extend ActiveSupport::Concern
7
+
8
+ # Only this method is a Rails action. The generated handler map stays private.
9
+ def connect
10
+ handler = @connect_handlers[connect_method.path]
11
+ raise Error.new(:unimplemented, "Unknown method") unless handler
12
+
13
+ @connect_result = handler.call(connect_request, connect_context)
14
+ @connect_result_set = true
15
+ self.response_body = ""
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :connect_request, :connect_context, :connect_method
21
+
22
+ def connect_service = raise NotImplementedError, "Implement private connect_service"
23
+ def connect_binding = raise NotImplementedError, "Implement private connect_binding"
24
+ def connect_handlers = raise NotImplementedError, "Implement private connect_handlers"
25
+ def connect_limits = {}
26
+ def connect_instrumentation = nil
27
+ def connect_protocol = :connect
28
+ def connect_status_codec = nil
29
+ def connect_cors = nil
30
+
31
+ def process_action(name, ...)
32
+ return super unless name.to_s == "connect"
33
+
34
+ @connect_handlers = connect_handlers
35
+ connect_dispatch do |rpc_method, message, context|
36
+ @connect_method = rpc_method
37
+ @connect_request = message
38
+ @connect_context = context
39
+ connect_run_lifecycle { super(name, ...) }
40
+ end
41
+ end
42
+
43
+ def connect_dispatch(&invoker)
44
+ service = connect_service
45
+ env = connect_env(service)
46
+ server = Server.new(services: [service], binding: connect_binding, handlers: @connect_handlers,
47
+ invoker: invoker, instrumentation: connect_instrumentation,
48
+ protocol: connect_protocol, status_codec: connect_status_codec, cors: connect_cors,
49
+ **connect_limits)
50
+ ConnectRPC::Rack.new(server: server).__send__(:transfer_response, env) do |result, cors_owned|
51
+ connect_apply_response(result, cors_owned: cors_owned)
52
+ end
53
+ end
54
+
55
+ def connect_env(service)
56
+ prefix = connect_route_prefix(service)
57
+ raw_path = request.get_header("PATH_INFO")
58
+ boundary = "#{prefix}/#{service.name}"
59
+ unless raw_path == boundary || raw_path.start_with?("#{boundary}/")
60
+ raise ArgumentError, "Connect route does not match the configured service"
61
+ end
62
+
63
+ request.env.merge("PATH_INFO" => raw_path.delete_prefix(prefix))
64
+ end
65
+
66
+ def connect_route_prefix(service)
67
+ # Use the matched route's literal prefix, never decoded wildcard parameters.
68
+ pattern = request.route_uri_pattern.to_s
69
+ match = %r{\A([^:*()]*)/#{Regexp.escape(service.name)}(?=/|\(|\z)}.match(pattern)
70
+ raise ArgumentError, "Connect route must name the configured service after a literal scope" unless match
71
+
72
+ match[1]
73
+ end
74
+
75
+ def connect_prepare_request
76
+ request.request_parameters = ActiveSupport::HashWithIndifferentAccess.new
77
+ request.formats = [:json]
78
+ end
79
+
80
+ def connect_run_lifecycle # rubocop:disable Metrics/AbcSize
81
+ connect_prepare_request
82
+ yield
83
+ raise connect_status_error(response.status) unless @connect_result_set && response.status == 200
84
+
85
+ @connect_result
86
+ rescue Error
87
+ raise
88
+ rescue StandardError => e
89
+ connect_context.record_failure(e)
90
+ connect_report(e)
91
+ raise connect_status_error(ActionDispatch::ExceptionWrapper.status_code_for_exception(e.class.name))
92
+ end
93
+
94
+ def connect_report(error)
95
+ return if ActionDispatch::ExceptionWrapper.rescue_responses.key?(error.class.name)
96
+
97
+ logger&.error { "[connectrpc.rails] #{error.full_message(highlight: false)}" }
98
+ return unless defined?(::Rails) && ::Rails.respond_to?(:error)
99
+
100
+ ::Rails.error.report(error, handled: false, source: "connectrpc.rails")
101
+ end
102
+
103
+ def connect_status_error(status)
104
+ # HTTP_FALLBACK is a client proxy mapping and is deliberately unsuitable here.
105
+ codes = Error::HTTP_STATUS.select { |_code, value| value == status }.keys
106
+ code = codes.one? ? codes.first : "internal"
107
+ Error.new(code, code == "internal" ? "Internal error" : code.tr("_", " ").capitalize)
108
+ end
109
+
110
+ def connect_apply_response(result, cors_owned:) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
111
+ status, headers, body = result
112
+ if cors_owned && response.headers.any? do |key, values|
113
+ key.start_with?("access-control-") && Array(values) != headers[key]
114
+ end
115
+ raise Error.new(:internal, "Conflicting application CORS policy")
116
+ end
117
+
118
+ %w[content-length content-encoding etag last-modified location].each { |key| response.delete_header(key) }
119
+ self.status = status
120
+ headers.each do |key, values|
121
+ values = connect_vary_header(values) if key == "vary"
122
+ # Rails parses Content-Type as a scalar. Repeated metadata remains Rack 3 arrays.
123
+ response.set_header(key, key == "content-type" ? values.fetch(0) : values)
124
+ end
125
+ self.response_body = body
126
+ end
127
+
128
+ def connect_vary_header(values)
129
+ native = Array(response.get_header("vary")).flat_map { |value| value.split(",").map(&:strip) }
130
+ return native if native.include?("*")
131
+
132
+ incoming = values.flat_map { |value| value.split(",").map(&:strip) }
133
+ (native + incoming).uniq(&:downcase)
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConnectRPC
4
+ module OnRails
5
+ VERSION = "0.1.0" # x-release-please-version
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_controller"
4
+ require_relative "on_rails/version"
5
+ require "connectrpc/rack"
6
+ require_relative "on_rails/controller"
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "connectrpc/on_rails"
@@ -0,0 +1,11 @@
1
+ # Opt in only when the application has no Action Controller signatures.
2
+ # Tested with actionpack 8.0.5.1 and 8.1.4. This is a nominal inheritance seam
3
+ # and the named callback form, not a declaration of the complete Rails API.
4
+ module ActionController
5
+ class API
6
+ def self.before_action: (*Symbol) -> void
7
+ end
8
+ class Base
9
+ def self.before_action: (*Symbol) -> void
10
+ end
11
+ end
@@ -0,0 +1,22 @@
1
+ module ConnectRPC
2
+ module OnRails
3
+ VERSION: String
4
+ module Controller
5
+ type limits = { ?request_limit: Integer, ?response_limit: Integer, ?request_wire_limit: Integer, ?response_wire_limit: Integer, ?encoding: :identity | :gzip, ?get_query_limit: Integer?, ?grpc_status_wire_limit: Integer, ?grpc_status_limit: Integer }
6
+ def connect: () -> String
7
+ private
8
+ def connect_service: () -> Service
9
+ def connect_binding: () -> _Binding
10
+ def connect_handlers: () -> Hash[String, ^(untyped, Context) -> untyped | _Handler[untyped, untyped]]
11
+ def connect_limits: () -> limits
12
+ def connect_protocol: () -> server_protocol_selection
13
+ def connect_status_codec: () -> _GRPCStatusCodec?
14
+ def connect_cors: () -> GRPCWebCORS?
15
+ def connect_instrumentation: () -> Instrumentation?
16
+ # Outside active decoded execution these readers have no established value.
17
+ attr_reader connect_request: Object?
18
+ attr_reader connect_context: Context?
19
+ attr_reader connect_method: Method[untyped, untyped]?
20
+ end
21
+ end
22
+ end
data/sig/manifest.yaml ADDED
@@ -0,0 +1,3 @@
1
+ dependencies:
2
+ - name: connectrpc
3
+ - name: connectrpc-rack
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: connectrpc-on-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - jimeh
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 2026-11-15 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: actionpack
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: 8.0.5.1
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: '8.2'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: 8.0.5.1
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: '8.2'
32
+ - !ruby/object:Gem::Dependency
33
+ name: connectrpc
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - "~>"
37
+ - !ruby/object:Gem::Version
38
+ version: 0.1.0
39
+ type: :runtime
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - "~>"
44
+ - !ruby/object:Gem::Version
45
+ version: 0.1.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: connectrpc-rack
48
+ requirement: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - "~>"
51
+ - !ruby/object:Gem::Version
52
+ version: 0.1.0
53
+ type: :runtime
54
+ prerelease: false
55
+ version_requirements: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: 0.1.0
60
+ description: Optional, experimental Rails lifecycle integration for the unofficial
61
+ ConnectRPC Ruby gem.
62
+ executables: []
63
+ extensions: []
64
+ extra_rdoc_files: []
65
+ files:
66
+ - LICENSE
67
+ - README.md
68
+ - lib/connectrpc-on-rails.rb
69
+ - lib/connectrpc/on_rails.rb
70
+ - lib/connectrpc/on_rails/controller.rb
71
+ - lib/connectrpc/on_rails/version.rb
72
+ - rbs/action_controller_8/controller.rbs
73
+ - sig/connectrpc-on-rails.rbs
74
+ - sig/manifest.yaml
75
+ homepage: https://github.com/jimeh/connectrpc-ruby
76
+ licenses:
77
+ - MIT
78
+ metadata:
79
+ allowed_push_host: https://rubygems.org
80
+ rubygems_mfa_required: 'true'
81
+ source_code_uri: https://github.com/jimeh/connectrpc-ruby
82
+ rdoc_options: []
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '3.4'
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ requirements: []
96
+ rubygems_version: 3.6.9
97
+ specification_version: 4
98
+ summary: Native Rails controller execution for Connect unary RPCs
99
+ test_files: []