rester 0.5.1 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a17ecdff2d900cdad60ccd5404afca94cfda269f
4
- data.tar.gz: efa02cdbfef4743e9c0538896d769cb33c979a51
3
+ metadata.gz: 9bb523b9b23530a59e8bc2a8fc8463324739b59d
4
+ data.tar.gz: 26d4a6f4cd0de0417b470357488c0ed941e8f8a8
5
5
  SHA512:
6
- metadata.gz: 38c3311be4b40526035536d31cbbd1e682911e51cd54dcd53e1789aa356c44ef4ef38ff4bdf9b7012735c0d79d844a324eeedd7418706985ddabb756b7546bb5
7
- data.tar.gz: 60f377dc74da842bf80696998657ce61549db1a7f7cb86f0d38967cd35a4311ae6d9d5d1d8c565956b56dcf0a82305cbac9d64044aabf9910569621cc42225df
6
+ metadata.gz: 9456f4f73aa5e8516e3731a273954a254735de21025dcdac36e70209e922f1ce4ee335b865a32efb4c01e6225ddfbd4161c3d89cc85096bd02521b09c6c5b3eb
7
+ data.tar.gz: 1ad4848eb57cd9770a6c9185c4b9f136111083a258ec9bff59addd160801e6e2865c02623ddbae1b4fb6bebe5152080b3566389cf5d3497d2ec1d51406936713
@@ -11,7 +11,7 @@ module Rester
11
11
 
12
12
  attr_reader :timeout
13
13
 
14
- def initialize(service=nil, opts={})
14
+ def initialize(service = nil, opts = {})
15
15
  @timeout = opts[:timeout]
16
16
  connect(service) if service
17
17
  end
@@ -19,7 +19,7 @@ module Rester
19
19
  ##
20
20
  # Returns the headers defined for this Adapter. Optionally, you may also
21
21
  # define additional headers you'd like to add/override.
22
- def headers(new_headers={})
22
+ def headers(new_headers = {})
23
23
  (@headers ||= {}).merge!(new_headers)
24
24
  end
25
25
 
@@ -37,7 +37,7 @@ module Rester
37
37
  end
38
38
 
39
39
  ##
40
- # Sends an HTTP request to the service.
40
+ # Sends a request (using one of the subclass adapters) to the service.
41
41
  #
42
42
  # `params` should be a hash if specified.
43
43
  def request(verb, path, params = nil)
@@ -38,7 +38,12 @@ module Rester
38
38
  private
39
39
 
40
40
  def _prepare_response(response)
41
- [response.code.to_i, response.body]
41
+ # We want to format the header keys in the way we would expect it in our
42
+ # client: X-Rester-Header-Key.
43
+ #
44
+ # http://ruby-doc.org/stdlib-2.1.1/libdoc/net/http/rdoc/Net/HTTPHeader.html
45
+ headers = {}.tap { |h| response.each_capitalized { |k,v| h[k] = v } }
46
+ [response.code.to_i, headers, response.body]
42
47
  end
43
48
 
44
49
  def _require_connection
@@ -37,11 +37,13 @@ module Rester
37
37
 
38
38
  response = Timeout::timeout(timeout) do
39
39
  service.call(
40
- 'REQUEST_METHOD' => verb.to_s.upcase,
41
- 'PATH_INFO' => path,
42
- 'CONTENT_TYPE' => 'application/x-www-form-urlencoded',
43
- 'QUERY_STRING' => query,
44
- 'rack.input' => StringIO.new(body)
40
+ _headers_to_http_format(opts[:headers]).merge(
41
+ 'REQUEST_METHOD' => verb.to_s.upcase,
42
+ 'PATH_INFO' => path,
43
+ 'CONTENT_TYPE' => 'application/x-www-form-urlencoded',
44
+ 'QUERY_STRING' => query,
45
+ 'rack.input' => StringIO.new(body)
46
+ )
45
47
  )
46
48
  end
47
49
 
@@ -52,11 +54,19 @@ module Rester
52
54
 
53
55
  [
54
56
  response.first, # The status code
57
+ {
58
+ 'X-Rester-Producer-Name' => service.service_name,
59
+ }, # The header
55
60
  body # The response body.
56
61
  ]
