rails_twirp 0.11.0 → 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +1 -1
- data/CHANGELOG.md +16 -0
- data/Gemfile +1 -1
- data/README.md +58 -3
- data/lib/rails_twirp/base.rb +12 -6
- data/lib/rails_twirp/engine.rb +5 -1
- data/lib/rails_twirp/exception_handling.rb +12 -2
- data/lib/rails_twirp/metal.rb +26 -0
- data/lib/rails_twirp/version.rb +1 -1
- data/lib/rails_twirp.rb +1 -0
- data/rails_twirp.gemspec +0 -1
- data/test/dummy/app/views/dummy/rpc_name_check.pb.pbbuilder +3 -1
- data/test/ping_controller_test.rb +18 -0
- metadata +5 -70
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6af836779332d2bcf1810379884b7ef9e219f63e81b2972b501a45ae1880f1cb
|
4
|
+
data.tar.gz: b0a967815e9326fa7889fdf1f3ec77b08f835921b8a919cee5cc95150407aa20
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 139e2f8517c0e1b2664b0d264d4722ebdc74fde78bfdb9724ea03d31d80ab99eec66a304a25bfaec23e17ed032b5396406742993ebabc2fc464c3eea338dd5bd
|
7
|
+
data.tar.gz: 4df604dc7f1772238a2ea95b3e573aa352b354a58a92f9caa157add1889ba59dd95d8bbab9283dbf9632b807514c28fc21eca913b03f0bb254d57eadfbef200b
|
data/.github/workflows/test.yml
CHANGED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
### 0.13.0
|
2
|
+
* Adding #controller_name methods to Metal/Base controller (used for instrumentation)
|
3
|
+
* Include `ActionController::Caching` with Base controller/helpers
|
4
|
+
|
5
|
+
|
6
|
+
### 0.12.0
|
7
|
+
|
8
|
+
* Allow a custom exception handling proc to be assigned using `RailsTwirp.unhandled_exception_handler`
|
9
|
+
|
10
|
+
### 0.11.0
|
11
|
+
|
12
|
+
* Update configuration and tests for Rails 7 compatibility
|
13
|
+
|
14
|
+
### 0.10.0
|
15
|
+
|
16
|
+
* Handle exceptions more like the Rails controllers do it, do not capture all of them as if they were Twirp exceptions
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,56 @@
|
|
1
1
|
# RailsTwirp
|
2
|
-
|
2
|
+
|
3
|
+
Allows for effortless integration of [Twirp](https://github.com/twitchtv/twirp) endpoints into your Rails application. RailsTwirp permits you to define your Twirp endpoints using the familiar vocabulary of Rails routes, controllers, actions and views.
|
4
|
+
|
5
|
+
Using an extra routes file you map your Twirp RPCs to controllers, and define your views using [pbbuilder.](https://github.com/cheddar-me/pbbuilder) Even though you are now serving Twirp RPC endpoints instead of HTML or JSON, all the skills and tools you have for working your usual Rails infrastructure apply. `TwirpRails` provides a customised controller superclass called `RailsTwirp::Base` which includes all the relevant ActionController components which still make sense when using Protobufs.
|
3
6
|
|
4
7
|
## Usage
|
5
|
-
|
8
|
+
|
9
|
+
Add the gem to your project (see "Installation" below). Then add a file called `config/twirp/routes.rb` to your application, and map your RPCs inside of that file. We will map a `GreetWorld` RPC call as an example:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
Rails.application.twirp.routes.draw do
|
13
|
+
service HelloService, module: :api do
|
14
|
+
rpc "GreetWorld", to: "greetings#greet"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
```
|
18
|
+
|
19
|
+
The `module` defines the module which will contain your `TwirpRails` controller. The `HelloService` module is your Protobuf mapping generated using Twirp, it inherits from `Twirp::Service` and could look like this:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
class HelloService < Twirp::Service
|
23
|
+
package 'api'
|
24
|
+
service 'App'
|
25
|
+
rpc :GreetWorld, GreetWorldRequest, GreetWorldResponse, :ruby_method => :greet_world
|
26
|
+
end
|
27
|
+
```
|
28
|
+
|
29
|
+
The `GreetWorldRequest` in this case is the autogenerated protobuf request class, the `GreetWorldResponse` is the response class.
|
30
|
+
|
31
|
+
Then you define your controller, which looks like a standard Rails controller - just with a different base class:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
class API::GreetingsController < RailsTwirp::Base
|
35
|
+
def greet
|
36
|
+
render pb: API::GreetResponse.new
|
37
|
+
end
|
38
|
+
end
|
39
|
+
```
|
40
|
+
|
41
|
+
Note that the controller is routed using the same naming conventions as Rails. Inside of a `TwirpRails` controller you can do most of the things you are used to from Rails controllers, such as renders and implicit renders - which have to be `pbbuilder` views. If our `GreetWorldResponse` has a `greeting` field, it can be filled inside the `pbbuilder` view like so:
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
pb.greeting "Hello stranger"
|
45
|
+
```
|
46
|
+
|
47
|
+
Inside the controllers you have access to the call parameters of your RPC call using `request`:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
def greet
|
51
|
+
request.name #=> # Returns the value of the "name" field from the request Protobuf
|
52
|
+
end
|
53
|
+
```
|
6
54
|
|
7
55
|
## Installation
|
8
56
|
Add this line to your application's Gemfile:
|
@@ -37,7 +85,14 @@ end
|
|
37
85
|
```
|
38
86
|
|
39
87
|
## Contributing
|
40
|
-
|
88
|
+
|
89
|
+
* Check out the latest `main` to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
90
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
91
|
+
* Fork the project.
|
92
|
+
* Start a feature/bugfix branch.
|
93
|
+
* Commit and push until you are happy with your contribution.
|
94
|
+
* Make sure to add tests for your change. This is important so we don't break it in a future version unintentionally.
|
95
|
+
* Please try not to mess with the Rakefile, version, or history.
|
41
96
|
|
42
97
|
## License
|
43
98
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/lib/rails_twirp/base.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "abstract_controller/base"
|
2
4
|
require "abstract_controller/rendering"
|
3
5
|
require "action_view/rendering"
|
4
6
|
require "rails_twirp/render_pb"
|
5
7
|
require "rails_twirp/errors"
|
6
8
|
require "abstract_controller/asset_paths"
|
7
|
-
require "abstract_controller/caching"
|
8
9
|
require "abstract_controller/logger"
|
9
10
|
require "abstract_controller/callbacks"
|
10
11
|
require "action_controller/metal/helpers"
|
@@ -13,11 +14,10 @@ require "rails_twirp/url_for"
|
|
13
14
|
require "rails_twirp/implicit_render"
|
14
15
|
require "rails_twirp/instrumentation"
|
15
16
|
require "rails_twirp/exception_handling"
|
17
|
+
require "rails_twirp/metal"
|
16
18
|
|
17
19
|
module RailsTwirp
|
18
|
-
class Base <
|
19
|
-
abstract!
|
20
|
-
|
20
|
+
class Base < RailsTwirp::Metal
|
21
21
|
# The order of these includes matter.
|
22
22
|
# The rendering modules extend each other, so need to be in this order.
|
23
23
|
include AbstractController::Rendering
|
@@ -26,7 +26,6 @@ module RailsTwirp
|
|
26
26
|
include ActionController::Helpers
|
27
27
|
include UrlFor
|
28
28
|
include AbstractController::AssetPaths
|
29
|
-
include AbstractController::Caching
|
30
29
|
|
31
30
|
include ActionView::Rendering
|
32
31
|
include RenderPb
|
@@ -39,7 +38,14 @@ module RailsTwirp
|
|
39
38
|
include Instrumentation
|
40
39
|
include ExceptionHandling
|
41
40
|
|
42
|
-
|
41
|
+
##
|
42
|
+
# :attr_reader: request
|
43
|
+
#
|
44
|
+
# The ActionDispatch::Request instance for the current request.
|
45
|
+
attr_internal :request
|
46
|
+
|
47
|
+
attr_internal :env, :response_class, :rpc_name
|
48
|
+
|
43
49
|
def initialize
|
44
50
|
@_request = nil
|
45
51
|
@_env = nil
|
data/lib/rails_twirp/engine.rb
CHANGED
@@ -34,7 +34,11 @@ module RailsTwirp
|
|
34
34
|
|
35
35
|
initializer "rails_twirp.helpers" do |app|
|
36
36
|
ActiveSupport.on_load(:rails_twirp) do
|
37
|
-
# Load all the application helpers into the controller
|
37
|
+
# Load all the application helpers into the controller.
|
38
|
+
# Note that helpers need to be set up here, because apparently
|
39
|
+
# the AbstractController::Helpers module won't be able to find
|
40
|
+
# the _helpers method on a reloaded controller
|
41
|
+
include ActionController::Caching
|
38
42
|
include app.routes.mounted_helpers
|
39
43
|
extend ::AbstractController::Railties::RoutesHelpers.with(app.routes, false)
|
40
44
|
extend ::ActionController::Railties::Helpers
|
@@ -9,17 +9,27 @@ module RailsTwirp
|
|
9
9
|
def process_action(*)
|
10
10
|
super
|
11
11
|
rescue Exception => e
|
12
|
+
# Only the exceptions which are not captured by ActionController-like "rescue_from" end up here.
|
13
|
+
# The idea is that any exception which is rescued by the controller is treated as part of the business
|
14
|
+
# logic, and thus taking action on it is the responsibility of the controller which uses "rescue_from".
|
15
|
+
# If an exception ends up here it means it wasn't captured by the handlers defined in the controller.
|
16
|
+
|
12
17
|
# We adopt the same error handling logic as Rails' standard middlewares:
|
13
18
|
# 1. When we 'show exceptions' we make the exception bubble up—this is useful for testing
|
19
|
+
# If the exception gets raised here error reporting will happen in the middleware of the APM package
|
20
|
+
# higher in the call stack.
|
14
21
|
raise e unless http_request.show_exceptions?
|
15
22
|
|
16
|
-
# 2.
|
23
|
+
# 2. We report the error to the error tracking service, this needs to be configured.
|
24
|
+
RailsTwirp.unhandled_exception_handler&.call(e)
|
25
|
+
|
26
|
+
# 3. When we want to show detailed exceptions we include the exception message in the error
|
17
27
|
if http_request.get_header("action_dispatch.show_detailed_exceptions")
|
18
28
|
self.response_body = Twirp::Error.internal_with(e)
|
19
29
|
return
|
20
30
|
end
|
21
31
|
|
22
|
-
#
|
32
|
+
# 4. Otherwise we just return a vague internal error message
|
23
33
|
self.response_body = Twirp::Error.internal("Internal error")
|
24
34
|
end
|
25
35
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsTwirp
|
4
|
+
# This is a simplest possible controller, providing a valid
|
5
|
+
# Rack interfacee without the additional niceties provided by RailsTwirp.
|
6
|
+
#
|
7
|
+
# Idiologially, it's similar to Rails version of ActionController::Metal
|
8
|
+
class Metal < AbstractController::Base
|
9
|
+
abstract!
|
10
|
+
|
11
|
+
# Returns the last part of the controller's name, underscored, without the ending
|
12
|
+
# <tt>Controller</tt>. For instance, PostsController returns <tt>posts</tt>.
|
13
|
+
# Namespaces are left out, so Admin::PostsController returns <tt>posts</tt> as well.
|
14
|
+
#
|
15
|
+
# ==== Returns
|
16
|
+
# * <tt>string</tt>
|
17
|
+
def self.controller_name
|
18
|
+
@controller_name ||= (name.demodulize.delete_suffix("Controller").underscore unless anonymous?)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Delegates to the class's ::controller_name.
|
22
|
+
def controller_name
|
23
|
+
self.class.controller_name
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/rails_twirp/version.rb
CHANGED
data/lib/rails_twirp.rb
CHANGED
data/rails_twirp.gemspec
CHANGED
@@ -66,6 +66,24 @@ class PingControllerTest < RailsTwirp::IntegrationTest
|
|
66
66
|
Rails.application.env_config["action_dispatch.show_exceptions"] = false
|
67
67
|
end
|
68
68
|
|
69
|
+
test "uncaught errors should be fanned out to the exception handler proc if one is defined" do
|
70
|
+
Rails.application.env_config["action_dispatch.show_exceptions"] = true
|
71
|
+
|
72
|
+
captured_exception = nil
|
73
|
+
RailsTwirp.unhandled_exception_handler = ->(e) { captured_exception = e }
|
74
|
+
|
75
|
+
req = RPC::DummyAPI::PingRequest.new
|
76
|
+
rpc RPC::DummyAPI::DummyService, "UncaughtError", req
|
77
|
+
assert_instance_of Twirp::Error, response
|
78
|
+
assert_equal :internal, response.code
|
79
|
+
assert_equal "Uncaught", response.msg
|
80
|
+
assert_equal "StandardError", response.meta["cause"]
|
81
|
+
assert_kind_of StandardError, captured_exception
|
82
|
+
ensure
|
83
|
+
RailsTwirp.unhandled_exception_handler = nil
|
84
|
+
Rails.application.env_config["action_dispatch.show_exceptions"] = false
|
85
|
+
end
|
86
|
+
|
69
87
|
test "uncaught errors should return an internal error without if show_exceptions is true and show_detailed_exceptions is false" do
|
70
88
|
Rails.application.env_config["action_dispatch.show_exceptions"] = true
|
71
89
|
Rails.application.env_config["action_dispatch.show_detailed_exceptions"] = false
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails_twirp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.13.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bouke van der Bijl
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-01-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -56,6 +56,7 @@ files:
|
|
56
56
|
- ".gitignore"
|
57
57
|
- ".ruby-version"
|
58
58
|
- ".standard.yml"
|
59
|
+
- CHANGELOG.md
|
59
60
|
- Gemfile
|
60
61
|
- MIT-LICENSE
|
61
62
|
- README.md
|
@@ -72,6 +73,7 @@ files:
|
|
72
73
|
- lib/rails_twirp/instrumentation.rb
|
73
74
|
- lib/rails_twirp/log_subscriber.rb
|
74
75
|
- lib/rails_twirp/mapper.rb
|
76
|
+
- lib/rails_twirp/metal.rb
|
75
77
|
- lib/rails_twirp/render_pb.rb
|
76
78
|
- lib/rails_twirp/rescue.rb
|
77
79
|
- lib/rails_twirp/route_set.rb
|
@@ -169,71 +171,4 @@ rubygems_version: 3.2.32
|
|
169
171
|
signing_key:
|
170
172
|
specification_version: 4
|
171
173
|
summary: Integrate Twirp into Rails
|
172
|
-
test_files:
|
173
|
-
- test/dummy/Rakefile
|
174
|
-
- test/dummy/app/assets/config/manifest.js
|
175
|
-
- test/dummy/app/assets/images/.keep
|
176
|
-
- test/dummy/app/assets/stylesheets/application.css
|
177
|
-
- test/dummy/app/channels/application_cable/channel.rb
|
178
|
-
- test/dummy/app/channels/application_cable/connection.rb
|
179
|
-
- test/dummy/app/controllers/application_controller.rb
|
180
|
-
- test/dummy/app/controllers/application_twirp_controller.rb
|
181
|
-
- test/dummy/app/controllers/concerns/.keep
|
182
|
-
- test/dummy/app/controllers/dummy_controller.rb
|
183
|
-
- test/dummy/app/controllers/pings_controller.rb
|
184
|
-
- test/dummy/app/controllers/testmod/nested/other_controller.rb
|
185
|
-
- test/dummy/app/helpers/application_helper.rb
|
186
|
-
- test/dummy/app/helpers/random_helper.rb
|
187
|
-
- test/dummy/app/javascript/packs/application.js
|
188
|
-
- test/dummy/app/jobs/application_job.rb
|
189
|
-
- test/dummy/app/mailers/application_mailer.rb
|
190
|
-
- test/dummy/app/models/application_record.rb
|
191
|
-
- test/dummy/app/models/concerns/.keep
|
192
|
-
- test/dummy/app/views/dummy/rpc_name_check.pb.pbbuilder
|
193
|
-
- test/dummy/app/views/layouts/application.html.erb
|
194
|
-
- test/dummy/app/views/layouts/mailer.html.erb
|
195
|
-
- test/dummy/app/views/layouts/mailer.text.erb
|
196
|
-
- test/dummy/app/views/pings/ping_template.pb.pbbuilder
|
197
|
-
- test/dummy/bin/generate
|
198
|
-
- test/dummy/bin/rails
|
199
|
-
- test/dummy/bin/rake
|
200
|
-
- test/dummy/bin/setup
|
201
|
-
- test/dummy/config.ru
|
202
|
-
- test/dummy/config/application.rb
|
203
|
-
- test/dummy/config/boot.rb
|
204
|
-
- test/dummy/config/cable.yml
|
205
|
-
- test/dummy/config/database.yml
|
206
|
-
- test/dummy/config/environment.rb
|
207
|
-
- test/dummy/config/environments/development.rb
|
208
|
-
- test/dummy/config/environments/production.rb
|
209
|
-
- test/dummy/config/environments/test.rb
|
210
|
-
- test/dummy/config/initializers/application_controller_renderer.rb
|
211
|
-
- test/dummy/config/initializers/backtrace_silencers.rb
|
212
|
-
- test/dummy/config/initializers/content_security_policy.rb
|
213
|
-
- test/dummy/config/initializers/cookies_serializer.rb
|
214
|
-
- test/dummy/config/initializers/filter_parameter_logging.rb
|
215
|
-
- test/dummy/config/initializers/inflections.rb
|
216
|
-
- test/dummy/config/initializers/mime_types.rb
|
217
|
-
- test/dummy/config/initializers/permissions_policy.rb
|
218
|
-
- test/dummy/config/initializers/wrap_parameters.rb
|
219
|
-
- test/dummy/config/locales/en.yml
|
220
|
-
- test/dummy/config/puma.rb
|
221
|
-
- test/dummy/config/routes.rb
|
222
|
-
- test/dummy/config/storage.yml
|
223
|
-
- test/dummy/config/twirp/routes.rb
|
224
|
-
- test/dummy/lib/assets/.keep
|
225
|
-
- test/dummy/log/.keep
|
226
|
-
- test/dummy/proto/api.proto
|
227
|
-
- test/dummy/proto/api_pb.rb
|
228
|
-
- test/dummy/proto/api_twirp.rb
|
229
|
-
- test/dummy/public/404.html
|
230
|
-
- test/dummy/public/422.html
|
231
|
-
- test/dummy/public/500.html
|
232
|
-
- test/dummy/public/apple-touch-icon-precomposed.png
|
233
|
-
- test/dummy/public/apple-touch-icon.png
|
234
|
-
- test/dummy/public/favicon.ico
|
235
|
-
- test/dummy_test.rb
|
236
|
-
- test/other_controller_test.rb
|
237
|
-
- test/ping_controller_test.rb
|
238
|
-
- test/rails_twirp_test.rb
|
239
|
-
- test/test_helper.rb
|
174
|
+
test_files: []
|