saddle 0.0.5 → 0.0.6
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 +8 -8
- data/lib/saddle/middleware/logging/airbrake.rb +35 -0
- data/lib/saddle/middleware/logging/statsd.rb +49 -0
- data/lib/saddle/middleware/request/encode_json.rb +34 -0
- data/lib/saddle/middleware/request/retry.rb +55 -0
- data/lib/saddle/middleware/request/url_encoded.rb +86 -0
- data/lib/saddle/middleware/response/default_response.rb +24 -0
- data/lib/saddle/middleware/response/parse_json.rb +45 -0
- data/lib/saddle/options.rb +11 -17
- data/lib/saddle/requester.rb +34 -40
- data/lib/saddle/version.rb +1 -1
- data/spec/requester/get_spec.rb +40 -0
- data/spec/requester/post_spec.rb +56 -0
- data/spec/requester/retry_spec.rb +54 -0
- metadata +15 -8
- data/lib/saddle/middleware/airbrake_logging.rb +0 -31
- data/lib/saddle/middleware/default_response.rb +0 -24
- data/lib/saddle/middleware/parse_json.rb +0 -36
- data/lib/saddle/middleware/statsd_logging.rb +0 -45
- data/spec/saddle_client_spec.rb +0 -34
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NDdlNWQ0MWNlODhhYTQ2NTMwZWQ2MDhkMjRlOGQ1YTQ1MTBhMjNjMA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YmE2MmYxZjQyNjhmYTJhNzhkZjk3MDcwMjdhNjgxNTI5ZWJlZDAyZA==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MWQ4Y2ZiOThmMGIwNjNiMDQ5M2EwMTNkZjdkMmIyMjFhOGFiNmU3NTNjMjQy
|
10
|
+
NGI4MmQyNWNmOTY5M2Y5MjJmMTg5ZGRjMThjYWUwZTRlZWQ3MDhkZDdiODIw
|
11
|
+
NWQwMWM4YTZmNjVmNmZjZjc3NjIwZDlhMGRlMDYzOWM3M2Y0ZTQ=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ODM4ZGM4ZTdlYTM3OGY3NzNkYTZlOTQ3ZWY0N2Y2MzViZjgzZWFiZjA2Njg4
|
14
|
+
Mjc4Y2NiNGVkYmZiZDhjYTliMzZmYmE0NjhkNDQ3YmY4MTQ0NjIzMzk0ZTkx
|
15
|
+
ZTczYmRkYTViOWIzODY0NWI0MGY3YWVmNDIzMDYxYTE2MDIwMmU=
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'airbrake'
|
2
|
+
require 'faraday'
|
3
|
+
|
4
|
+
|
5
|
+
|
6
|
+
module Saddle::Middleware
|
7
|
+
module Logging
|
8
|
+
|
9
|
+
# Public: Reports exceptions to Airbrake
|
10
|
+
#
|
11
|
+
class AirbrakeLogger < Faraday::Middleware
|
12
|
+
|
13
|
+
def initialize(app, airbrake_api_key=nil)
|
14
|
+
super(app)
|
15
|
+
@airbrake_api_key = airbrake_api_key
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(env)
|
19
|
+
begin
|
20
|
+
@app.call(env)
|
21
|
+
rescue => e
|
22
|
+
# If we don't have an api key, use the default config
|
23
|
+
if @airbrake_api_key
|
24
|
+
::Airbrake.notify(e, {:api_key => @airbrake_api_key} )
|
25
|
+
else
|
26
|
+
::Airbrake.notify(e)
|
27
|
+
end
|
28
|
+
# Re-raise the error
|
29
|
+
raise
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'statsd'
|
2
|
+
require 'faraday'
|
3
|
+
|
4
|
+
|
5
|
+
|
6
|
+
|
7
|
+
module Saddle::Middleware
|
8
|
+
module Logging
|
9
|
+
|
10
|
+
# Public: Wraps request with statsd logging
|
11
|
+
# Expects statsd_path in request options. However, if using saddle and no statsd_path is specified
|
12
|
+
# will read call_chain and action and use them to construct a statsd_path
|
13
|
+
class StatsdLogger < Faraday::Middleware
|
14
|
+
attr_accessor :graphite_host, :graphite_port, :namespace
|
15
|
+
|
16
|
+
def initialize(app, graphite_host, graphite_port=nil, namespace=nil)
|
17
|
+
super(app)
|
18
|
+
@graphite_host = graphite_host
|
19
|
+
@graphite_port = graphite_port
|
20
|
+
@namespace = namespace
|
21
|
+
end
|
22
|
+
|
23
|
+
def statsd
|
24
|
+
@statsd ||= begin
|
25
|
+
client = ::Statsd.new(@graphite_host, @graphite_port)
|
26
|
+
client.namespace = @namespace if @namespace
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def call(env)
|
31
|
+
statsd_path = nil
|
32
|
+
if env[:request][:statsd_path]
|
33
|
+
statsd_path = env[:request][:statsd_path]
|
34
|
+
elsif env[:request][:saddle] && env[:request][:saddle][:call_chain] && env[:request][:saddle][:action]
|
35
|
+
statsd_path = (env[:request][:saddle][:call_chain] + [env[:request][:saddle][:action]]).join('.')
|
36
|
+
end
|
37
|
+
|
38
|
+
if statsd_path
|
39
|
+
self.statsd.time statsd_path do
|
40
|
+
@app.call(env)
|
41
|
+
end
|
42
|
+
else
|
43
|
+
@app.call(env)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
module Saddle::Middleware
|
6
|
+
module Request
|
7
|
+
|
8
|
+
# Request middleware that encodes the body as JSON.
|
9
|
+
#
|
10
|
+
# Make sure you set request[:request_style] = :json
|
11
|
+
# for it to be activated.
|
12
|
+
|
13
|
+
class JsonEncoded < Faraday::Middleware
|
14
|
+
CONTENT_TYPE = 'Content-Type'.freeze
|
15
|
+
MIME_TYPE = 'application/json'.freeze
|
16
|
+
|
17
|
+
dependency do
|
18
|
+
require 'json' unless defined?(::JSON)
|
19
|
+
end
|
20
|
+
|
21
|
+
def call(env)
|
22
|
+
if env[:request][:request_style] == :json
|
23
|
+
# Make sure we're working with a valid body that's not a String
|
24
|
+
if env[:body] and !env[:body].respond_to?(:to_str)
|
25
|
+
env[:request_headers][CONTENT_TYPE] ||= MIME_TYPE
|
26
|
+
env[:body] = ::JSON.dump(env[:body])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
@app.call env
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
module Saddle::Middleware
|
6
|
+
module Request
|
7
|
+
|
8
|
+
# Catches exceptions and retries each request a limited number of times.
|
9
|
+
#
|
10
|
+
# By default, it retries 2 times and performs exponential backoff, starting
|
11
|
+
# at 50ms
|
12
|
+
class Retry < Faraday::Middleware
|
13
|
+
def initialize(app, ignored_exceptions=[])
|
14
|
+
super(app)
|
15
|
+
@ignored_exceptions = ignored_exceptions
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(env)
|
19
|
+
retries = env[:request][:num_retries] || 2
|
20
|
+
backoff = env[:request][:retry_backoff] || 0.050 # ms
|
21
|
+
begin
|
22
|
+
@app.call(self.class.deep_copy(env))
|
23
|
+
rescue => e
|
24
|
+
unless @ignored_exceptions.include?(e.class)
|
25
|
+
# Retry a limited number of times
|
26
|
+
if retries > 0
|
27
|
+
retries -= 1
|
28
|
+
sleep(backoff) if backoff > 0.0
|
29
|
+
backoff *= 2
|
30
|
+
retry
|
31
|
+
end
|
32
|
+
end
|
33
|
+
# Re-raise if we're out of retries or it's not handled
|
34
|
+
raise
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.deep_copy(value)
|
39
|
+
if value.is_a?(Hash)
|
40
|
+
result = value.clone
|
41
|
+
value.each{|k, v| result[k] = deep_copy(v)}
|
42
|
+
result
|
43
|
+
elsif value.is_a?(Array)
|
44
|
+
result = value.clone
|
45
|
+
result.clear
|
46
|
+
value.each{|v| result << deep_copy(v)}
|
47
|
+
result
|
48
|
+
else
|
49
|
+
value
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
module Saddle::Middleware
|
6
|
+
module Request
|
7
|
+
|
8
|
+
# This magically handles converting your body from a hash
|
9
|
+
# into an url-encoded (or multipart if needed) request
|
10
|
+
|
11
|
+
# Make sure you set request[:request_style] = :urlencoded
|
12
|
+
# for it to be activated.
|
13
|
+
|
14
|
+
class UrlEncoded < Faraday::Middleware
|
15
|
+
CONTENT_TYPE = 'Content-Type'.freeze unless defined? CONTENT_TYPE
|
16
|
+
|
17
|
+
URL_ENCODED_MIME_TYPE = 'application/x-www-form-urlencoded'.freeze
|
18
|
+
MULTIPART_MIME_TYPE = 'multipart/form-data'.freeze
|
19
|
+
|
20
|
+
VALID_MIME_TYPES = [URL_ENCODED_MIME_TYPE, MULTIPART_MIME_TYPE]
|
21
|
+
|
22
|
+
DEFAULT_MULTIPART_BOUNDARY = "-^---_---^-".freeze
|
23
|
+
|
24
|
+
|
25
|
+
def call(env)
|
26
|
+
if env[:request][:request_style] == :urlencoded
|
27
|
+
# Make sure we're working with a valid body that's not a String
|
28
|
+
if env[:body] and !env[:body].respond_to?(:to_str)
|
29
|
+
if has_multipart?(env[:body])
|
30
|
+
env[:request][:boundary] ||= DEFAULT_MULTIPART_BOUNDARY
|
31
|
+
env[:request_headers][CONTENT_TYPE] ||= MULTIPART_MIME_TYPE
|
32
|
+
env[:request_headers][CONTENT_TYPE] += ";boundary=#{env[:request][:boundary]}"
|
33
|
+
env[:body] = create_multipart(env, env[:body])
|
34
|
+
else
|
35
|
+
env[:request_headers][CONTENT_TYPE] ||= URL_ENCODED_MIME_TYPE
|
36
|
+
env[:body] = Faraday::Utils::ParamsHash[env[:body]].to_query
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
@app.call env
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def has_multipart?(obj)
|
47
|
+
# string is an enum in 1.8, returning list of itself
|
48
|
+
if obj.respond_to?(:each) && !obj.is_a?(String)
|
49
|
+
(obj.respond_to?(:values) ? obj.values : obj).each do |val|
|
50
|
+
return true if (val.respond_to?(:content_type) || has_multipart?(val))
|
51
|
+
end
|
52
|
+
end
|
53
|
+
false
|
54
|
+
end
|
55
|
+
|
56
|
+
def create_multipart(env, params)
|
57
|
+
boundary = env[:request][:boundary]
|
58
|
+
parts = process_params(params) do |key, value|
|
59
|
+
Faraday::Parts::Part.new(boundary, key, value)
|
60
|
+
end
|
61
|
+
parts << Faraday::Parts::EpiloguePart.new(boundary)
|
62
|
+
|
63
|
+
body = Faraday::CompositeReadIO.new(parts)
|
64
|
+
env[:request_headers][Faraday::Env::ContentLength] = body.length.to_s
|
65
|
+
body
|
66
|
+
end
|
67
|
+
|
68
|
+
def process_params(params, prefix = nil, pieces = nil, &block)
|
69
|
+
params.inject(pieces || []) do |all, (key, value)|
|
70
|
+
key = "#{prefix}[#{key}]" if prefix
|
71
|
+
|
72
|
+
case value
|
73
|
+
when Array
|
74
|
+
values = value.inject([]) { |a,v| a << [nil, v] }
|
75
|
+
process_params(values, key, all, &block)
|
76
|
+
when Hash
|
77
|
+
process_params(value, key, all, &block)
|
78
|
+
else
|
79
|
+
all << block.call(key, value)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
|
3
|
+
|
4
|
+
module Saddle::Middleware
|
5
|
+
module Response
|
6
|
+
|
7
|
+
# Public: Returns a default response in the case of an exception
|
8
|
+
# Expects default_response to be defined in the request of connection options, otherwise rethrows exception
|
9
|
+
class DefaultResponse < Faraday::Middleware
|
10
|
+
def call(env)
|
11
|
+
begin
|
12
|
+
@app.call(env)
|
13
|
+
rescue Faraday::Error
|
14
|
+
if res = env[:request][:default_response]
|
15
|
+
return ::Faraday::Response.new(:body => res)
|
16
|
+
else
|
17
|
+
raise
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
module Saddle::Middleware
|
6
|
+
module Response
|
7
|
+
|
8
|
+
# Public: Parse response bodies as JSON.
|
9
|
+
class ParseJson < Faraday::Middleware
|
10
|
+
CONTENT_TYPE = 'Content-Type'.freeze
|
11
|
+
MIME_TYPE = 'application/json'.freeze
|
12
|
+
|
13
|
+
dependency do
|
14
|
+
require 'json' unless defined?(::JSON)
|
15
|
+
end
|
16
|
+
|
17
|
+
def call(env)
|
18
|
+
if parse_response?(env)
|
19
|
+
# Make sure we're working with a valid body that's not a String
|
20
|
+
if env.body and !env.body.respond_to?(:to_str)
|
21
|
+
env[:request_headers][CONTENT_TYPE] ||= MIME_TYPE
|
22
|
+
env.body = ::JSON.dump env.body
|
23
|
+
end
|
24
|
+
end
|
25
|
+
@app.call env
|
26
|
+
end
|
27
|
+
|
28
|
+
def parse_response?(env)
|
29
|
+
has_body?(env) and (response_type(env) == MIME_TYPE)
|
30
|
+
end
|
31
|
+
|
32
|
+
def has_body?(env)
|
33
|
+
body = env[:body] and !(body.respond_to?(:to_str) and body.empty?)
|
34
|
+
end
|
35
|
+
|
36
|
+
def response_type(env)
|
37
|
+
return nil unless env[:response_headers]
|
38
|
+
type = env[:response_headers][CONTENT_TYPE].to_s
|
39
|
+
type = type.split(';', 2).first if type.index(';')
|
40
|
+
type
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
data/lib/saddle/options.rb
CHANGED
@@ -13,11 +13,10 @@ module Saddle::Options
|
|
13
13
|
:host => host,
|
14
14
|
:port => port,
|
15
15
|
:use_ssl => use_ssl,
|
16
|
-
:
|
17
|
-
:response_style => response_style,
|
16
|
+
:request_style => request_style,
|
18
17
|
:num_retries => num_retries,
|
19
18
|
:timeout => timeout,
|
20
|
-
:additional_middlewares => additional_middlewares,
|
19
|
+
:additional_middlewares => @@additional_middlewares,
|
21
20
|
:stubs => stubs,
|
22
21
|
}
|
23
22
|
end
|
@@ -39,13 +38,7 @@ module Saddle::Options
|
|
39
38
|
|
40
39
|
# The POST/PUT style for this client
|
41
40
|
# options are [:json, :urlencoded]
|
42
|
-
def
|
43
|
-
:json
|
44
|
-
end
|
45
|
-
|
46
|
-
# How to parse results
|
47
|
-
# options are [:json, :urlencoded]
|
48
|
-
def response_style
|
41
|
+
def request_style
|
49
42
|
:json
|
50
43
|
end
|
51
44
|
|
@@ -59,17 +52,18 @@ module Saddle::Options
|
|
59
52
|
30
|
60
53
|
end
|
61
54
|
|
62
|
-
#
|
55
|
+
# Use this to add additional middleware to the request stack
|
63
56
|
# ex:
|
64
|
-
#
|
65
|
-
#
|
66
|
-
#
|
67
|
-
#
|
57
|
+
# add_middleware({
|
58
|
+
# :klass => MyMiddleware,
|
59
|
+
# :args => [arg1, arg2],
|
60
|
+
# })
|
68
61
|
# end
|
69
62
|
#
|
70
63
|
###
|
71
|
-
|
72
|
-
|
64
|
+
@@additional_middlewares = []
|
65
|
+
def add_middleware m
|
66
|
+
@@additional_middlewares << m
|
73
67
|
end
|
74
68
|
|
75
69
|
# If the Typhoeus adapter is being used, pass stubs to it for testing.
|
data/lib/saddle/requester.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
require 'faraday'
|
2
2
|
require 'faraday_middleware'
|
3
3
|
|
4
|
-
require 'saddle/middleware/
|
5
|
-
require 'saddle/middleware/
|
4
|
+
require 'saddle/middleware/request/encode_json'
|
5
|
+
require 'saddle/middleware/request/retry'
|
6
|
+
require 'saddle/middleware/request/url_encoded'
|
7
|
+
require 'saddle/middleware/response/default_response'
|
8
|
+
require 'saddle/middleware/response/parse_json'
|
6
9
|
require 'saddle/middleware/ruby_timeout'
|
7
10
|
|
8
11
|
|
@@ -21,8 +24,7 @@ module Saddle
|
|
21
24
|
## host - host to connect to (default: localhost)
|
22
25
|
## port - port to connect on (default: 80)
|
23
26
|
## use_ssl - true if we should use https (default: false)
|
24
|
-
##
|
25
|
-
## response_style - :json or :urlencoded (default: :json)
|
27
|
+
## request_style - :json or :urlencoded (default: :json)
|
26
28
|
## num_retries - number of times to retry each request (default: 3)
|
27
29
|
## timeout - timeout in seconds
|
28
30
|
## additional_middleware - an Array of more middlewares to apply to the top of the stack
|
@@ -35,10 +37,8 @@ module Saddle
|
|
35
37
|
raise ':port must be an integer' unless @port.is_a?(Fixnum)
|
36
38
|
@use_ssl = opt[:use_ssl] || false
|
37
39
|
raise ':use_ssl must be true or false' unless (@use_ssl.is_a?(TrueClass) || @use_ssl.is_a?(FalseClass))
|
38
|
-
@
|
39
|
-
raise ":
|
40
|
-
@response_style = opt[:response_style] || :json
|
41
|
-
raise ":response_style must be in: #{VALID_BODY_STYLES.join(',')}" unless VALID_BODY_STYLES.include?(@response_style)
|
40
|
+
@request_style = opt[:request_style] || :json
|
41
|
+
raise ":request_style must be in: #{VALID_BODY_STYLES.join(',')}" unless VALID_BODY_STYLES.include?(@request_style)
|
42
42
|
@num_retries = opt[:num_retries] || 3
|
43
43
|
raise ':num_retries must be an integer' unless @num_retries.is_a?(Fixnum)
|
44
44
|
@timeout = opt[:timeout]
|
@@ -65,33 +65,24 @@ module Saddle
|
|
65
65
|
response.body
|
66
66
|
end
|
67
67
|
|
68
|
-
#
|
69
|
-
def
|
70
|
-
response = connection.
|
68
|
+
# Make a POST request
|
69
|
+
def post(url, data={}, options={})
|
70
|
+
response = connection.post do |req|
|
71
71
|
req.options.merge! options
|
72
72
|
req.url url
|
73
|
-
|
74
|
-
case @post_style
|
75
|
-
when :json
|
76
|
-
req.headers['Content-Type'] = 'application/json'
|
77
|
-
req.body = params.to_json
|
78
|
-
when :urlencoded
|
79
|
-
req.params = params
|
80
|
-
else
|
81
|
-
raise RuntimeError(":post_style must be one of: #{VALID_POST_STYLES.join(',')}")
|
82
|
-
end
|
73
|
+
req.body = data
|
83
74
|
end
|
84
75
|
response.body
|
85
76
|
end
|
86
77
|
|
87
|
-
# Make a POST request
|
88
|
-
def post(url, params={}, options={})
|
89
|
-
post_or_put(:post, url, params, options)
|
90
|
-
end
|
91
|
-
|
92
78
|
# Make a PUT request
|
93
|
-
def put(url,
|
94
|
-
|
79
|
+
def put(url, data={}, options={})
|
80
|
+
response = connection.put do |req|
|
81
|
+
req.options.merge! options
|
82
|
+
req.url url
|
83
|
+
req.body = data
|
84
|
+
end
|
85
|
+
response.body
|
95
86
|
end
|
96
87
|
|
97
88
|
# Make a DELETE request
|
@@ -118,10 +109,12 @@ module Saddle
|
|
118
109
|
# Config options
|
119
110
|
unless @timeout.nil?
|
120
111
|
builder.options[:timeout] = @timeout
|
112
|
+
builder.options[:request_style] = @request_style
|
113
|
+
builder.options[:num_retries] = @num_retries
|
121
114
|
end
|
122
115
|
|
123
116
|
# Support default return values upon exception
|
124
|
-
builder.use Saddle::Middleware::DefaultResponse
|
117
|
+
builder.use Saddle::Middleware::Response::DefaultResponse
|
125
118
|
|
126
119
|
# Apply additional implementation-specific middlewares
|
127
120
|
@additional_middlewares.each do |m|
|
@@ -131,12 +124,18 @@ module Saddle
|
|
131
124
|
# Hard timeout on the entire request
|
132
125
|
builder.use Saddle::Middleware::RubyTimeout
|
133
126
|
|
134
|
-
#
|
135
|
-
builder.
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
127
|
+
# Request encoding
|
128
|
+
builder.use Saddle::Middleware::Request::JsonEncoded
|
129
|
+
builder.use Saddle::Middleware::Request::UrlEncoded
|
130
|
+
|
131
|
+
# Automatic retries
|
132
|
+
builder.use Saddle::Middleware::Request::Retry
|
133
|
+
|
134
|
+
# Handle parsing out the response if it's JSON
|
135
|
+
builder.use Saddle::Middleware::Response::ParseJson
|
136
|
+
|
137
|
+
# Raise exceptions on 4xx and 5xx errors
|
138
|
+
builder.use Faraday::Response::RaiseError
|
140
139
|
|
141
140
|
# Set up our adapter
|
142
141
|
if @stubs.nil?
|
@@ -146,11 +145,6 @@ module Saddle
|
|
146
145
|
# Use the test adapter
|
147
146
|
builder.adapter :test, @stubs
|
148
147
|
end
|
149
|
-
|
150
|
-
# Raise exceptions on 4xx and 5xx errors
|
151
|
-
builder.response :raise_error
|
152
|
-
# Handle parsing out the response if it's JSON
|
153
|
-
builder.use Saddle::Middleware::ParseJson
|
154
148
|
end
|
155
149
|
end
|
156
150
|
|
data/lib/saddle/version.rb
CHANGED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'saddle'
|
2
|
+
|
3
|
+
describe Saddle::Client do
|
4
|
+
|
5
|
+
context "GET requests" do
|
6
|
+
context "using the default client" do
|
7
|
+
|
8
|
+
before :each do
|
9
|
+
@stubs = Faraday::Adapter::Test::Stubs.new
|
10
|
+
@default_client = Saddle::Client.create(:stubs => @stubs)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should request properly with params" do
|
14
|
+
@stubs.get('/test?name=mike&party=true') {
|
15
|
+
[
|
16
|
+
200,
|
17
|
+
{},
|
18
|
+
'Party on!',
|
19
|
+
]
|
20
|
+
}
|
21
|
+
@default_client.requester.get(
|
22
|
+
'/test',
|
23
|
+
{'name' => 'mike', 'party' => true}
|
24
|
+
).should == 'Party on!'
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should parse JSON encoded responses" do
|
28
|
+
@stubs.get('/test.json') {
|
29
|
+
[
|
30
|
+
200,
|
31
|
+
{'Content-Type' => 'application/json'},
|
32
|
+
{'success' => true},
|
33
|
+
]
|
34
|
+
}
|
35
|
+
@default_client.requester.get('/test.json')['success'].should == true
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'saddle'
|
2
|
+
|
3
|
+
describe Saddle::Client do
|
4
|
+
|
5
|
+
context "POST requests" do
|
6
|
+
context "using the default client" do
|
7
|
+
|
8
|
+
before :each do
|
9
|
+
@stubs = Faraday::Adapter::Test::Stubs.new
|
10
|
+
@default_client = Saddle::Client.create(:stubs => @stubs)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should post empty" do
|
14
|
+
@stubs.post('/test') {
|
15
|
+
[
|
16
|
+
200,
|
17
|
+
{},
|
18
|
+
'Party on!',
|
19
|
+
]
|
20
|
+
}
|
21
|
+
@default_client.requester.post('/test').should == 'Party on!'
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should post url encoded" do
|
25
|
+
@stubs.post('/test', 'a=0&b=true&c=Wingdings') {
|
26
|
+
[
|
27
|
+
200,
|
28
|
+
{},
|
29
|
+
'Party on!',
|
30
|
+
]
|
31
|
+
}
|
32
|
+
@default_client.requester.post(
|
33
|
+
'/test',
|
34
|
+
{'a' => 0, 'b' => true, 'c' => 'Wingdings'},
|
35
|
+
{:request_style => :urlencoded}
|
36
|
+
).should == 'Party on!'
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should post JSON encoded" do
|
40
|
+
@stubs.post('/test', '{"a":0,"b":true,"c":"Wingdings"}') {
|
41
|
+
[
|
42
|
+
200,
|
43
|
+
{},
|
44
|
+
'Party on!',
|
45
|
+
]
|
46
|
+
}
|
47
|
+
@default_client.requester.post(
|
48
|
+
'/test',
|
49
|
+
{'a' => 0, 'b' => true, 'c' => 'Wingdings'},
|
50
|
+
{:request_style => :json}
|
51
|
+
).should == 'Party on!'
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'saddle'
|
2
|
+
|
3
|
+
describe Saddle::Client do
|
4
|
+
|
5
|
+
context "retry requests" do
|
6
|
+
context "using the default client" do
|
7
|
+
|
8
|
+
before :each do
|
9
|
+
@stubs = Faraday::Adapter::Test::Stubs.new
|
10
|
+
@default_client = Saddle::Client.create(:stubs => @stubs)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should retry properly with no params" do
|
14
|
+
@stubs.get('/test') {
|
15
|
+
[
|
16
|
+
500,
|
17
|
+
{},
|
18
|
+
'Failure',
|
19
|
+
]
|
20
|
+
}
|
21
|
+
@stubs.get('/test') {
|
22
|
+
[
|
23
|
+
200,
|
24
|
+
{},
|
25
|
+
'Party!',
|
26
|
+
]
|
27
|
+
}
|
28
|
+
@default_client.requester.get('/test').should == 'Party!'
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should retry properly when posting params urlencoded" do
|
32
|
+
@stubs.post('/test', '{"a":"b","c":"d"}') {
|
33
|
+
[
|
34
|
+
500,
|
35
|
+
{},
|
36
|
+
'Failure',
|
37
|
+
]
|
38
|
+
}
|
39
|
+
@stubs.post('/test', '{"a":"b","c":"d"}') {
|
40
|
+
[
|
41
|
+
200,
|
42
|
+
{},
|
43
|
+
'Party!',
|
44
|
+
]
|
45
|
+
}
|
46
|
+
@default_client.requester.post(
|
47
|
+
'/test',
|
48
|
+
{'a' => 'b', 'c' => 'd'}
|
49
|
+
).should == 'Party!'
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: saddle
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Lewis
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-04-
|
12
|
+
date: 2013-04-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -68,16 +68,21 @@ files:
|
|
68
68
|
- lib/saddle.rb
|
69
69
|
- lib/saddle/endpoint.rb
|
70
70
|
- lib/saddle/method_tree_builder.rb
|
71
|
-
- lib/saddle/middleware/
|
72
|
-
- lib/saddle/middleware/
|
73
|
-
- lib/saddle/middleware/
|
71
|
+
- lib/saddle/middleware/logging/airbrake.rb
|
72
|
+
- lib/saddle/middleware/logging/statsd.rb
|
73
|
+
- lib/saddle/middleware/request/encode_json.rb
|
74
|
+
- lib/saddle/middleware/request/retry.rb
|
75
|
+
- lib/saddle/middleware/request/url_encoded.rb
|
76
|
+
- lib/saddle/middleware/response/default_response.rb
|
77
|
+
- lib/saddle/middleware/response/parse_json.rb
|
74
78
|
- lib/saddle/middleware/ruby_timeout.rb
|
75
|
-
- lib/saddle/middleware/statsd_logging.rb
|
76
79
|
- lib/saddle/options.rb
|
77
80
|
- lib/saddle/requester.rb
|
78
81
|
- lib/saddle/version.rb
|
79
82
|
- saddle.gemspec
|
80
|
-
- spec/
|
83
|
+
- spec/requester/get_spec.rb
|
84
|
+
- spec/requester/post_spec.rb
|
85
|
+
- spec/requester/retry_spec.rb
|
81
86
|
homepage: https://github.com/mLewisLogic/saddle
|
82
87
|
licenses:
|
83
88
|
- MIT
|
@@ -104,4 +109,6 @@ specification_version: 4
|
|
104
109
|
summary: A generic client wrapper for building service-specific wrappers. Base functionality,
|
105
110
|
meant to be extended to concrete implementations.
|
106
111
|
test_files:
|
107
|
-
- spec/
|
112
|
+
- spec/requester/get_spec.rb
|
113
|
+
- spec/requester/post_spec.rb
|
114
|
+
- spec/requester/retry_spec.rb
|
@@ -1,31 +0,0 @@
|
|
1
|
-
require 'faraday'
|
2
|
-
|
3
|
-
|
4
|
-
module Saddle::Middleware
|
5
|
-
|
6
|
-
# Public: Reports exceptions to airbrake
|
7
|
-
#
|
8
|
-
class AirbrakeLogging < Faraday::Middleware
|
9
|
-
|
10
|
-
def initialize(app, airbrake_api_key)
|
11
|
-
@airbrake_api_key = airbrake_api_key
|
12
|
-
super(app)
|
13
|
-
end
|
14
|
-
|
15
|
-
def call(env)
|
16
|
-
begin
|
17
|
-
@app.call(env)
|
18
|
-
rescue => e
|
19
|
-
::Airbrake.notify(
|
20
|
-
e,
|
21
|
-
{
|
22
|
-
:api_key => @airbrake_api_key,
|
23
|
-
}
|
24
|
-
)
|
25
|
-
raise
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
end
|
30
|
-
|
31
|
-
end
|
@@ -1,24 +0,0 @@
|
|
1
|
-
require 'faraday'
|
2
|
-
|
3
|
-
|
4
|
-
module Saddle::Middleware
|
5
|
-
|
6
|
-
# Public: Returns a default response in the case of an exception
|
7
|
-
# Expects default_response to be defined in the request of connection options, otherwise rethrows exception
|
8
|
-
class DefaultResponse < Faraday::Middleware
|
9
|
-
|
10
|
-
def call(env)
|
11
|
-
begin
|
12
|
-
@app.call(env)
|
13
|
-
rescue => e
|
14
|
-
if res = env[:request][:default_response]
|
15
|
-
return ::Faraday::Response.new(:body => res)
|
16
|
-
else
|
17
|
-
raise
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
end
|
23
|
-
|
24
|
-
end
|
@@ -1,36 +0,0 @@
|
|
1
|
-
require 'faraday_middleware/response_middleware'
|
2
|
-
|
3
|
-
|
4
|
-
module Saddle::Middleware
|
5
|
-
|
6
|
-
# Public: Parse response bodies as JSON.
|
7
|
-
class ParseJson < FaradayMiddleware::ResponseMiddleware
|
8
|
-
MIME_TYPE = 'application/json'.freeze
|
9
|
-
|
10
|
-
dependency do
|
11
|
-
require 'json' unless defined?(::JSON)
|
12
|
-
end
|
13
|
-
|
14
|
-
define_parser do |body|
|
15
|
-
::JSON.parse body unless body.strip.empty?
|
16
|
-
end
|
17
|
-
|
18
|
-
|
19
|
-
def parse_response?(env)
|
20
|
-
type = response_type(env)
|
21
|
-
super and has_body?(env) and (type.empty? or type == MIME_TYPE)
|
22
|
-
end
|
23
|
-
|
24
|
-
def has_body?(env)
|
25
|
-
body = env[:body] and !(body.respond_to?(:to_str) and body.empty?)
|
26
|
-
end
|
27
|
-
|
28
|
-
def response_type(env)
|
29
|
-
type = env[:response_headers][CONTENT_TYPE].to_s
|
30
|
-
type = type.split(';', 2).first if type.index(';')
|
31
|
-
type
|
32
|
-
end
|
33
|
-
|
34
|
-
end
|
35
|
-
|
36
|
-
end
|
@@ -1,45 +0,0 @@
|
|
1
|
-
require 'statsd'
|
2
|
-
|
3
|
-
module Saddle::Middleware
|
4
|
-
|
5
|
-
# Public: Wraps request with statsd logging
|
6
|
-
# Expects statsd_path in request options. However, if using saddle and no statsd_path is specified
|
7
|
-
# will read call_chain and action and use them to construct a statsd_path
|
8
|
-
class StatsdLogging < Faraday::Middleware
|
9
|
-
attr_accessor :graphite_host, :graphite_port, :namespace
|
10
|
-
|
11
|
-
def initialize(app, graphite_host, graphite_port=nil, namespace=nil)
|
12
|
-
super(app)
|
13
|
-
@graphite_host = graphite_host
|
14
|
-
@graphite_port = graphite_port
|
15
|
-
@namespace = namespace
|
16
|
-
self.statsd
|
17
|
-
end
|
18
|
-
|
19
|
-
def statsd
|
20
|
-
if(@statsd.nil?)
|
21
|
-
@statsd = Statsd.new(@graphite_host, @graphite_port)
|
22
|
-
@statsd.namespace = @namespace if @namespace
|
23
|
-
end
|
24
|
-
return @statsd
|
25
|
-
end
|
26
|
-
|
27
|
-
def call(env)
|
28
|
-
if env[:request][:statsd_path]
|
29
|
-
statsd_path = env[:request][:statsd_path]
|
30
|
-
elsif env[:request][:saddle] && env[:request][:saddle][:call_chain] && env[:request][:saddle][:action]
|
31
|
-
statsd_path = (env[:request][:saddle][:call_chain] + [env[:request][:saddle][:action]]).join(".")
|
32
|
-
end
|
33
|
-
|
34
|
-
if statsd_path
|
35
|
-
self.statsd.time statsd_path do
|
36
|
-
@app.call(env)
|
37
|
-
end
|
38
|
-
else
|
39
|
-
@app.call(env)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
end
|
44
|
-
|
45
|
-
end
|
data/spec/saddle_client_spec.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
require 'saddle'
|
2
|
-
|
3
|
-
describe Saddle::Client do
|
4
|
-
|
5
|
-
context "instance" do
|
6
|
-
before :each do
|
7
|
-
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
|
8
|
-
stub.get('/test') {
|
9
|
-
[
|
10
|
-
200,
|
11
|
-
{'Content-Type' => 'application/x-www-form-urlencoded'},
|
12
|
-
'success'
|
13
|
-
]
|
14
|
-
}
|
15
|
-
stub.get('/test.json') {
|
16
|
-
[
|
17
|
-
200,
|
18
|
-
{'Content-Type' => 'application/json'},
|
19
|
-
{'success' => true}.to_json
|
20
|
-
]
|
21
|
-
}
|
22
|
-
end
|
23
|
-
@client = Saddle::Client.create(:stubs => stubs)
|
24
|
-
end
|
25
|
-
|
26
|
-
it "should be able to request urlencoded" do
|
27
|
-
@client.requester.get('/test').should == 'success'
|
28
|
-
end
|
29
|
-
|
30
|
-
it "should be able to request JSON encoded" do
|
31
|
-
@client.requester.get('/test.json')['success'].should == true
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|