57
62
  rescue Timeout::Error
58
63
  fail Errors::TimeoutError
59
64
  end
65
+
66
+
67
+ def _headers_to_http_format(headers={})
68
+ Hash[headers.map { |k,v| ["HTTP_#{k.to_s.upcase.gsub('-', '_')}", v] }]
69
+ end
60
70
  end # LocalAdapter
61
71
  end # Client::Adapters
62
72
  end # Rester
@@ -30,7 +30,15 @@ module Rester
30
30
  !!stub
31
31
  end
32
32
 
33
+ def producer
34
+ stub['producer']
35
+ end
36
+
33
37
  def request!(verb, path, encoded_data)
38
+ if verb == :get && path == '/ping'
39
+ return [200, { 'X-Rester-Producer-Name' => producer }, '']
40
+ end
41
+
34
42
  params = Rack::Utils.parse_nested_query(encoded_data)
35
43
  _request(verb.to_s.upcase, path, params)
36
44
  end
@@ -45,7 +53,11 @@ module Rester
45
53
 
46
54
  def _request(verb, path, params)
47
55
  spec = _process_request(path, verb, params)
48
- [spec['response_code'], spec['response'].to_json]
56
+ [
57
+ spec['response_code'],
58
+ { 'X-Rester-Producer-Name' => producer },
59
+ spec['response'].to_json
60
+ ]
49
61
  end
50
62
 
51
63
  def _process_request(path, verb, params)
@@ -0,0 +1,15 @@
1
+ module Rester
2
+ module Client::Middleware
3
+ class Base
4
+ attr_reader :app
5
+
6
+ def initialize(app)
7
+ @app = app
8
+ end
9
+
10
+ def call(env)
11
+ app.call(env)
12
+ end
13
+ end # Base
14
+ end # Client::Middleware
15
+ end # Rester
@@ -0,0 +1,14 @@
1
+ module Rester
2
+ module Client::Middleware
3
+ class RequestHandler < Base
4
+ def call(env)
5
+ Rester.begin_request
6
+ Rester.correlation_id = SecureRandom.uuid
7
+ Rester.request_info[:consumer_name] = Rester.service_name
8
+ super
9
+ ensure
10
+ Rester.end_request
11
+ end
12
+ end # RequestHandlers
13
+ end # Client::Middleware
14
+ end # Rester
@@ -0,0 +1,8 @@
1
+ module Rester
2
+ class Client
3
+ module Middleware
4
+ autoload(:Base, 'rester/client/middleware/base')
5
+ autoload(:RequestHandler, 'rester/client/middleware/request_handler')
6
+ end
7
+ end
8
+ end
data/lib/rester/client.rb CHANGED
@@ -4,9 +4,10 @@ require 'logger'
4
4
 
5
5
  module Rester
6
6
  class Client
7
- autoload(:Adapters, 'rester/client/adapters')
8
- autoload(:Resource, 'rester/client/resource')
9
- autoload(:Response, 'rester/client/response')
7
+ autoload(:Adapters, 'rester/client/adapters')
8
+ autoload(:Resource, 'rester/client/resource')
9
+ autoload(:Response, 'rester/client/response')
10
+ autoload(:Middleware, 'rester/client/middleware')
10
11
 
11
12
  attr_reader :adapter
12
13
  attr_reader :version
@@ -19,22 +20,45 @@ module Rester
19
20
  self.version = params[:version]
20
21
  @error_threshold = (params[:error_threshold] || 3).to_i
21
22
  @retry_period = (params[:retry_period] || 1).to_f
22
- @logger = params[:logger] || Logger.new(STDOUT)
23
+ self.logger = params[:logger]
24
+ @_breaker_enabled = params.fetch(:circuit_breaker_enabled,
25
+ ENV['RACK_ENV'] != 'test' && ENV['RAILS_ENV'] != 'test'
26
+ )
23
27
 
24
28
  @_resource = Resource.new(self)
25
- _init_request_breaker
26
- end
29
+ _init_requester
27
30
 
28
- def connect(*args)
29
- adapter.connect(*args)
31
+ # Send a test ping request to the service so we can store the producer's
32
+ # name for future request logs
33
+ fail Errors::ConnectionError unless connected?
30
34
  end
