tom 0.2.0 → 0.2.1
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.
- data/README.md +7 -4
- data/VERSION +1 -1
- data/lib/adapter.rb +28 -2
- data/lib/dispatcher.rb +12 -115
- data/lib/goliath_api.rb +33 -0
- data/lib/init.rb +11 -1
- data/lib/merger.rb +5 -2
- data/lib/tom.rb +6 -55
- data/lib/tom/config.rb +32 -0
- data/lib/tom/log.rb +11 -0
- data/lib/tom/routes.rb +136 -0
- data/tom.gemspec +6 -2
- metadata +35 -31
data/README.md
CHANGED
@@ -10,8 +10,13 @@ All you have to do is define some `Adapter`s that get activated for certain rout
|
|
10
10
|
As you have seen in the video above, Tom Smykowski talks extra much when
|
11
11
|
consultants are present who might fire him. So this gem will log to
|
12
12
|
STDOUT when you're in development mode or the BOBS environment variable
|
13
|
-
is set.
|
14
|
-
|
13
|
+
is set. For example, you could
|
14
|
+
|
15
|
+
BOBS=present ruby yourapp.rb
|
16
|
+
|
17
|
+
Useful for debugging, but nothing you really want in production/testing.
|
18
|
+
Also, Tom is yard documented. Just run `yard --server` and direct your
|
19
|
+
browser to `http://localhost:8808`.
|
15
20
|
|
16
21
|
# TL;DR
|
17
22
|
|
@@ -111,8 +116,6 @@ Same goes for mergers.
|
|
111
116
|
# Todo
|
112
117
|
|
113
118
|
- handle adapter errors/states in mergers
|
114
|
-
- register routes with concurrency on and off
|
115
|
-
- use Goliath::Rack::Params
|
116
119
|
- use Goliath::Rack::Heartbeat
|
117
120
|
- think about consensus protocols
|
118
121
|
- ...
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.1
|
data/lib/adapter.rb
CHANGED
@@ -2,6 +2,12 @@ require 'em-synchrony/em-http'
|
|
2
2
|
require_relative 'http'
|
3
3
|
|
4
4
|
module Tom
|
5
|
+
|
6
|
+
# Please see the {https://github.com/moviepilot/tom#readme README} for
|
7
|
+
# examples on how to use this.
|
8
|
+
#
|
9
|
+
# @attribute host [String] The hostname this adapter is connecting to (the
|
10
|
+
# "other" API endpoint).
|
5
11
|
class Adapter
|
6
12
|
class << self
|
7
13
|
attr_accessor :host
|
@@ -16,13 +22,16 @@ module Tom
|
|
16
22
|
#
|
17
23
|
# @param route [String] The route this Adapter should
|
18
24
|
# respond to.
|
25
|
+
#
|
19
26
|
# @param methods [Array<Symbol>] Optional array of methods
|
20
27
|
# that this Adapter is listening to. It defaults to
|
21
|
-
# all (
|
28
|
+
# all (:head, :get, :put, :post, :delete)
|
29
|
+
#
|
30
|
+
# @return [Hash] See {Tom::Routes.register}
|
22
31
|
def self.register_route(*args)
|
23
32
|
route = args[0]
|
24
33
|
methods = args[1..-1]
|
25
|
-
|
34
|
+
Tom::Routes.register(route: /#{route}/, adapter: self, methods: methods)
|
26
35
|
end
|
27
36
|
|
28
37
|
def initialize
|
@@ -35,6 +44,9 @@ module Tom
|
|
35
44
|
#
|
36
45
|
# @param env [Array] The incoming (original request)
|
37
46
|
# rack env object
|
47
|
+
#
|
48
|
+
# @return [Array] Your beloved triple of [status_code, headers,
|
49
|
+
# response_body]
|
38
50
|
def forward_request(env)
|
39
51
|
rewrite_request(env)
|
40
52
|
options = http_request_options(env)
|
@@ -54,6 +66,10 @@ module Tom
|
|
54
66
|
# and POSTs this will add the request body
|
55
67
|
#
|
56
68
|
# @param env [Array] A rack env object
|
69
|
+
#
|
70
|
+
# @return [Hash] Has the value of @request[:body] inside its
|
71
|
+
# :body key, but only when the request method in the given
|
72
|
+
# env matches :put or :post
|
57
73
|
def http_request_options(env)
|
58
74
|
opts = {}
|
59
75
|
if [:put, :post].include? @request[:method]
|
@@ -68,6 +84,10 @@ module Tom
|
|
68
84
|
# this is what we feed into the EM::HttpRequest
|
69
85
|
#
|
70
86
|
# @param env [Array] A rack env object
|
87
|
+
#
|
88
|
+
# @return [Hash] Returns whatever the client POSTed/PUT and defaults
|
89
|
+
# to an empty hash (while debugging, consider your middlewares,
|
90
|
+
# they might touch this depending on the Content-Type)
|
71
91
|
def extract_request_body(env)
|
72
92
|
Rack::Request.new(env).POST rescue {}
|
73
93
|
end
|
@@ -78,6 +98,8 @@ module Tom
|
|
78
98
|
# before forwarding it.
|
79
99
|
#
|
80
100
|
# @param env [Array] A rack env object
|
101
|
+
#
|
102
|
+
# @return [Hash] The @request variable
|
81
103
|
def rewrite_request(env)
|
82
104
|
rewritten = rewrite_host(env)
|
83
105
|
@request = rewritten.merge(@request)
|
@@ -88,6 +110,8 @@ module Tom
|
|
88
110
|
#
|
89
111
|
# @param env [Array] The incoming (original request)
|
90
112
|
# rack env object
|
113
|
+
#
|
114
|
+
# @return [void] This mofo raises and never returns anything.
|
91
115
|
def handle(env)
|
92
116
|
raise "Subclass, implement #handle(env)!"
|
93
117
|
end
|
@@ -99,6 +123,8 @@ module Tom
|
|
99
123
|
# hostname.
|
100
124
|
#
|
101
125
|
# @param env [Array] A rack env object
|
126
|
+
#
|
127
|
+
# @return [Hash] With :host, :uri and :method
|
102
128
|
def rewrite_host(env)
|
103
129
|
{ host: self.class.host,
|
104
130
|
uri: env["REQUEST_URI"],
|
data/lib/dispatcher.rb
CHANGED
@@ -1,16 +1,6 @@
|
|
1
1
|
require "em-synchrony/fiber_iterator"
|
2
|
-
require 'logger'
|
3
2
|
|
4
3
|
module Tom
|
5
|
-
|
6
|
-
LOG = ::Logger.new(STDOUT)
|
7
|
-
LOG.level = ::Logger::ERROR
|
8
|
-
LOG.datetime_format = "%H:%M:%S:"
|
9
|
-
Logger::Formatter.module_eval(
|
10
|
-
%q{ def call(severity, time, progname, msg)} +
|
11
|
-
%q{ "#{format_datetime(time)} #{msg2str(msg)}\n" end}
|
12
|
-
)
|
13
|
-
|
14
4
|
class Dispatcher
|
15
5
|
|
16
6
|
# Dispatches this request to all adapters that registered
|
@@ -18,9 +8,11 @@ module Tom
|
|
18
8
|
# to compose a response
|
19
9
|
#
|
20
10
|
# @param env [Array] A rack env object
|
21
|
-
#
|
11
|
+
#
|
12
|
+
# @return [Array] Whatever {Tom::Dispatcher.merge} had to say
|
22
13
|
def self.dispatch(env)
|
23
|
-
|
14
|
+
route, method = route_and_method(env)
|
15
|
+
adapters = Tom::Routes.adapters_for_route(route, method)
|
24
16
|
return [404, {}, '{reason: "No adapters for this route"}'] if adapters.empty?
|
25
17
|
|
26
18
|
# Hit APIs. All at the same time. Oh, mygodd!
|
@@ -44,125 +36,30 @@ module Tom
|
|
44
36
|
# The merger used depends on the route.
|
45
37
|
#
|
46
38
|
# @param env [Array] A rack env object
|
39
|
+
#
|
47
40
|
# @return [Array] The merged result of all requests
|
48
41
|
# made as an array of status code, headers and body, e.g.
|
49
|
-
#
|
42
|
+
# [200, {}, "Hi!"]
|
50
43
|
def self.merge(env, responses)
|
51
|
-
|
44
|
+
route, method = route_and_method(env)
|
45
|
+
merger = Tom::Routes.merger_for_route(route, method)
|
52
46
|
Tom::LOG.info "Merging with:"
|
53
47
|
Tom::LOG.info " -> #{merger}"
|
54
48
|
merger.new.merge env, responses
|
55
49
|
end
|
56
50
|
|
57
|
-
# Registers a opts[:adapter] or opts[:merger] for the
|
58
|
-
# given opts[:route].
|
59
|
-
#
|
60
|
-
# This method should not be called directly, use register_route
|
61
|
-
# in Tom::Adapter or Tom::Merger instead.
|
62
|
-
#
|
63
|
-
# @param opts [Hash] Needs to have at least `:route` and
|
64
|
-
# either `:adapter` or `:merger` set, depending on what you
|
65
|
-
# are registering. But you don't need to use this method
|
66
|
-
# directly, Adapter and Merger provide convenience methods
|
67
|
-
# for you.
|
68
|
-
def self.register(opts)
|
69
|
-
return register_adapter(opts) if opts[:adapter]
|
70
|
-
return register_merger(opts) if opts[:merger]
|
71
|
-
raise "You need to supply opts[:adapter] or opts[:merger]"
|
72
|
-
end
|
73
|
-
|
74
|
-
private
|
75
|
-
|
76
|
-
#
|
77
|
-
# Registers an adapter for a given route and request method
|
78
|
-
#
|
79
|
-
def self.register_adapter(opts)
|
80
|
-
validate_type(opts[:adapter], Adapter)
|
81
|
-
methods = get_methods(opts)
|
82
|
-
@adapters ||= default_methods_hash
|
83
|
-
methods.each do |method|
|
84
|
-
@adapters[method][opts[:route]] ||= []
|
85
|
-
@adapters[method][opts[:route]] << opts[:adapter]
|
86
|
-
end
|
87
|
-
end
|
88
51
|
|
89
|
-
#
|
90
|
-
# Registers merger for a given route and request method
|
91
|
-
#
|
92
|
-
def self.register_merger(opts)
|
93
|
-
validate_type(opts[:merger], Merger)
|
94
|
-
methods = get_methods(opts)
|
95
|
-
@mergers ||= default_methods_hash
|
96
|
-
methods.each do |method|
|
97
|
-
@mergers[method][opts[:route]] ||= []
|
98
|
-
@mergers[method][opts[:route]] << opts[:merger]
|
99
|
-
end
|
100
|
-
end
|
101
52
|
|
53
|
+
# Extract the route/request uri and the method from a
|
54
|
+
# rack env
|
102
55
|
#
|
103
|
-
#
|
104
|
-
# to all methods.
|
105
|
-
#
|
106
|
-
def self.get_methods(opts)
|
107
|
-
return opts[:methods] unless opts[:methods].empty?
|
108
|
-
[:head, :get, :put, :post, :delete]
|
109
|
-
end
|
110
|
-
|
111
|
-
#
|
112
|
-
# Just some defaults to initialize thing
|
113
|
-
#
|
114
|
-
def self.default_methods_hash
|
115
|
-
{ head: {},
|
116
|
-
get: {},
|
117
|
-
put: {},
|
118
|
-
post: {},
|
119
|
-
delete: {}
|
120
|
-
}
|
121
|
-
end
|
122
|
-
|
123
|
-
#
|
124
|
-
# Find the right adapter for a route
|
125
|
-
#
|
126
|
-
def self.adapters_for_route(env)
|
127
|
-
@adapters ||= default_methods_hash
|
128
|
-
route, method = route_and_method(env)
|
129
|
-
matches = []
|
130
|
-
@adapters[method].map do |reg_route, adapters|
|
131
|
-
next unless reg_route.match(route)
|
132
|
-
matches += adapters
|
133
|
-
end
|
134
|
-
matches.uniq
|
135
|
-
end
|
136
|
-
|
137
|
-
#
|
138
|
-
# Find the right merger for a route
|
139
|
-
#
|
140
|
-
def self.merger_for_route(env)
|
141
|
-
@mergers ||= default_methods_hash
|
142
|
-
route, method = route_and_method(env)
|
143
|
-
@mergers[method].each do |reg_route, mergers|
|
144
|
-
next unless reg_route.match(route)
|
145
|
-
return mergers.first
|
146
|
-
end
|
147
|
-
raise "Found no merger for route #{route}"
|
148
|
-
end
|
149
|
-
|
150
|
-
#
|
151
|
-
# Extract the route/request uri and the method from a
|
152
|
-
# rack env
|
56
|
+
# @param env [Hash] A rack env
|
153
57
|
#
|
58
|
+
# @return [Array] Contains request_path, request_method as symbols
|
154
59
|
def self.route_and_method(env)
|
155
60
|
[env["REQUEST_PATH"],
|
156
61
|
env["REQUEST_METHOD"].downcase.to_sym]
|
157
62
|
end
|
158
63
|
|
159
|
-
#
|
160
|
-
# Make sure one class is a subclass of another class
|
161
|
-
#
|
162
|
-
def self.validate_type(c, expected)
|
163
|
-
return if c < expected
|
164
|
-
raise "Invalid type. Expected #{expected} got #{c}"
|
165
|
-
end
|
166
|
-
|
167
64
|
end
|
168
65
|
end
|
data/lib/goliath_api.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
module Tom
|
2
|
+
|
3
|
+
# WE HAZ ALL TEH GOLIATH REQUESTS AND FORWARDETH
|
4
|
+
# THEM TO DEH DISPATCHERETH.
|
5
|
+
#
|
6
|
+
# We have to see if this is the right way to do
|
7
|
+
# it when it comes to parallel stuff and so on...
|
8
|
+
class GoliathAPI < Goliath::API
|
9
|
+
use Goliath::Rack::Render
|
10
|
+
|
11
|
+
# Forwards env to {Tom::Dispatcher.dispatch}, look there.
|
12
|
+
#
|
13
|
+
# @param env [Hash] Rack env
|
14
|
+
def response(env)
|
15
|
+
begin
|
16
|
+
Tom::Dispatcher.dispatch(env)
|
17
|
+
rescue => e
|
18
|
+
handle_exception e, env
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Hardcoded JSON stacktrace stuff for now...
|
23
|
+
def handle_exception(e, env)
|
24
|
+
trace = e.backtrace.join "\n"
|
25
|
+
Tom::LOG.info e
|
26
|
+
Tom::LOG.info trace
|
27
|
+
[500, {}, {error: e,
|
28
|
+
stacktrace: trace,
|
29
|
+
url: env["REQUEST_URI"]
|
30
|
+
}.to_json]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/init.rb
CHANGED
@@ -1,8 +1,18 @@
|
|
1
1
|
require 'bundler'
|
2
2
|
Bundler.require :default
|
3
3
|
|
4
|
-
|
4
|
+
# Init Goliath env unless it was done already
|
5
|
+
Goliath.env rescue Goliath.env = (ENV['RACK_ENV'] || 'development').to_sym
|
5
6
|
|
7
|
+
# In dev mode, we do some logging (defaults to Logger::ERROR in other
|
8
|
+
# envs)
|
9
|
+
if Goliath.env == :development || ENV['BOBS']
|
10
|
+
Tom::LOG.level = Logger::INFO
|
11
|
+
end
|
12
|
+
Tom::LOG.info "Started goliath in #{Goliath.env} environment (change with ruby your_app.rb -e development or by setting $RACK_ENV)"
|
13
|
+
|
14
|
+
# Fetch the codes
|
15
|
+
require_relative 'goliath_api'
|
6
16
|
require_relative 'dispatcher'
|
7
17
|
require_relative 'adapter'
|
8
18
|
require_relative 'merger'
|
data/lib/merger.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
module Tom
|
2
|
+
|
3
|
+
# Please see the {https://github.com/moviepilot/tom#readme README} for
|
4
|
+
# examples on how to use this.
|
2
5
|
class Merger
|
3
6
|
|
4
7
|
# Registers a route with the request dispatcher
|
@@ -18,7 +21,7 @@ module Tom
|
|
18
21
|
def self.register_route(*args)
|
19
22
|
route = args[0]
|
20
23
|
methods = args[1..-1]
|
21
|
-
|
24
|
+
Tom::Routes.register(route: /#{route}/, merger: self, methods: methods)
|
22
25
|
end
|
23
26
|
|
24
27
|
# When the request dispatcher made all the requests,
|
@@ -29,7 +32,7 @@ module Tom
|
|
29
32
|
# rack env object
|
30
33
|
# @param responses [Hash] Replies from all Adapters that
|
31
34
|
# got triggered by route and method, e.g.
|
32
|
-
# `{MyAdapter: rack_env, MyOtherAdapter: other_env}`
|
35
|
+
# `{ MyAdapter: rack_env, MyOtherAdapter: other_env }`
|
33
36
|
# @return [Array] A rack response (for example, something
|
34
37
|
# like [200, {}, "body"])
|
35
38
|
def merge(env, responses)
|
data/lib/tom.rb
CHANGED
@@ -1,59 +1,10 @@
|
|
1
|
+
require_relative 'tom/log'
|
2
|
+
require_relative 'tom/routes'
|
3
|
+
require_relative 'tom/config'
|
1
4
|
require_relative 'init'
|
2
5
|
|
3
|
-
#
|
4
|
-
|
5
|
-
|
6
|
-
# In dev mode, we do some logging (defaults to Logger::ERROR in other
|
7
|
-
# envs)
|
8
|
-
if Goliath.env == :development || ENV['BOBS']
|
9
|
-
Tom::LOG.level = Logger::INFO
|
10
|
-
end
|
11
|
-
Tom::LOG.info "Started goliath in #{Goliath.env} environment (change with ruby your_app.rb -e development or by setting $RACK_ENV)"
|
12
|
-
|
6
|
+
# The only thing you should really do with the Tom class is
|
7
|
+
# setting or reading its configuration.
|
13
8
|
module Tom
|
14
|
-
|
15
|
-
def self.config
|
16
|
-
@config || default_config
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.config=(config)
|
20
|
-
@config = config
|
21
|
-
end
|
22
|
-
|
23
|
-
#
|
24
|
-
# WE HAZ ALL TEH GOLIATH REQUESTS AND FORWARDETH
|
25
|
-
# THEM TO DEH DISPATCHERETH.
|
26
|
-
#
|
27
|
-
# We have to see if this is the right way to do
|
28
|
-
# it when it comes to parallel stuff and so on...
|
29
|
-
#
|
30
|
-
class GoliathAPI < Goliath::API
|
31
|
-
use Goliath::Rack::Render
|
32
|
-
|
33
|
-
def response(env)
|
34
|
-
begin
|
35
|
-
Tom::Dispatcher.dispatch(env)
|
36
|
-
rescue => e
|
37
|
-
handle_exception e, env
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def handle_exception(e, env)
|
42
|
-
trace = e.backtrace.join "\n"
|
43
|
-
Tom::LOG.info e
|
44
|
-
Tom::LOG.info trace
|
45
|
-
[500, {}, {error: e,
|
46
|
-
stacktrace: trace,
|
47
|
-
url: env["REQUEST_URI"]
|
48
|
-
}.to_json]
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
private
|
53
|
-
|
54
|
-
def self.default_config
|
55
|
-
{ timeouts:
|
56
|
-
{ connect_timeout: 5,
|
57
|
-
inactivity_timeout: 10 } }
|
58
|
-
end
|
9
|
+
extend Tom::Config
|
59
10
|
end
|
data/lib/tom/config.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
module Tom
|
2
|
+
|
3
|
+
# Don't use this module directly - access these methods
|
4
|
+
# through {Tom} who does a `extend Tom::Config`
|
5
|
+
module Config
|
6
|
+
|
7
|
+
# @return [Hash] configuration
|
8
|
+
def config
|
9
|
+
@config || default_config
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
# @param config [Hash] The configuration you want
|
14
|
+
# to set. Currently, only the `:timeout` key is
|
15
|
+
# read, and in it the `:connect_timeout` and the
|
16
|
+
# `:inactivity_timeout` keys can be set to an int
|
17
|
+
# value (seconds)
|
18
|
+
# @return [Hash] configuration
|
19
|
+
def config=(config)
|
20
|
+
@config = config
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def default_config
|
27
|
+
{ timeouts:
|
28
|
+
{ connect_timeout: 5,
|
29
|
+
inactivity_timeout: 10 } }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/tom/log.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Tom
|
4
|
+
LOG = ::Logger.new(STDOUT)
|
5
|
+
LOG.level = ::Logger::ERROR
|
6
|
+
LOG.datetime_format = "%H:%M:%S:"
|
7
|
+
Logger::Formatter.module_eval(
|
8
|
+
%q{ def call(severity, time, progname, msg)} +
|
9
|
+
%q{ "#{format_datetime(time)} #{msg2str(msg)}\n" end}
|
10
|
+
)
|
11
|
+
end
|
data/lib/tom/routes.rb
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
module Tom
|
2
|
+
|
3
|
+
# Takes care of registering adapters and mergers for routes,
|
4
|
+
# and then matching routes and methods to adapters and mergers.
|
5
|
+
module Routes
|
6
|
+
|
7
|
+
# Registers a opts[:adapter] or opts[:merger] for the
|
8
|
+
# given opts[:route].
|
9
|
+
#
|
10
|
+
# This method should not be called directly in your app, you
|
11
|
+
# would use {Tom::Adapter.register_route} or
|
12
|
+
# {Tom::Merger.register_route} in your subclass of {Tom::Adapter}
|
13
|
+
# or {Tom::Merger}
|
14
|
+
#
|
15
|
+
# @param opts [Hash] Needs to have at least :route and
|
16
|
+
# either :adapter or :merger set, depending on what you
|
17
|
+
# are registering. But you don't need to use this method
|
18
|
+
# directly, Adapter and Merger provide convenience methods
|
19
|
+
# for you.
|
20
|
+
#
|
21
|
+
# @return [Hash] See {Tom::Routes.register_adapter} or
|
22
|
+
# {Tom::Routes.register_merger}
|
23
|
+
def self.register(opts)
|
24
|
+
return register_adapter(opts) if opts[:adapter]
|
25
|
+
return register_merger(opts) if opts[:merger]
|
26
|
+
raise "You need to supply opts[:adapter] or opts[:merger]"
|
27
|
+
end
|
28
|
+
|
29
|
+
# Registers an {Adapter} for a given route and request method
|
30
|
+
#
|
31
|
+
# @param opts [Hash] Needs to have the :route and :adapter
|
32
|
+
# keys set with a regular expression and a subclass of
|
33
|
+
# {Tom::Adapter}
|
34
|
+
#
|
35
|
+
# @return [Hash] First level are the HTTP methods, second
|
36
|
+
# level are the regular expressions for routes, and the
|
37
|
+
# values are arrays of Adapters for the combination of
|
38
|
+
# request method and route.
|
39
|
+
def self.register_adapter(opts)
|
40
|
+
validate_type(opts[:adapter], Adapter)
|
41
|
+
methods = get_methods(opts)
|
42
|
+
@adapters ||= default_methods_hash
|
43
|
+
methods.each do |method|
|
44
|
+
@adapters[method][opts[:route]] ||= []
|
45
|
+
@adapters[method][opts[:route]] << opts[:adapter]
|
46
|
+
end
|
47
|
+
@adapters
|
48
|
+
end
|
49
|
+
|
50
|
+
# Registers a {Merger} for a given route and request method
|
51
|
+
#
|
52
|
+
# @return [Hash] First level are the HTTP methods, second
|
53
|
+
# level are the regular expressions for routes, and the
|
54
|
+
# values are arrays of Mergers for the combination of
|
55
|
+
# request method and route.
|
56
|
+
def self.register_merger(opts)
|
57
|
+
validate_type(opts[:merger], Merger)
|
58
|
+
methods = get_methods(opts)
|
59
|
+
@mergers ||= default_methods_hash
|
60
|
+
methods.each do |method|
|
61
|
+
@mergers[method][opts[:route]] ||= []
|
62
|
+
@mergers[method][opts[:route]] << opts[:merger]
|
63
|
+
end
|
64
|
+
@mergers
|
65
|
+
end
|
66
|
+
|
67
|
+
# @return [Hash] See what {Tom::Routes.register_adapter} returns
|
68
|
+
def self.adapters
|
69
|
+
@adapters
|
70
|
+
end
|
71
|
+
|
72
|
+
# @return [Hash] See what {Tom::Routes.register_merger} returns
|
73
|
+
def self.mergers
|
74
|
+
@mergers
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
# Fetches the methods from the options hash, defaults
|
80
|
+
# to all methods.
|
81
|
+
#
|
82
|
+
# @param opts [Hash] We're looking for :methods in this hash
|
83
|
+
#
|
84
|
+
# @return [Hash] Returns what's in :methods, and defaults to
|
85
|
+
# [:head, :get, :put, :post, :delete]
|
86
|
+
def self.get_methods(opts)
|
87
|
+
return opts[:methods] unless opts[:methods].empty?
|
88
|
+
[:head, :get, :put, :post, :delete]
|
89
|
+
end
|
90
|
+
|
91
|
+
#
|
92
|
+
# Just some defaults to initialize thing
|
93
|
+
#
|
94
|
+
def self.default_methods_hash
|
95
|
+
{ head: {},
|
96
|
+
get: {},
|
97
|
+
put: {},
|
98
|
+
post: {},
|
99
|
+
delete: {}
|
100
|
+
}
|
101
|
+
end
|
102
|
+
|
103
|
+
#
|
104
|
+
# Find the right adapter for a route
|
105
|
+
#
|
106
|
+
def self.adapters_for_route(route, method)
|
107
|
+
@adapters ||= default_methods_hash
|
108
|
+
matches = []
|
109
|
+
@adapters[method].map do |reg_route, adapters|
|
110
|
+
next unless reg_route.match(route)
|
111
|
+
matches += adapters
|
112
|
+
end
|
113
|
+
matches.uniq
|
114
|
+
end
|
115
|
+
|
116
|
+
#
|
117
|
+
# Find the right merger for a route
|
118
|
+
#
|
119
|
+
def self.merger_for_route(route, method)
|
120
|
+
@mergers ||= default_methods_hash
|
121
|
+
@mergers[method].each do |reg_route, mergers|
|
122
|
+
next unless reg_route.match(route)
|
123
|
+
return mergers.first
|
124
|
+
end
|
125
|
+
raise "Found no merger for route #{route}"
|
126
|
+
end
|
127
|
+
|
128
|
+
#
|
129
|
+
# Make sure one class is a subclass of another class
|
130
|
+
#
|
131
|
+
def self.validate_type(c, expected)
|
132
|
+
return if c < expected
|
133
|
+
raise "Invalid type. Expected #{expected} got #{c}"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
data/tom.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "tom"
|
8
|
-
s.version = "0.2.
|
8
|
+
s.version = "0.2.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Jannis Hermanns"]
|
12
|
-
s.date = "2011-12-
|
12
|
+
s.date = "2011-12-07"
|
13
13
|
s.description = " Tom uses Goliath to dispatch HTTP requests to multiple other APIs (via Adapters) in parallel. In a next step, a Merger merges the result and responds to the clients request."
|
14
14
|
s.email = "jannis@gmail.com"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -27,10 +27,14 @@ Gem::Specification.new do |s|
|
|
27
27
|
"VERSION",
|
28
28
|
"lib/adapter.rb",
|
29
29
|
"lib/dispatcher.rb",
|
30
|
+
"lib/goliath_api.rb",
|
30
31
|
"lib/http.rb",
|
31
32
|
"lib/init.rb",
|
32
33
|
"lib/merger.rb",
|
33
34
|
"lib/tom.rb",
|
35
|
+
"lib/tom/config.rb",
|
36
|
+
"lib/tom/log.rb",
|
37
|
+
"lib/tom/routes.rb",
|
34
38
|
"spec/lib/adapter_spec.rb",
|
35
39
|
"spec/lib/dispatcher_spec.rb",
|
36
40
|
"spec/spec_helper.rb",
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tom
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-12-
|
12
|
+
date: 2011-12-07 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: goliath
|
16
|
-
requirement: &
|
16
|
+
requirement: &70109120203120 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70109120203120
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: em-synchrony
|
27
|
-
requirement: &
|
27
|
+
requirement: &70109120202620 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70109120202620
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: em-http-request
|
38
|
-
requirement: &
|
38
|
+
requirement: &70109120202140 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70109120202140
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: json
|
49
|
-
requirement: &
|
49
|
+
requirement: &70109120201560 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: '0'
|
55
55
|
type: :runtime
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70109120201560
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: rake
|
60
|
-
requirement: &
|
60
|
+
requirement: &70109120201040 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ~>
|
@@ -65,10 +65,10 @@ dependencies:
|
|
65
65
|
version: 0.9.2
|
66
66
|
type: :runtime
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *70109120201040
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: bundler
|
71
|
-
requirement: &
|
71
|
+
requirement: &70109120200460 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
74
|
- - ~>
|
@@ -76,10 +76,10 @@ dependencies:
|
|
76
76
|
version: 1.0.0
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *70109120200460
|
80
80
|
- !ruby/object:Gem::Dependency
|
81
81
|
name: jeweler
|
82
|
-
requirement: &
|
82
|
+
requirement: &70109120199960 !ruby/object:Gem::Requirement
|
83
83
|
none: false
|
84
84
|
requirements:
|
85
85
|
- - ~>
|
@@ -87,10 +87,10 @@ dependencies:
|
|
87
87
|
version: 1.6.4
|
88
88
|
type: :development
|
89
89
|
prerelease: false
|
90
|
-
version_requirements: *
|
90
|
+
version_requirements: *70109120199960
|
91
91
|
- !ruby/object:Gem::Dependency
|
92
92
|
name: shoulda
|
93
|
-
requirement: &
|
93
|
+
requirement: &70109120199420 !ruby/object:Gem::Requirement
|
94
94
|
none: false
|
95
95
|
requirements:
|
96
96
|
- - ! '>='
|
@@ -98,10 +98,10 @@ dependencies:
|
|
98
98
|
version: '0'
|
99
99
|
type: :development
|
100
100
|
prerelease: false
|
101
|
-
version_requirements: *
|
101
|
+
version_requirements: *70109120199420
|
102
102
|
- !ruby/object:Gem::Dependency
|
103
103
|
name: rcov
|
104
|
-
requirement: &
|
104
|
+
requirement: &70109120198820 !ruby/object:Gem::Requirement
|
105
105
|
none: false
|
106
106
|
requirements:
|
107
107
|
- - ! '>='
|
@@ -109,10 +109,10 @@ dependencies:
|
|
109
109
|
version: '0'
|
110
110
|
type: :development
|
111
111
|
prerelease: false
|
112
|
-
version_requirements: *
|
112
|
+
version_requirements: *70109120198820
|
113
113
|
- !ruby/object:Gem::Dependency
|
114
114
|
name: ruby-debug19
|
115
|
-
requirement: &
|
115
|
+
requirement: &70109120198220 !ruby/object:Gem::Requirement
|
116
116
|
none: false
|
117
117
|
requirements:
|
118
118
|
- - ! '>='
|
@@ -120,10 +120,10 @@ dependencies:
|
|
120
120
|
version: '0'
|
121
121
|
type: :development
|
122
122
|
prerelease: false
|
123
|
-
version_requirements: *
|
123
|
+
version_requirements: *70109120198220
|
124
124
|
- !ruby/object:Gem::Dependency
|
125
125
|
name: rspec
|
126
|
-
requirement: &
|
126
|
+
requirement: &70109120197620 !ruby/object:Gem::Requirement
|
127
127
|
none: false
|
128
128
|
requirements:
|
129
129
|
- - ! '>='
|
@@ -131,10 +131,10 @@ dependencies:
|
|
131
131
|
version: '0'
|
132
132
|
type: :development
|
133
133
|
prerelease: false
|
134
|
-
version_requirements: *
|
134
|
+
version_requirements: *70109120197620
|
135
135
|
- !ruby/object:Gem::Dependency
|
136
136
|
name: webmock
|
137
|
-
requirement: &
|
137
|
+
requirement: &70109120197020 !ruby/object:Gem::Requirement
|
138
138
|
none: false
|
139
139
|
requirements:
|
140
140
|
- - ! '>='
|
@@ -142,10 +142,10 @@ dependencies:
|
|
142
142
|
version: '0'
|
143
143
|
type: :development
|
144
144
|
prerelease: false
|
145
|
-
version_requirements: *
|
145
|
+
version_requirements: *70109120197020
|
146
146
|
- !ruby/object:Gem::Dependency
|
147
147
|
name: yard
|
148
|
-
requirement: &
|
148
|
+
requirement: &70109120196420 !ruby/object:Gem::Requirement
|
149
149
|
none: false
|
150
150
|
requirements:
|
151
151
|
- - ! '>='
|
@@ -153,10 +153,10 @@ dependencies:
|
|
153
153
|
version: '0'
|
154
154
|
type: :development
|
155
155
|
prerelease: false
|
156
|
-
version_requirements: *
|
156
|
+
version_requirements: *70109120196420
|
157
157
|
- !ruby/object:Gem::Dependency
|
158
158
|
name: rdiscount
|
159
|
-
requirement: &
|
159
|
+
requirement: &70109120195780 !ruby/object:Gem::Requirement
|
160
160
|
none: false
|
161
161
|
requirements:
|
162
162
|
- - ! '>='
|
@@ -164,7 +164,7 @@ dependencies:
|
|
164
164
|
version: '0'
|
165
165
|
type: :development
|
166
166
|
prerelease: false
|
167
|
-
version_requirements: *
|
167
|
+
version_requirements: *70109120195780
|
168
168
|
description: ! ' Tom uses Goliath to dispatch HTTP requests to multiple other APIs
|
169
169
|
(via Adapters) in parallel. In a next step, a Merger merges the result and responds
|
170
170
|
to the clients request.'
|
@@ -185,10 +185,14 @@ files:
|
|
185
185
|
- VERSION
|
186
186
|
- lib/adapter.rb
|
187
187
|
- lib/dispatcher.rb
|
188
|
+
- lib/goliath_api.rb
|
188
189
|
- lib/http.rb
|
189
190
|
- lib/init.rb
|
190
191
|
- lib/merger.rb
|
191
192
|
- lib/tom.rb
|
193
|
+
- lib/tom/config.rb
|
194
|
+
- lib/tom/log.rb
|
195
|
+
- lib/tom/routes.rb
|
192
196
|
- spec/lib/adapter_spec.rb
|
193
197
|
- spec/lib/dispatcher_spec.rb
|
194
198
|
- spec/spec_helper.rb
|
@@ -208,7 +212,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
208
212
|
version: '0'
|
209
213
|
segments:
|
210
214
|
- 0
|
211
|
-
hash:
|
215
|
+
hash: 1267034355888235248
|
212
216
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
213
217
|
none: false
|
214
218
|
requirements:
|