rails_twirp 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/.gitignore +13 -0
- data/Gemfile +8 -0
- data/MIT-LICENSE +20 -0
- data/README.md +28 -0
- data/Rakefile +11 -0
- data/bin/test +5 -0
- data/lib/commands/twirp/routes_command.rb +26 -0
- data/lib/rails_twirp.rb +11 -0
- data/lib/rails_twirp/application.rb +9 -0
- data/lib/rails_twirp/base.rb +50 -0
- data/lib/rails_twirp/engine.rb +63 -0
- data/lib/rails_twirp/route_set.rb +113 -0
- data/lib/rails_twirp/testing/integration_test.rb +43 -0
- data/lib/rails_twirp/version.rb +3 -0
- data/rails_twirp.gemspec +17 -0
- data/test/rails_twirp_test.rb +7 -0
- data/test/test_helper.rb +7 -0
- metadata +90 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: fe63b82d059ab115dd9fdb2ad342fa6e71264a519abac81ffb68b361ed908209
|
4
|
+
data.tar.gz: '049a00e7d8928a0df13e012613840d21ac722693125440070261a2ad07d7e1b0'
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 919cede079ec3bf55898d5014d22dc3324379d9b793a7cfc3ef35d769a79a2458abe56f9971496ff2ec3c0d624967f51c6c50b0f501009cf0f76a5c9167fecd8
|
7
|
+
data.tar.gz: a7845a591ced9a9afe3dca8f0cd02babb8ed80142be3ddc7a0d1afd152a230a9dc9b1a6e8c847fb0095fa1665ea5647c89232f9810b83aa06178ab29a453b24b
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2021 Bouke van der Bijl
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# RailsTwirp
|
2
|
+
Short description and motivation.
|
3
|
+
|
4
|
+
## Usage
|
5
|
+
How to use my plugin.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'rails_twirp'
|
12
|
+
```
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
```bash
|
16
|
+
$ bundle
|
17
|
+
```
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
```bash
|
21
|
+
$ gem install rails_twirp
|
22
|
+
```
|
23
|
+
|
24
|
+
## Contributing
|
25
|
+
Contribution directions go here.
|
26
|
+
|
27
|
+
## License
|
28
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/test
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
module RailsTwirp
|
2
|
+
module Command
|
3
|
+
class RoutesCommand < Rails::Command::Base
|
4
|
+
namespace "twirp"
|
5
|
+
|
6
|
+
desc "routes", "Show Twirp routes"
|
7
|
+
def perform
|
8
|
+
require_application_and_environment!
|
9
|
+
lines = [["Method", "Controller#Action"]]
|
10
|
+
|
11
|
+
Rails.application.twirp.routes.services.each do |svc, route_set|
|
12
|
+
route_set.rpcs.each do |name, mapping|
|
13
|
+
lines << ["/#{svc.service_full_name}/#{name}", mapping.to_s]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
first_width = lines.map { |line| line[0].length }.max
|
18
|
+
second_width = lines.map { |line| line[1].length }.max
|
19
|
+
|
20
|
+
lines.each do |(first, second)|
|
21
|
+
say "#{first.rjust(first_width)} #{second.ljust(second_width)}\n"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/rails_twirp.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
module RailsTwirp
|
2
|
+
class Base < AbstractController::Base
|
3
|
+
abstract!
|
4
|
+
|
5
|
+
include AbstractController::Logger
|
6
|
+
include AbstractController::AssetPaths
|
7
|
+
include AbstractController::Callbacks
|
8
|
+
include AbstractController::Caching
|
9
|
+
include AbstractController::Rendering
|
10
|
+
include ActionView::Rendering
|
11
|
+
|
12
|
+
attr_internal :request, :env, :response_class
|
13
|
+
def initialize
|
14
|
+
@_request = nil
|
15
|
+
@_env = nil
|
16
|
+
@_response_class = nil
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
def http_request
|
21
|
+
@_http_request ||= ActionDispatch::Request.new(env[:rack_env])
|
22
|
+
end
|
23
|
+
|
24
|
+
def dispatch(action, request, response_class, env = {})
|
25
|
+
self.request = request
|
26
|
+
self.env = env
|
27
|
+
self.response_class = response_class
|
28
|
+
|
29
|
+
http_request.controller_instance = self
|
30
|
+
|
31
|
+
process(action)
|
32
|
+
|
33
|
+
# Implicit render
|
34
|
+
self.response_body = render unless response_body
|
35
|
+
response_body
|
36
|
+
end
|
37
|
+
|
38
|
+
def render(*args)
|
39
|
+
options = {formats: :pb, handlers: :pbbuilder, locals: {response_class: response_class}}
|
40
|
+
options.deep_merge! args.extract_options!
|
41
|
+
super(*args, options)
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.dispatch(action, request, response_class, env = {})
|
45
|
+
new.dispatch(action, request, response_class, env)
|
46
|
+
end
|
47
|
+
|
48
|
+
ActiveSupport.run_load_hooks(:rails_twirp, self)
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require "rails/railtie"
|
2
|
+
require "rails_twirp/application"
|
3
|
+
|
4
|
+
module RailsTwirp
|
5
|
+
# Even though this is an engine, we don't inherit from Rails::Engine because we don't want anything it provides.
|
6
|
+
class Engine < ::Rails::Railtie
|
7
|
+
# Implement Rack API
|
8
|
+
delegate :call, to: :routes
|
9
|
+
|
10
|
+
module TwirpValue
|
11
|
+
def twirp
|
12
|
+
@twirp ||= Application.new
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
initializer "rails_twirp.logger" do
|
17
|
+
# This hook is called whenever a RailsTwirp::Base is initialized, and it sets the logger
|
18
|
+
ActiveSupport.on_load(:rails_twirp) { self.logger ||= Rails.logger }
|
19
|
+
end
|
20
|
+
|
21
|
+
initializer :add_paths, before: :bootstrap_hook do |app|
|
22
|
+
app.config.paths.add "config/twirp/routes.rb"
|
23
|
+
app.config.paths.add "config/twirp/routes", glob: "**/*.rb"
|
24
|
+
app.config.paths.add "app/twirp/controllers", eager_load: true
|
25
|
+
app.config.paths.add "app/twirp/proto", load_path: true
|
26
|
+
app.config.paths.add "app/twirp/views", load_path: true
|
27
|
+
end
|
28
|
+
|
29
|
+
initializer :set_controller_view_path do
|
30
|
+
ActiveSupport.on_load(:rails_twirp) { prepend_view_path "app/twirp/views" }
|
31
|
+
end
|
32
|
+
|
33
|
+
initializer :add_twirp do |app|
|
34
|
+
# Here we add the 'twirp' method to application, which is accessible at Rails.application.twirp
|
35
|
+
app.extend TwirpValue
|
36
|
+
end
|
37
|
+
|
38
|
+
initializer :load_twirp_routes do |app|
|
39
|
+
# Load route files
|
40
|
+
route_configs = [
|
41
|
+
*app.config.paths["config/twirp/routes.rb"].existent,
|
42
|
+
*app.config.paths["config/twirp/routes"].existent
|
43
|
+
]
|
44
|
+
load(*route_configs)
|
45
|
+
|
46
|
+
# Create a router that knows how to route all the registered services
|
47
|
+
services = app.twirp.routes.to_services
|
48
|
+
routes.draw do
|
49
|
+
services.each do |service|
|
50
|
+
mount service => service.full_name
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
initializer :set_test_app do |app|
|
56
|
+
RailsTwirp.test_app = app
|
57
|
+
end
|
58
|
+
|
59
|
+
def routes
|
60
|
+
@routes ||= ActionDispatch::Routing::RouteSet.new
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# Most of this logic is stolen from Rails ActionDispatch::Routing::RouteSet
|
2
|
+
|
3
|
+
module RailsTwirp
|
4
|
+
class RouteSet
|
5
|
+
attr_reader :services
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
# Make services a hash with a default_proc, so the same class gets reused if the service
|
9
|
+
# method is used multiple times with the same key.
|
10
|
+
# This makes it possible to split up the routes into multiple files.
|
11
|
+
@services = Hash.new { |hash, key| hash[key] = ServiceRouteSet.new(key) }
|
12
|
+
end
|
13
|
+
|
14
|
+
def draw(&block)
|
15
|
+
mapper = Mapper.new(self)
|
16
|
+
mapper.instance_exec(&block)
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_services
|
20
|
+
services.each_value.map(&:to_service)
|
21
|
+
end
|
22
|
+
|
23
|
+
class ServiceRouteSet
|
24
|
+
attr_reader :rpcs
|
25
|
+
|
26
|
+
def initialize(service_class)
|
27
|
+
@service_class = service_class
|
28
|
+
@rpcs = {}
|
29
|
+
end
|
30
|
+
|
31
|
+
def add_route(name, mapping)
|
32
|
+
if @rpcs[name]
|
33
|
+
raise ArgumentError, "Invalid RPC, route already defined: #{name}"
|
34
|
+
end
|
35
|
+
|
36
|
+
@rpcs[name] = mapping
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_service
|
40
|
+
# Synthesize a handler that will process the requests
|
41
|
+
#
|
42
|
+
handler = Class.new
|
43
|
+
@rpcs.each do |name, mapping|
|
44
|
+
rpc_info = @service_class.rpcs[name]
|
45
|
+
method_name = rpc_info[:ruby_method]
|
46
|
+
|
47
|
+
# Stolen from Rails in ActionDispatch::Request#controller_class_for
|
48
|
+
controller_name = mapping.controller.underscore
|
49
|
+
const_name = controller_name.camelize << "Controller"
|
50
|
+
action_name = mapping.action
|
51
|
+
response_class = rpc_info[:output_class]
|
52
|
+
|
53
|
+
handler.define_method(method_name) do |req, env|
|
54
|
+
controller_class = ::ActiveSupport::Dependencies.constantize(const_name)
|
55
|
+
controller_class.dispatch(action_name, req, response_class, env)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
service = @service_class.new(handler.new)
|
60
|
+
service.before do |rack_env, env|
|
61
|
+
env[:rack_env] = rack_env
|
62
|
+
end
|
63
|
+
service
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class Mapping
|
68
|
+
attr_reader :controller, :action
|
69
|
+
|
70
|
+
def initialize(to:)
|
71
|
+
@controller, @action = split_to(to)
|
72
|
+
end
|
73
|
+
|
74
|
+
def to_s
|
75
|
+
"#{controller}##{action}"
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
# copied from Rails
|
81
|
+
def split_to(to)
|
82
|
+
if /#/.match?(to)
|
83
|
+
to.split("#").map!(&:-@)
|
84
|
+
else
|
85
|
+
[]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
class ServiceMapper
|
91
|
+
def initialize(service_route_set)
|
92
|
+
@service_route_set = service_route_set
|
93
|
+
end
|
94
|
+
|
95
|
+
def rpc(name, to:)
|
96
|
+
mapping = Mapping.new(to: to)
|
97
|
+
@service_route_set.add_route(name, mapping)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class Mapper
|
102
|
+
def initialize(route_set)
|
103
|
+
@route_set = route_set
|
104
|
+
end
|
105
|
+
|
106
|
+
def service(service_definition, &block)
|
107
|
+
service_route_set = @route_set.services[service_definition]
|
108
|
+
service_mapper = ServiceMapper.new(service_route_set)
|
109
|
+
service_mapper.instance_exec(&block)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module RailsTwirp
|
2
|
+
class IntegrationTest < ActiveSupport::TestCase
|
3
|
+
attr_reader :response, :request, :controller
|
4
|
+
|
5
|
+
def initialize(name)
|
6
|
+
super
|
7
|
+
reset!
|
8
|
+
@before_rpc = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def reset!
|
12
|
+
@request = nil
|
13
|
+
@response = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def before_rpc(&block)
|
17
|
+
@before_rpc << block
|
18
|
+
end
|
19
|
+
|
20
|
+
def rpc(service, rpc, request, headers: nil)
|
21
|
+
@request = request
|
22
|
+
service = app.twirp.routes.services[service].to_service
|
23
|
+
|
24
|
+
rack_env = {}
|
25
|
+
http_request = ActionDispatch::Request.new(rack_env)
|
26
|
+
http_request.headers.merge! headers if headers.present?
|
27
|
+
env = {rack_env: rack_env}
|
28
|
+
|
29
|
+
@before_rpc.each do |hook|
|
30
|
+
hook.call(env)
|
31
|
+
end
|
32
|
+
|
33
|
+
response = service.call_rpc rpc, request, env
|
34
|
+
@response = response
|
35
|
+
@controller = http_request.controller_instance
|
36
|
+
response
|
37
|
+
end
|
38
|
+
|
39
|
+
def app
|
40
|
+
RailsTwirp.test_app
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/rails_twirp.gemspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require_relative "lib/rails_twirp/version"
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = "rails_twirp"
|
5
|
+
spec.version = RailsTwirp::VERSION
|
6
|
+
spec.authors = ["Bouke van der Bijl"]
|
7
|
+
spec.email = ["bouke@cheddar.me"]
|
8
|
+
spec.homepage = "https://github.com/cheddar-me/rails-twirp"
|
9
|
+
spec.summary = "Integrate Twirp into Rails"
|
10
|
+
spec.license = "MIT"
|
11
|
+
|
12
|
+
spec.files = `git ls-files`.split("\n")
|
13
|
+
spec.test_files = `git ls-files -- test/*`.split("\n")
|
14
|
+
|
15
|
+
spec.add_dependency "rails", "~> 6.1.3"
|
16
|
+
spec.add_dependency "twirp", "~> 1.7.2"
|
17
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rails_twirp
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Bouke van der Bijl
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-03-31 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 6.1.3
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 6.1.3
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: twirp
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.7.2
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.7.2
|
41
|
+
description:
|
42
|
+
email:
|
43
|
+
- bouke@cheddar.me
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- ".gitignore"
|
49
|
+
- Gemfile
|
50
|
+
- MIT-LICENSE
|
51
|
+
- README.md
|
52
|
+
- Rakefile
|
53
|
+
- bin/test
|
54
|
+
- lib/commands/twirp/routes_command.rb
|
55
|
+
- lib/rails_twirp.rb
|
56
|
+
- lib/rails_twirp/application.rb
|
57
|
+
- lib/rails_twirp/base.rb
|
58
|
+
- lib/rails_twirp/engine.rb
|
59
|
+
- lib/rails_twirp/route_set.rb
|
60
|
+
- lib/rails_twirp/testing/integration_test.rb
|
61
|
+
- lib/rails_twirp/version.rb
|
62
|
+
- rails_twirp.gemspec
|
63
|
+
- test/rails_twirp_test.rb
|
64
|
+
- test/test_helper.rb
|
65
|
+
homepage: https://github.com/cheddar-me/rails-twirp
|
66
|
+
licenses:
|
67
|
+
- MIT
|
68
|
+
metadata: {}
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options: []
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
requirements: []
|
84
|
+
rubygems_version: 3.2.3
|
85
|
+
signing_key:
|
86
|
+
specification_version: 4
|
87
|
+
summary: Integrate Twirp into Rails
|
88
|
+
test_files:
|
89
|
+
- test/rails_twirp_test.rb
|
90
|
+
- test/test_helper.rb
|