31
35
 
32
36
  def connected?
33
- adapter.connected? && adapter.get('/ping').first == 200
37
+ adapter.connected? && @_requester.call(:get, '/ping', {}).successful?
38
+ rescue
39
+ false
40
+ end
41
+
42
+ def circuit_breaker_enabled?
43
+ !!@_breaker_enabled
44
+ end
45
+
46
+ def logger=(logger)
47
+ logger = Utils::LoggerWrapper.new(logger) if logger
48
+ @logger = logger
49
+ end
50
+
51
+ def logger
52
+ @logger || Rester.logger
53
+ end
54
+
55
+ def name
56
+ @_producer_name
34
57
  end
35
58
 
36
59
  def request(verb, path, params={})
37
- @_request_breaker.call(verb, path, params)
60
+ path = _path_with_version(path)
61
+ @_requester.call(verb, path, params)
38
62
  rescue Utils::CircuitBreaker::CircuitOpenError
39
63
  # Translate this error so it's easier handle for clients.
40
64
  # Also, at some point we may want to extract CircuitBreaker into its own
@@ -77,31 +101,55 @@ module Rester
77
101
  #
78
102
  # When the circuit is opened or closed, a message is sent to the logger for
79
103
  # the client.
80
- def _init_request_breaker
81
- @_request_breaker = Utils::CircuitBreaker.new(
82
- threshold: error_threshold, retry_period: retry_period
83
- ) { |*args| _request(*args) }
84
-
85
- @_request_breaker.on_open do
86
- logger.error("circuit opened")
87
- end
104
+ def _init_requester
105
+ if circuit_breaker_enabled?
106
+ @_requester = Utils::CircuitBreaker.new(
107
+ threshold: error_threshold, retry_period: retry_period
108
+ ) { |*args| _request(*args) }
109
+
110
+ @_requester.on_open do
111
+ logger.error("circuit opened for #{name}")
112
+ end
88
113
 
89
- @_request_breaker.on_close do
90
- logger.info("circuit closed")
114
+ @_requester.on_close do
115
+ logger.info("circuit closed for #{name}")
116
+ end
117
+ else
118
+ @_requester = proc { |*args| _request(*args) }
91
119
  end
92
120
  end
93
121
 
122
+ ##
123
+ # Add a correlation ID to the header and send the request to the adapter
94
124
  def _request(verb, path, params)
95
- path = _path_with_version(path)
96
- _process_response(path, *adapter.request(verb, path, params))
125
+ Rester.request_info[:producer_name] = name
126
+ Rester.request_info[:path] = path
127
+ Rester.request_info[:verb] = verb
128
+ logger.info('sending request')
129
+
130
+ _set_default_headers
131
+ start_time = Time.now.to_f
132
+ response = adapter.request(verb, path, params)
133
+ _process_response(start_time, verb, path, *response)
134
+ end
135
+
136
+ def _set_default_headers
137
+ adapter.headers(
138
+ 'X-Rester-Correlation-ID' => Rester.correlation_id,
139
+ 'X-Rester-Consumer-Name' => Rester.service_name,
140
+ 'X-Rester-Producer-Name' => name
141
+ )
97
142
  end
98
143
 
99
144
  def _path_with_version(path)
100
145
  Utils.join_paths("/v#{version}", path)
101
146
  end
102
147
 
103
- def _process_response(path, status, body)
148
+ def _process_response(start_time, verb, path, status, headers, body)
149
+ elapsed_ms = (Time.now.to_f - start_time) * 1000
104
150
  response = Response.new(status, _parse_json(body))
151
+ @_producer_name = headers['X-Rester-Producer-Name']
152
+ logger.info("received status #{status} after %0.3fms" % elapsed_ms)
105
153
 
106
154
  unless [200, 201, 400].include?(status)
107
155
  case status
data/lib/rester/errors.rb CHANGED
@@ -23,11 +23,17 @@ module Rester
23
23
  # Client Errors
24
24
  class ClientError < Error; end
25
25
  class CircuitOpenError < ClientError; end
26
+ class ConnectionError < ClientError; end
26
27
 
27
28
  #############
28
29
  # Stub Errors
29
30
  class StubError < Error; end
30
31
 
32
+ #############
33
+ # RSpec Errors
34
+ class RSpecError < Error; end
35
+ class TestError < RSpecError; end
36
+
31
37
  #############
32
38
  # Http Errors
33
39
  class HttpError < Error; end
data/lib/rester/rspec.rb CHANGED
@@ -17,7 +17,7 @@ RSpec::Matchers.define :include_stub_response do |stub|
17
17
  end
18
18
  }
19
19
 
20
- failure_message { |actual| failure }
20
+ failure_message { failure }
21
21
  end
22
22
 
23
23
  RSpec.configure do |config|
@@ -31,7 +31,7 @@ RSpec.configure do |config|
31
31
  raise "invalid service to test"
32
32
  end
33
33
 
34
- @rester_adapter = Rester::Client::Adapters::LocalAdapter.new(klass, {})
34
+ @rester_adapter = Rester::Client::Adapters::LocalAdapter.new(klass)
35
35
 
36
36
  _validate_test_coverage(ex)
37
37
  end
@@ -40,7 +40,7 @@ RSpec.configure do |config|
40
40
  _setup_example(ex) unless ex.pending?
41
41
  end
42
42
 
43
- config.after :each, rester: // do |ex|
43
+ config.after :each, rester: // do
44
44
  if defined?(service_response_code)
45
45
  expect(service_response_code).to eq stub_response_code
46
46
  end
@@ -67,10 +67,9 @@ RSpec.configure do |config|
67
67
 
68
68
  begin
69
69
  spec = @rester_stub[path][verb][context]
70
+ _stub_path_not_found(path, verb, context) unless spec
70
71
  rescue NoMethodError
71
- fail Rester::Errors::StubError,
72
- "Could not find path: #{path.inspect} verb: #{verb.inspect} context: "\
73
- "#{context.inspect} in #{@rester_stub_filepath}"
72
+ _stub_path_not_found(path, verb, context)
74
73
  end
75
74
 
76
75
  ##
@@ -106,6 +105,12 @@ RSpec.configure do |config|
106
105
  ex.example_group.let(:subject) { service_response }
107
106
  end
108
107
 
108
+ def _stub_path_not_found(path, verb, context)
109
+ fail Rester::Errors::TestError,
110
+ "Could not find path: #{path.inspect} verb: #{verb.inspect} context: "\
111
+ "#{context.inspect} in #{@rester_stub_filepath}"
112
+ end
113
+
109
114
  ##
110
115
  # Check to see if each stub example has a corresponding test written for it
111
116
  def _validate_test_coverage(ex)
@@ -164,24 +169,24 @@ RSpec.configure do |config|
164
169
  # }
165
170
  # }
166
171
  def _missing_stub_tests(tests)
167
- @rester_stub.reject { |k, _|
172
+ Hash[@rester_stub.reject { |k, _|
168
173
  ['version', 'consumer', 'producer'].include?(k)
169
174
  }.map { |path, verbs|
170
175
  [
171
176
  path,
172
- verbs.map { |verb, contexts|
177
+ Hash[verbs.map { |verb, contexts|
173
178
  [
174
179
  verb,
175
- contexts.map { |context, _|
180
+ Hash[contexts.map { |context, _|
176
181
  [
177
182
  context,
178
183
  !!(tests[path] && tests[path][verb] && tests[path][verb][context])
179
184
  ]
180
- }.to_h.reject { |_, v| v }
185
+ }].reject { |_, v| v }
181
186
  ]
182
- }.to_h.reject { |_, v| v.empty? }
187
+ }].reject { |_, v| v.empty? }
183
188
  ]
184
- }.to_h.reject { |_, v| v.empty? }
189
+ }].reject { |_, v| v.empty? }
185
190
  end
186
191
 
187
192
  def _find_child_with_description(group, description)
@@ -1,5 +1,5 @@
1
1
  module Rester
2
- module Middleware
2
+ module Service::Middleware
3
3
  class Base
4
4
  attr_reader :app
5
5
  attr_reader :options
@@ -40,5 +40,5 @@ module Rester
40
40
  Errors.throw_error!(klass, message)
41
41
  end
42
42
  end # Base
43
- end # Middleware
43
+ end # Service::Middleware
44
44
  end # Rester
@@ -1,7 +1,7 @@
1
1
  require 'json'
2
2
 
3
3
  module Rester
4
- module Middleware
4
+ module Service::Middleware
5
5
  ##
6
6
  # Provides error handling for Rester. Should be mounted above all other
7
7
  # Rester middleware.
@@ -15,6 +15,7 @@ module Rester
15
15
  end
16
16
  }
17
17
 
18
+ service.logger.error(error.inspect)
18
19
  _error_to_response(error).finish
19
20
  end
20
21
 
@@ -68,5 +69,5 @@ module Rester
68
69
  end
69
70
  end
70
71
  end # ErrorHandling
71
- end # Middleware
72
+ end # Service::Middleware
72
73
  end # Rester
@@ -4,7 +4,7 @@ require 'active_support/inflector'
4
4
  NewRelic::Agent.manual_start unless defined?(Rails)
5
5
 
6
6
  module Rester
7
- module Middleware
7
+ module Service::Middleware
8
8
  class NewRelic < Base
9
9
  include ::NewRelic::Agent::Instrumentation::ControllerInstrumentation
10
10
 
@@ -1,15 +1,15 @@
1
1
  module Rester
2
- module Middleware
2
+ module Service::Middleware
3
3
  ##
4
4
  # Provides a basic status check. Used by the Client#connected? method.
5
5
  class Ping < Base
6
6
  def call(env)
7
- if %r{\A/ping\z}.match(env['REQUEST_PATH'])
7
+ if %r{\A/ping\z}.match(Rester.request.path_info)
8
8
  [200, {}, []]
9
9
  else
10
10
  super
11
11
  end
12
12
  end
13
13
  end # Ping
14
- end # Middleware
14
+ end # Service::Middleware
15
15
  end # Rester
@@ -0,0 +1,30 @@
1
+ module Rester
2
+ module Service::Middleware
3
+ ##
4
+ # Create a Request object for this thread, store the correlation ID, and
5
+ # perform the necessary logging. Cleanup the request once it's complete.
6
+ class RequestHandler < Base
7
+ def call(env)
8
+ Rester.begin_request
9
+ Rester.request = request = Rester::Service::Request.new(env)
10
+ Rester.correlation_id = request.correlation_id
11
+ Rester.request_info[:producer_name] = service.name
12
+ Rester.request_info[:consumer_name] = request.consumer_name
13
+ Rester.request_info[:path] = request.path_info
14
+ Rester.request_info[:verb] = request.request_method
15
+
16
+ service.logger.info('request received')
17
+
18
+ start_time = Time.now.to_f
19
+ super.tap { |response|
20
+ elapsed_ms = (Time.now.to_f - start_time) * 1000
21
+ response[1]["X-Rester-Producer-Name"] = service.name
22
+ service.logger.info("responding with #{response[0]} after %0.3fms" %
23
+ elapsed_ms)
24
+ }
25
+ ensure
26
+ Rester.end_request
27
+ end
28
+ end # RequestHandler
29
+ end # Service::Middleware
30
+ end # Rester
@@ -0,0 +1,11 @@
1
+ module Rester
2
+ class Service
3
+ module Middleware
4
+ autoload(:Base, 'rester/service/middleware/base')
5
+ autoload(:ErrorHandling, 'rester/service/middleware/error_handling')
6
+ autoload(:Ping, 'rester/service/middleware/ping')
7
+ autoload(:NewRelic, 'rester/service/middleware/new_relic')
8
+ autoload(:RequestHandler, 'rester/service/middleware/request_handler')
9
+ end
10
+ end
11
+ end
@@ -6,6 +6,9 @@ module Rester
6
6
  attr_reader :version
7
7
  attr_reader :object_chain
8
8
 
9
+ CUSTOM_FIELDS = ['correlation_id', 'producer_name',
10
+ 'consumer_name'].freeze
11
+
9
12
  def initialize(env)
10
13
  super
11
14
  _parse_path if valid?
@@ -25,6 +28,10 @@ module Rester
25
28
  end
26
29
  end
27
30
 
31
+ CUSTOM_FIELDS.each { |field|
32
+ define_method(field) { env["HTTP_X_RESTER_#{field.upcase}"] }
33
+ }
34
+
28
35
  private
29
36
 
30
37
  def _parse_path
@@ -51,9 +51,9 @@ module Rester
51
51
  _error!("unexpected params: #{unexpected.join(', ')}")
52
52
  end
53
53
 
54
- validated_params = params.map do |key, value|
55
- [key.to_sym, validate!(key.to_sym, value)]
56
- end.to_h
54
+ validated_params = Hash[
55
+ params.map { |key, value| [key.to_sym, validate!(key.to_sym, value)] }
56
+ ]
57
57
 
58
58
  @_defaults.merge(validated_params)
59
59
  end
@@ -82,7 +82,7 @@ module Rester
82
82
  # determining which instance method to call. For example, if the request
83
83
  # method is GET: the ID being specified will call the `get` method and if
84
84
  # it's not specified then it will call the `search` method.
85
- def process(request_method, id_provided, params={})
85
+ def process(request_method, id_provided, params = {})
86
86
  meth = (id_provided ? REQUEST_METHOD_TO_IDENTIFIED_METHOD
87
87
  : REQUEST_METHOD_TO_UNIDENTIFIED_METHOD)[request_method]
88
88
 
@@ -1,17 +1,22 @@
1
1
  require 'uri'
2
2
  require 'rack'
3
3
  require 'active_support/inflector'
4
+ require 'logger'
4
5
 
5
6
  module Rester
6
7
  class Service
7
- autoload(:Request, 'rester/service/request')
8
- autoload(:Resource, 'rester/service/resource')
8
+ autoload(:Request, 'rester/service/request')
9
+ autoload(:Resource, 'rester/service/resource')
10
+ autoload(:Middleware, 'rester/service/middleware')
11
+
12
+ attr_reader :logger
9
13
 
10
14
  ##
11
15
  # The base set of middleware to use for every service.
12
16
  # Middleware will be executed in the order specified.
13
17
  BASE_MIDDLEWARE = [
14
18
  Rack::Head,
19
+ Middleware::RequestHandler,
15
20
  Middleware::ErrorHandling,
16
21
  Middleware::Ping
17
22
  ].freeze
@@ -57,6 +62,10 @@ module Rester
57
62
  }.map(&:downcase).map(&:to_sym)
58
63
  end
59
64
 
65
+ def service_name
66
+ @__name ||= name.split('::').last
67
+ end
68
+
60
69
  def version_module(version)
61
70
  (@__version_modules ||= {})[version.to_sym] ||= _load_version_module(version)
62
71
  end
@@ -81,6 +90,19 @@ module Rester
81
90
  end
82
91
  end # Class methods
83
92
 
93
+ def logger
94
+ @_logger || Rester.logger
95
+ end
96
+
97
+ def logger=(new_logger)
98
+ new_logger = Utils::LoggerWrapper.new(new_logger) if new_logger
99
+ @_logger = new_logger
100
+ end
101
+
102
+ def name
103
+ self.class.service_name
104
+ end
105
+
84
106
  ##
85
107
  # To be called by Rack. Wraps the app in middleware.
86
108
  def rack_call(env)
@@ -102,7 +124,7 @@ module Rester
102
124
  # Calls methods that may modify instance variables, so the instance should
103
125
  # be dup'd beforehand.
104
126
  def call!(env)
105
- _process_request(Request.new(env))
127
+ _process_request(Rester.request)
106
128
  end
107
129
 
108
130
  private
@@ -142,7 +164,6 @@ module Rester
142
164
  # request.
143
165
  def _call_method(request)
144
166
  params = request.params
145
- retval = nil
146
167
  resource_obj = nil
147
168
  resource_id = nil
148
169
 
@@ -0,0 +1,41 @@
1
+ module Rester
2
+ module Utils
3
+ class LoggerWrapper
4
+ attr_reader :logger
5
+
6
+ def initialize(logger = Logger.new(STDOUT))
7
+ @logger = logger
8
+ end
9
+
10
+ (Logger::SEV_LABEL - ['ANY']).map(&:downcase).map(&:to_sym).each do |sev|
11
+ define_method(sev) { |msg| _log(sev, msg) if logger }
12
+ end
13
+
14
+ private
15
+
16
+ def method_missing(meth, *args, &block)
17
+ logger.public_send(meth, *args, &block)
18
+ end
19
+
20
+ def respond_to_missing?(*args)
21
+ logger.respond_to?(*args)
22
+ end
23
+
24
+ def _log(level, msg)
25
+ if Rester.processing_request?
26
+ producer_name = Rester.request_info[:producer_name]
27
+ consumer_name = Rester.request_info[:consumer_name]
28
+ path = Rester.request_info[:path]
29
+ verb = Rester.request_info[:verb]
30
+ verb = verb && verb.upcase
31
+
32
+ msg = "Correlation-ID=#{Rester.correlation_id} Consumer=" \
33
+ "#{consumer_name} Producer=#{producer_name} #{verb} " \
34
+ "#{path} - #{msg}"
35
+ end
36
+
37
+ logger.public_send(level, msg)
38
+ end
39
+ end # LoggerWrapper
40
+ end # Utils
41
+ end # Rester
@@ -92,7 +92,7 @@ module Rester
92
92
  # Takes a response key (e.g., "response[successful=false]") and parses out
93
93
  # the tags (e.g., {"successful" => false})
94
94
  def _parse_tags(path, verb, context, resp_key)
95
- DEFAULT_TAGS.merge(resp_key.scan(/(\w+) *= *(\w+)/).to_h).tap { |tags|
95
+ DEFAULT_TAGS.merge(Hash[resp_key.scan(/(\w+) *= *(\w+)/)]).tap { |tags|
96
96
  _validate_tags(path, verb, context, tags)
97
97
  }
98
98
  end
data/lib/rester/utils.rb CHANGED
@@ -6,6 +6,7 @@ module Rester
6
6
  autoload(:StubFile, 'rester/utils/stub_file')
7
7
  autoload(:RSpec, 'rester/utils/rspec')
8
8
  autoload(:CircuitBreaker, 'rester/utils/circuit_breaker')
9
+ autoload(:LoggerWrapper, 'rester/utils/logger_wrapper')
9
10
 
10
11
  class << self
11
12
  ##
@@ -1,3 +1,3 @@
1
1
  module Rester
2
- VERSION = '0.5.1'
2
+ VERSION = '0.5.2'
3
3
  end
data/lib/rester.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  require 'rester/version'
2
2
  require 'rack'
3
+ require 'securerandom'
4
+ require 'active_support/core_ext/class/subclasses'
3
5
 
4
6
  module Rester
5
7
  require 'rester/railtie' if defined?(Rails)
@@ -9,17 +11,92 @@ module Rester
9
11
  autoload(:Utils, 'rester/utils')
10
12
  autoload(:Middleware, 'rester/middleware')
11
13
 
14
+ @_request_infos ||= ThreadSafe::Cache.new
15
+
16
+ # Set up the Client middleware if it's a Rails application
17
+ if defined?(Rails) && Rails.application
18
+ Rails.configuration.middleware.use(Client::Middleware::RequestHandler)
19
+ end
20
+
12
21
  class << self
22
+ def logger
23
+ @_logger ||= Utils::LoggerWrapper.new
24
+ end
25
+
26
+ def logger=(new_logger)
27
+ @_logger = Utils::LoggerWrapper.new(new_logger)
28
+ end
29
+
13
30
  def load_tasks
14
31
  Dir[
15
32
  File.expand_path("../../tasks", __FILE__) + '/**.rake'
16
33
  ].each { |rake_file| load rake_file }
17
34
  end
18
35
 
19
- def connect(service, params={})
36
+ def connect(service, params = {})
20
37
  adapter_opts = Client::Adapters.extract_opts(params)
21
38
  adapter = Client::Adapters.connect(service, adapter_opts)
22
39
  Client.new(adapter, params)
23
40
  end
41
+
42
+ def service_name
43
+ @_service_name ||= _get_service_name
44
+ end
45
+
46
+ def service_name=(name)
47
+ @_service_name = name
48
+ end
49
+
50
+ def begin_request
51
+ self.request_info = {}
52
+ end
53
+
54
+ def end_request
55
+ self.request_info = nil
56
+ end
57
+
58
+ def processing_request?
59
+ !!request_info
60
+ end
61
+
62
+ def request_info
63
+ @_request_infos[Thread.current.object_id]
64
+ end
65
+
66
+ def request_info=(info)
67
+ if info.nil?
68
+ @_request_infos.delete(Thread.current.object_id)
69
+ else
70
+ @_request_infos[Thread.current.object_id] = info
71
+ end
72
+ end
73
+
74
+ def request
75
+ request_info[:request]
76
+ end
77
+
78
+ def request=(request)
79
+ request_info[:request] = request
80
+ end
81
+
82
+ def correlation_id
83
+ request_info && request_info[:correlation_id]
84
+ end
85
+
86
+ def correlation_id=(correlation_id)
87
+ request_info[:correlation_id] = correlation_id
88
+ end
89
+
90
+ private
91
+
92
+ def _get_service_name
93
+ if defined?(Rails) && Rails.application
94
+ Rails.application.class.parent_name
95
+ else
96
+ services = Service.descendants
97
+ fail "Define a service name" if services.empty?
98
+ services.first.service_name
99
+ end
100
+ end
24
101
  end # Class Methods
25
102
  end # Rester
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rester
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Honer
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-01-07 00:00:00.000000000 Z
12
+ date: 2016-02-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rack
@@ -45,6 +45,20 @@ dependencies:
45
45
  - - ">="
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
+ - !ruby/object:Gem::Dependency
49
+ name: thread_safe
50
+ requirement: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
48
62
  - !ruby/object:Gem::Dependency
49
63
  name: rspec
50
64
  requirement: !ruby/object:Gem::Requirement
@@ -145,22 +159,27 @@ files:
145
159
  - lib/rester/client/adapters/http_adapter/connection.rb
146
160
  - lib/rester/client/adapters/local_adapter.rb
147
161
  - lib/rester/client/adapters/stub_adapter.rb
162
+ - lib/rester/client/middleware.rb
163
+ - lib/rester/client/middleware/base.rb
164
+ - lib/rester/client/middleware/request_handler.rb
148
165
  - lib/rester/client/resource.rb
149
166
  - lib/rester/client/response.rb
150
167
  - lib/rester/errors.rb
151
- - lib/rester/middleware.rb
152
- - lib/rester/middleware/base.rb
153
- - lib/rester/middleware/error_handling.rb
154
- - lib/rester/middleware/new_relic.rb
155
- - lib/rester/middleware/ping.rb
156
168
  - lib/rester/railtie.rb
157
169
  - lib/rester/rspec.rb
158
170
  - lib/rester/service.rb
171
+ - lib/rester/service/middleware.rb
172
+ - lib/rester/service/middleware/base.rb
173
+ - lib/rester/service/middleware/error_handling.rb
174
+ - lib/rester/service/middleware/new_relic.rb
175
+ - lib/rester/service/middleware/ping.rb
176
+ - lib/rester/service/middleware/request_handler.rb
159
177
  - lib/rester/service/request.rb
160
178
  - lib/rester/service/resource.rb
161
179
  - lib/rester/service/resource/params.rb
162
180
  - lib/rester/utils.rb
163
181
  - lib/rester/utils/circuit_breaker.rb
182
+ - lib/rester/utils/logger_wrapper.rb
164
183
  - lib/rester/utils/rspec.rb
165
184
  - lib/rester/utils/stub_file.rb
166
185
  - lib/rester/version.rb
@@ -184,7 +203,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
184
203
  version: '0'
185
204
  requirements: []
186
205
  rubyforge_project:
187
- rubygems_version: 2.4.6
206
+ rubygems_version: 2.4.8
188
207
  signing_key:
189
208
  specification_version: 4
190
209
  summary: A framework for creating simple RESTful interfaces between services.
@@ -1,8 +0,0 @@
1
- module Rester
2
- module Middleware
3
- autoload(:Base, 'rester/middleware/base')
4
- autoload(:ErrorHandling, 'rester/middleware/error_handling')
5
- autoload(:Ping, 'rester/middleware/ping')
6
- autoload(:NewRelic, 'rester/middleware/new_relic')
7
- end
8
- end