rester 0.5.1 → 0.5.2

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 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