rough 0.2.5
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/lib/rough.rb +5 -0
- data/lib/rough/base_controller.rb +73 -0
- data/lib/rough/engine.rb +15 -0
- data/lib/rough/invalid_request_proto.rb +6 -0
- data/lib/rough/invalid_route.rb +6 -0
- data/lib/rough/middleware.rb +35 -0
- data/lib/rough/route.rb +39 -0
- data/lib/rough/route_registry.rb +67 -0
- data/lib/rough/rpc_registry.rb +42 -0
- data/lib/rough/version.rb +3 -0
- metadata +181 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a468899e1c99040aeeb6ae39e39dbc053dbcbeff
|
4
|
+
data.tar.gz: df0d272d7cd18682fc2e95529eb5a8fd2321c7ea
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0237bab2668299a304339f7fc481e98d053ca03c119c40f2e77873033634dc58c80f9a0a345de56b051428ed3bede0c779964692ca44956ceb85c47c9e14623c
|
7
|
+
data.tar.gz: 71c71fb2bc285bd0d85818a7a81da5254fa4b1b06beb21e4511ad6813c7fc3b18e223f51d251ca0cda13d234c18e50392eada037bd5677a45430c1f0d1e837de
|
data/lib/rough.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
require_relative 'rpc_registry'
|
2
|
+
require_relative 'invalid_request_proto'
|
3
|
+
require 'action_dispatch'
|
4
|
+
|
5
|
+
module Rough
|
6
|
+
|
7
|
+
module BaseController
|
8
|
+
|
9
|
+
PROTO_MIME = Mime::Type.new('application/x-protobuf')
|
10
|
+
|
11
|
+
def self.included(base)
|
12
|
+
base.class_eval do
|
13
|
+
before_action :log_proto, if: :rpc?
|
14
|
+
alias_method_chain :params, :request_proto
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def render(options = {})
|
19
|
+
return if performed?
|
20
|
+
if rpc?
|
21
|
+
if request.accept == PROTO_MIME || request.content_type == PROTO_MIME
|
22
|
+
response.headers['Content-Type'] = PROTO_MIME.to_s
|
23
|
+
super text: response_proto.encode, status: options[:status]
|
24
|
+
else
|
25
|
+
super json: response_proto.to_json, status: options[:status]
|
26
|
+
end
|
27
|
+
else
|
28
|
+
super
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def request_proto
|
33
|
+
return nil unless rpc?
|
34
|
+
@request_proto ||=
|
35
|
+
if request.content_type == PROTO_MIME
|
36
|
+
RpcRegistry.request_class_for(rpc_name).decode(request.body.read)
|
37
|
+
else
|
38
|
+
RpcRegistry.request_class_for(rpc_name).new(params_without_request_proto)
|
39
|
+
end
|
40
|
+
rescue TypeError => e
|
41
|
+
raise InvalidRequestProto, e
|
42
|
+
end
|
43
|
+
|
44
|
+
def response_proto
|
45
|
+
return nil unless rpc?
|
46
|
+
@response_proto ||= RpcRegistry.response_class_for(rpc_name).new
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def params_with_request_proto
|
52
|
+
if rpc?
|
53
|
+
ActionController::Parameters.new(request_proto.to_hash)
|
54
|
+
else
|
55
|
+
params_without_request_proto
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def rpc?
|
60
|
+
rpc_name
|
61
|
+
end
|
62
|
+
|
63
|
+
def log_proto
|
64
|
+
Rails.logger.info(" Request Proto: #{request_proto.inspect}")
|
65
|
+
end
|
66
|
+
|
67
|
+
def rpc_name
|
68
|
+
params_without_request_proto[:rpc]
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
data/lib/rough/engine.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require_relative 'middleware'
|
2
|
+
require_relative 'base_controller'
|
3
|
+
require 'rails/engine'
|
4
|
+
|
5
|
+
module Rough
|
6
|
+
|
7
|
+
class Engine < Rails::Engine
|
8
|
+
|
9
|
+
initializer 'rough_engine.middleware' do |app|
|
10
|
+
app.middleware.insert_after ActionDispatch::ParamsParser, Rough::Middleware
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require_relative 'route_registry'
|
2
|
+
|
3
|
+
module Rough
|
4
|
+
|
5
|
+
class Middleware
|
6
|
+
|
7
|
+
# This is the format for the service routes. It is expected to capture
|
8
|
+
# service_name and method_name (as named regex matches) from the given URL.
|
9
|
+
ROUTE_FORMAT = %r{^/services/(?<service_name>.+)/(?<method_name>.+)$}
|
10
|
+
|
11
|
+
def initialize(app)
|
12
|
+
@app = app
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(env)
|
16
|
+
path = env['PATH_INFO']
|
17
|
+
if env['REQUEST_METHOD'] == 'POST'
|
18
|
+
match = path.match(ROUTE_FORMAT)
|
19
|
+
if match
|
20
|
+
# re-map this route if we detect a matching RPC
|
21
|
+
rpc_route = RouteRegistry.rpc_route_for(match[:service_name], match[:method_name])
|
22
|
+
if rpc_route
|
23
|
+
env['PATH_INFO'] = rpc_route.path
|
24
|
+
env['REQUEST_METHOD'] = rpc_route.request_method
|
25
|
+
env['HTTP_ACCEPT'] = 'application/x-protobuf'
|
26
|
+
env['HTTP_CONTENT_TYPE'] = 'application/x-protobuf'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
@app.call(env)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
data/lib/rough/route.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require_relative 'invalid_route'
|
2
|
+
|
3
|
+
module Rough
|
4
|
+
|
5
|
+
class Route
|
6
|
+
|
7
|
+
attr_reader :route
|
8
|
+
|
9
|
+
def initialize(route)
|
10
|
+
@route = route
|
11
|
+
end
|
12
|
+
|
13
|
+
def path
|
14
|
+
@path ||= load_path
|
15
|
+
end
|
16
|
+
|
17
|
+
def request_method
|
18
|
+
@request_method ||= load_request_method
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def load_request_method
|
24
|
+
method_rule = @route.constraints[:request_method]
|
25
|
+
methods = ActionDispatch::Request::RFC2616.select { |t| t =~ method_rule }
|
26
|
+
methods.count == 1 ? methods.first : fail(InvalidRoute)
|
27
|
+
end
|
28
|
+
|
29
|
+
def load_path
|
30
|
+
fake_defaults = @route.defaults.dup
|
31
|
+
fake_defaults[:only_path] = true
|
32
|
+
@route.segments.each { |s| fake_defaults[s.to_sym] = s }
|
33
|
+
path = Rails.application.routes.url_for(fake_defaults)
|
34
|
+
path || fail(InvalidRoute)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require_relative 'route'
|
2
|
+
require_relative 'invalid_route'
|
3
|
+
|
4
|
+
module Rough
|
5
|
+
|
6
|
+
module RouteRegistry
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
def rpc_route_for(service_name, method_name)
|
11
|
+
return cached_routes[service_name][method_name] if cached_routes[service_name].key?(method_name)
|
12
|
+
route = find_route(service_name, method_name)
|
13
|
+
cached_routes[service_name][method_name] = route ? Route.new(route) : nil
|
14
|
+
end
|
15
|
+
|
16
|
+
# Warm up cache for each defined RPC route
|
17
|
+
def warm!
|
18
|
+
rpc_driven_routes.each do |route|
|
19
|
+
service_name, method_name = route.defaults[:rpc].split('#')
|
20
|
+
|
21
|
+
# java-ize service name
|
22
|
+
service_segments = service_name.split('::')
|
23
|
+
final_service_name = service_segments.pop
|
24
|
+
service_name = (service_segments.map(&:underscore) << final_service_name).join('.')
|
25
|
+
|
26
|
+
# java-ize method name
|
27
|
+
method_name = method_name.camelize
|
28
|
+
|
29
|
+
cached_routes[service_name][method_name] = Route.new(route)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def cached_routes
|
36
|
+
@cached_routes ||= Hash.new { |h, k| h[k] = {} }
|
37
|
+
end
|
38
|
+
|
39
|
+
# find a particular rails route for a given service_name and method_name
|
40
|
+
def find_route(matched_service_name, matched_method_name)
|
41
|
+
rpc_driven_routes.find do |route|
|
42
|
+
service_name, method_name = route.defaults[:rpc].split('#')
|
43
|
+
|
44
|
+
# ruby-ize service name
|
45
|
+
matched_service_segments = matched_service_name.split('.')
|
46
|
+
final_service_name = matched_service_segments.pop
|
47
|
+
matched_service_name = (matched_service_segments.map(&:capitalize) << final_service_name).join('::')
|
48
|
+
|
49
|
+
# ruby-ize method name
|
50
|
+
matched_method_name = matched_method_name.underscore
|
51
|
+
|
52
|
+
# does it match
|
53
|
+
matched_service_name == service_name && matched_method_name == method_name
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def rpc_driven_routes
|
58
|
+
Rails.application.routes.routes.lazy.select do |route|
|
59
|
+
route.defaults && route.defaults[:rpc]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'protobuf'
|
2
|
+
|
3
|
+
module Rough
|
4
|
+
|
5
|
+
module RpcRegistry
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def request_class_for(rpc_name)
|
10
|
+
rpc_for(rpc_name).request_type
|
11
|
+
end
|
12
|
+
|
13
|
+
def response_class_for(rpc_name)
|
14
|
+
rpc_for(rpc_name).response_type
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def rpc_for(rpc_name)
|
20
|
+
return method_structs[rpc_name] if method_structs.key?(rpc_name)
|
21
|
+
|
22
|
+
# TODO: in the future, should you be able to pass in a Rpc::Service, or separate rpc_name and method_names?
|
23
|
+
service_name, method_name = rpc_name.split('#')
|
24
|
+
|
25
|
+
service_class = service_name.constantize
|
26
|
+
fail 'not a service class' unless service_class < Protobuf::Rpc::Service
|
27
|
+
|
28
|
+
method_struct = service_class.rpcs[method_name.to_sym]
|
29
|
+
fail 'not a valid rpc' unless method_struct.is_a?(Struct::RpcMethod)
|
30
|
+
|
31
|
+
method_structs[rpc_name] = method_struct
|
32
|
+
end
|
33
|
+
|
34
|
+
def method_structs
|
35
|
+
@method_structs ||= {}
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
metadata
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rough
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.5
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- john crepezzi
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-12-08 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: '4.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: protobuf
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.4'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.4'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: actionpack
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '4.1'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '4.1'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec-rails
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: simplecov
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0.8'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0.8'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0.28'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0.28'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: combustion
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0.5'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0.5'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: stickler
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '2.4'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '2.4'
|
139
|
+
description: protobuf-driven APIs in Rails
|
140
|
+
email:
|
141
|
+
- johnc@squareup.com
|
142
|
+
executables: []
|
143
|
+
extensions: []
|
144
|
+
extra_rdoc_files: []
|
145
|
+
files:
|
146
|
+
- lib/rough.rb
|
147
|
+
- lib/rough/base_controller.rb
|
148
|
+
- lib/rough/engine.rb
|
149
|
+
- lib/rough/invalid_request_proto.rb
|
150
|
+
- lib/rough/invalid_route.rb
|
151
|
+
- lib/rough/middleware.rb
|
152
|
+
- lib/rough/route.rb
|
153
|
+
- lib/rough/route_registry.rb
|
154
|
+
- lib/rough/rpc_registry.rb
|
155
|
+
- lib/rough/version.rb
|
156
|
+
homepage: https://rubygems.org/gems/rough
|
157
|
+
licenses:
|
158
|
+
- Apache
|
159
|
+
metadata: {}
|
160
|
+
post_install_message:
|
161
|
+
rdoc_options: []
|
162
|
+
require_paths:
|
163
|
+
- lib
|
164
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
165
|
+
requirements:
|
166
|
+
- - ">="
|
167
|
+
- !ruby/object:Gem::Version
|
168
|
+
version: '0'
|
169
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
requirements: []
|
175
|
+
rubyforge_project:
|
176
|
+
rubygems_version: 2.2.2
|
177
|
+
signing_key:
|
178
|
+
specification_version: 4
|
179
|
+
summary: protobuf APIs
|
180
|
+
test_files: []
|
181
|
+
has_rdoc:
|