rough 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,5 @@
1
+ require_relative 'rough/engine'
2
+
3
+ # Rough
4
+ module Rough
5
+ end
@@ -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
@@ -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,6 @@
1
+ module Rough
2
+
3
+ class InvalidRequestProto < StandardError
4
+ end
5
+
6
+ end
@@ -0,0 +1,6 @@
1
+ module Rough
2
+
3
+ class InvalidRoute < StandardError
4
+ end
5
+
6
+ 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
@@ -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
@@ -0,0 +1,3 @@
1
+ module Rough
2
+ VERSION = '0.2.5'
3
+ 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: