faraday-stack 0.1.0

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 ADDED
@@ -0,0 +1,61 @@
1
+ # Über Stack
2
+
3
+ [Faraday][] is an HTTP client lib that provides a common interface over many adapters (such as Net::HTTP) and embraces the concept of Rack middleware when processing the request/response cycle.
4
+
5
+ *“Faraday Stack”* is an add-on library that implements several middleware (such as JSON and XML parsers) and helps you build an awesome stack that covers most of your API-consuming needs.
6
+
7
+ Boring example:
8
+
9
+ require 'faraday_stack'
10
+
11
+ response = FaradayStack.get 'http://google.com'
12
+
13
+ response.headers['content-type'] #=> "text/html; charset=UTF-8"
14
+ response.headers['location'] #=> "http://www.google.com/"
15
+ puts response.body
16
+
17
+ Awesome example:
18
+
19
+ conn = FaradayStack.build 'http://github.com/api/v2'
20
+
21
+ # JSON resource
22
+ resp = conn.get 'json/repos/show/mislav/faraday-stack'
23
+ resp.body
24
+ #=> {"repository"=>{"language"=>"Ruby", "fork"=>false, ...}}
25
+
26
+ # XML resource
27
+ resp = conn.get 'xml/repos/show/mislav/faraday-stack'
28
+ resp.body.class
29
+ #=> Nokogiri::XML::Document
30
+
31
+ # 404
32
+ conn.get 'zomg/wrong/url'
33
+ #=> raises Faraday::Error::ResourceNotFound
34
+
35
+ ## Features
36
+
37
+ * parses JSON, XML & HTML
38
+ * raises exceptions on 4xx, 5xx responses
39
+ * follows redirects
40
+
41
+ Optional features:
42
+
43
+ * encode POST/PUT bodies as JSON:
44
+
45
+ conn.post(path, payload, :content_type => 'application/json')
46
+
47
+ * add `Instrumentation` middleware to instrument requests with ActiveSupport
48
+
49
+ conn.builder.insert_after Faraday::Response::RaiseError, FaradayStack::Instrumentation
50
+
51
+ * add `Caching` middleware to have GET responses cached
52
+
53
+ conn.builder.insert_before FaradayStack::ResponseJSON, FaradayStack::Caching do
54
+ ActiveSupport::Cache::FileStore.new 'tmp/cache',
55
+ :namespace => 'faraday', :expires_in => 3600
56
+ end
57
+
58
+ To see how the default stack is built, see "[faraday_stack.rb][source]".
59
+
60
+ [faraday]: https://github.com/technoweenie/faraday
61
+ [source]: https://github.com/mislav/faraday-stack/blob/master/lib/faraday_stack.rb
@@ -0,0 +1 @@
1
+ require 'faraday_stack'
@@ -0,0 +1,18 @@
1
+ require 'addressable/uri'
2
+
3
+ # fix `normalized_query` by sorting query key-value pairs
4
+ # (rejected: https://github.com/sporkmonger/addressable/issues/28)
5
+ class Addressable::URI
6
+ class << self
7
+ alias old_normalize_component normalize_component
8
+
9
+ def normalize_component(component, character_class = CharacterClasses::RESERVED + CharacterClasses::UNRESERVED)
10
+ normalized = old_normalize_component(component, character_class)
11
+ if character_class == Addressable::URI::CharacterClasses::QUERY
12
+ pairs = normalized.split('&').sort_by { |pair| pair[0, pair.index('=') || pair.length] }
13
+ normalized = pairs.join('&')
14
+ end
15
+ normalized
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,58 @@
1
+ module FaradayStack
2
+ class Caching < Faraday::Middleware
3
+ attr_reader :cache
4
+
5
+ def initialize(app, cache = nil, options = {})
6
+ super(app)
7
+ @cache = cache || Proc.new.call
8
+ @options = options
9
+ end
10
+
11
+ def call(env)
12
+ if :get == env[:method]
13
+ if env[:parallel_manager]
14
+ # callback mode
15
+ cache_on_complete(env)
16
+ else
17
+ # synchronous mode
18
+ response = cache.fetch(cache_key(env)) { @app.call(env) }
19
+ finalize_response(response, env)
20
+ end
21
+ else
22
+ @app.call(env)
23
+ end
24
+ end
25
+
26
+ def cache_key(env)
27
+ url = env[:url]
28
+ if params_to_strip.any?
29
+ url = url.dup
30
+ url.query_values = url.query_values.reject { |k,| params_to_strip.include? k }
31
+ url.normalize!
32
+ else
33
+ url = url.normalize
34
+ end
35
+ url.request_uri
36
+ end
37
+
38
+ def params_to_strip
39
+ @params_to_strip ||= Array(@options[:strip_params]).map { |p| p.to_s }
40
+ end
41
+
42
+ def cache_on_complete(env)
43
+ key = cache_key(env)
44
+ if cached_response = cache.read(key)
45
+ finalize_response(cached_response, env)
46
+ else
47
+ response = @app.call(env)
48
+ response.on_complete { cache.write(key, response) }
49
+ end
50
+ end
51
+
52
+ def finalize_response(response, env)
53
+ response = env[:response] = response.dup if response.frozen?
54
+ response.apply_request env unless response.env[:method]
55
+ response
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,43 @@
1
+ module FaradayStack
2
+ class RedirectLimitReached < Faraday::Error::ClientError
3
+ attr_reader :response
4
+
5
+ def initialize(response)
6
+ super "too many redirects; last one to: #{response['location']}"
7
+ @response = response
8
+ end
9
+ end
10
+
11
+ class FollowRedirects < Faraday::Middleware
12
+ # TODO: 307
13
+ REDIRECTS = [301, 302, 303]
14
+ # default value for max redirects followed
15
+ FOLLOW_LIMIT = 3
16
+
17
+ def initialize(app, options = {})
18
+ super(app)
19
+ @options = options
20
+ @follow_limit = options[:limit] || FOLLOW_LIMIT
21
+ end
22
+
23
+ def call(env)
24
+ process_response(@app.call(env), @follow_limit)
25
+ end
26
+
27
+ def process_response(response, follows)
28
+ response.on_complete do |env|
29
+ if redirect? response
30
+ raise RedirectLimitReached, response if follows.zero?
31
+ env[:url] += response['location']
32
+ env[:method] = :get
33
+ response = process_response(@app.call(env), follows - 1)
34
+ end
35
+ end
36
+ response
37
+ end
38
+
39
+ def redirect?(response)
40
+ REDIRECTS.include? response.status
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,26 @@
1
+ module FaradayStack
2
+ # Measures request time only in synchronous request mode.
3
+ # Sample subscriber:
4
+ #
5
+ # ActiveSupport::Notifications.subscribe('request.faraday') do |name, start_time, end_time, _, env|
6
+ # url = env[:url]
7
+ # http_method = env[:method].to_s.upcase
8
+ # duration = end_time - start_time
9
+ # $stderr.puts '[%s] %s %s (%.3f s)' % [url.host, http_method, url.request_uri, duration]
10
+ # end
11
+ class Instrumentation < Faraday::Middleware
12
+ dependency 'active_support/notifications'
13
+
14
+ def initialize(app, options = {})
15
+ super(app)
16
+ @options = options
17
+ @name = options[:name] || 'request.faraday'
18
+ end
19
+
20
+ def call(env)
21
+ ActiveSupport::Notifications.instrument(@name, env) do
22
+ @app.call(env)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,76 @@
1
+ require 'stringio'
2
+
3
+ module FaradayStack
4
+ # Wraps a handler originally written for Rack to make it compatible with Faraday.
5
+ #
6
+ # Experimental. Only handles changes in request headers.
7
+ class RackCompatible
8
+ def initialize(app, rack_handler, *args)
9
+ # tiny middleware that decomposes a Faraday::Response to standard Rack
10
+ # array: [status, headers, body]
11
+ compatible_app = lambda do |env|
12
+ restore_env(env)
13
+ response = app.call(env)
14
+ [response.status, response.headers, Array(response.body)]
15
+ end
16
+ @rack = rack_handler.new(compatible_app, *args)
17
+ end
18
+
19
+ def call(env)
20
+ prepare_env(env)
21
+ rack_response = @rack.call(env)
22
+ finalize_response(env, rack_response)
23
+ end
24
+
25
+ NonPrefixedHeaders = %w[CONTENT_LENGTH CONTENT_TYPE]
26
+
27
+ # faraday to rack-compatible
28
+ def prepare_env(env)
29
+ env[:request_headers].each do |name, value|
30
+ name = name.upcase.tr('-', '_')
31
+ name = "HTTP_#{name}" unless NonPrefixedHeaders.include? name
32
+ env[name] = value
33
+ end
34
+
35
+ url = env[:url]
36
+ env['rack.url_scheme'] = url.scheme
37
+ env['PATH_INFO'] = url.path
38
+ env['SERVER_PORT'] = url.inferred_port
39
+ env['QUERY_STRING'] = url.query
40
+ env['REQUEST_METHOD'] = env[:method].to_s.upcase
41
+
42
+ env['rack.errors'] ||= StringIO.new
43
+
44
+ env
45
+ end
46
+
47
+ # rack to faraday-compatible
48
+ def restore_env(env)
49
+ headers = env[:request_headers]
50
+ headers.clear
51
+
52
+ env.each do |name, value|
53
+ next unless String === name
54
+ if NonPrefixedHeaders.include? name or name.index('HTTP_') == 0
55
+ name = name.sub(/^HTTP_/).downcase.tr('_', '-')
56
+ headers[name] = value
57
+ end
58
+ end
59
+
60
+ env[:method] = env['REQUEST_METHOD'].downcase.to_sym
61
+ env
62
+ end
63
+
64
+ def finalize_response(env, rack_response)
65
+ status, headers, body = rack_response
66
+ body = body.inject('') { |str, part| str << part }
67
+ headers = Faraday::Utils::Headers.new(headers) unless Faraday::Utils::Headers === headers
68
+
69
+ response_env = { :status => status, :body => body, :response_headers => headers }
70
+
71
+ env[:response] ||= Faraday::Response.new({})
72
+ env[:response].env.update(response_env)
73
+ env[:response]
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,12 @@
1
+ module FaradayStack
2
+ class ResponseHTML < ResponseMiddleware
3
+ dependency do
4
+ require 'nokogiri'
5
+ Nokogiri::HTML
6
+ end
7
+
8
+ define_parser do |body|
9
+ Nokogiri::HTML body
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,32 @@
1
+ module FaradayStack
2
+ class ResponseJSON < ResponseMiddleware
3
+ adapter_name = nil
4
+
5
+ # loads the JSON decoder either from yajl-ruby or activesupport
6
+ dependency do
7
+ require 'yajl'
8
+ adapter_name = Yajl::Parser.name
9
+ end
10
+
11
+ dependency do
12
+ require 'active_support/json/decoding'
13
+ adapter_name = ActiveSupport::JSON.name
14
+ end unless loaded?
15
+
16
+ # defines a parser block depending on which adapter has loaded
17
+ case adapter_name
18
+ when 'Yajl::Parser'
19
+ define_parser do |body|
20
+ Yajl::Parser.parse(body)
21
+ end
22
+ when 'ActiveSupport::JSON'
23
+ define_parser do |body|
24
+ unless body.nil? or body.empty?
25
+ result = ActiveSupport::JSON.decode(body)
26
+ raise ActiveSupport::JSON.backend::ParseError if String === result
27
+ result
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,64 @@
1
+ module FaradayStack
2
+ # A base class for middleware that parses responses
3
+ class ResponseMiddleware < Faraday::Response::Middleware
4
+ CONTENT_TYPE = 'Content-Type'.freeze
5
+
6
+ class << self
7
+ attr_accessor :parser
8
+ end
9
+
10
+ # Stores a block that receives the body and should return a parsed result
11
+ def self.define_parser(&block)
12
+ @parser = block
13
+ end
14
+
15
+ def self.inherited(subclass)
16
+ super
17
+ subclass.load_error = self.load_error
18
+ subclass.parser = self.parser
19
+ end
20
+
21
+ def initialize(app = nil, options = {})
22
+ super(app)
23
+ @options = options
24
+ @content_types = Array(options[:content_type])
25
+ end
26
+
27
+ # Override this to modify the environment after the response has finished.
28
+ def on_complete(env)
29
+ if process_response_type?(response_type(env)) and parse_response?(env)
30
+ env[:body] = parse(env[:body])
31
+ end
32
+ end
33
+
34
+ # Parses the response body and returns the result.
35
+ # Instead of overriding this method, consider using `define_parser`
36
+ def parse(body)
37
+ if self.class.parser
38
+ begin
39
+ self.class.parser.call(body)
40
+ rescue
41
+ raise Faraday::Error::ParsingError, $!
42
+ end
43
+ else
44
+ body
45
+ end
46
+ end
47
+
48
+ def response_type(env)
49
+ type = env[:response_headers][CONTENT_TYPE].to_s
50
+ type = type.split(';', 2).first if type.index(';')
51
+ type
52
+ end
53
+
54
+ def process_response_type?(type)
55
+ @content_types.empty? or @content_types.any? { |pattern|
56
+ Regexp === pattern ? type =~ pattern : type == pattern
57
+ }
58
+ end
59
+
60
+ def parse_response?(env)
61
+ env[:body].respond_to? :to_str
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,12 @@
1
+ module FaradayStack
2
+ class ResponseXML < ResponseMiddleware
3
+ dependency do
4
+ require 'nokogiri'
5
+ Nokogiri::XML
6
+ end
7
+
8
+ define_parser do |body|
9
+ Nokogiri::XML body
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,43 @@
1
+ # encoding: utf-8
2
+ require 'faraday'
3
+ require 'forwardable'
4
+ require 'faraday_stack/addressable_patch'
5
+
6
+ module FaradayStack
7
+ extend Faraday::AutoloadHelper
8
+
9
+ autoload_all 'faraday_stack',
10
+ :ResponseMiddleware => 'response_middleware',
11
+ :ResponseJSON => 'response_json',
12
+ :ResponseXML => 'response_xml',
13
+ :ResponseHTML => 'response_html',
14
+ :Instrumentation => 'instrumentation',
15
+ :Caching => 'caching',
16
+ :FollowRedirects => 'follow_redirects',
17
+ :RackCompatible => 'rack_compatible'
18
+
19
+ # THE ÜBER STACK
20
+ def self.default_connection
21
+ @default_connection ||= self.build
22
+ end
23
+
24
+ class << self
25
+ extend Forwardable
26
+ attr_writer :default_connection
27
+ def_delegators :default_connection, :get, :post, :put, :head, :delete
28
+ end
29
+
30
+ def self.build(url = nil, options = {})
31
+ Faraday::Connection.new(url, options) do |builder|
32
+ builder.request :url_encoded
33
+ builder.request :json
34
+ yield builder if block_given?
35
+ builder.use ResponseXML, :content_type => /[+\/]xml$/
36
+ builder.use ResponseHTML, :content_type => 'text/html'
37
+ builder.use ResponseJSON, :content_type => 'application/json'
38
+ builder.use FollowRedirects
39
+ builder.response :raise_error
40
+ builder.adapter Faraday.default_adapter
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,120 @@
1
+ require 'test_helper'
2
+ require 'forwardable'
3
+ require 'fileutils'
4
+ require 'rack/cache'
5
+
6
+ class CachingTest < Test::Unit::TestCase
7
+ class TestCache < Hash
8
+ def read(key)
9
+ if cached = self[key]
10
+ Marshal.load(cached)
11
+ end
12
+ end
13
+
14
+ def write(key, data)
15
+ self[key] = Marshal.dump(data)
16
+ end
17
+
18
+ def fetch(key)
19
+ read(key) || yield.tap { |data| write(key, data) }
20
+ end
21
+ end
22
+
23
+ def setup
24
+ @cache = TestCache.new
25
+
26
+ request_count = 0
27
+ response = lambda { |env|
28
+ [200, {'Content-Type' => 'text/plain'}, "request:#{request_count+=1}"]
29
+ }
30
+
31
+ @conn = Faraday.new do |b|
32
+ b.use FaradayStack::Caching, @cache
33
+ b.adapter :test do |stub|
34
+ stub.get('/', &response)
35
+ stub.get('/?foo=bar', &response)
36
+ stub.post('/', &response)
37
+ stub.get('/other', &response)
38
+ end
39
+ end
40
+ end
41
+
42
+ extend Forwardable
43
+ def_delegators :@conn, :get, :post
44
+
45
+ def test_cache_get
46
+ assert_equal 'request:1', get('/').body
47
+ assert_equal 'request:1', get('/').body
48
+ assert_equal 'request:2', get('/other').body
49
+ assert_equal 'request:2', get('/other').body
50
+ end
51
+
52
+ def test_response_has_request_params
53
+ get('/') # make cache
54
+ response = get('/')
55
+ assert_equal :get, response.env[:method]
56
+ assert_equal '/', response.env[:url].to_s
57
+ end
58
+
59
+ def test_cache_query_params
60
+ assert_equal 'request:1', get('/').body
61
+ assert_equal 'request:2', get('/?foo=bar').body
62
+ assert_equal 'request:2', get('/?foo=bar').body
63
+ assert_equal 'request:1', get('/').body
64
+ end
65
+
66
+ def test_doesnt_cache_post
67
+ assert_equal 'request:1', post('/').body
68
+ assert_equal 'request:2', post('/').body
69
+ assert_equal 'request:3', post('/').body
70
+ end
71
+ end
72
+
73
+ # RackCompatible + Rack::Cache
74
+ class HttpCachingTest < Test::Unit::TestCase
75
+ include FileUtils
76
+
77
+ CACHE_DIR = File.expand_path('../cache', __FILE__)
78
+
79
+ def setup
80
+ rm_r CACHE_DIR if File.exists? CACHE_DIR
81
+
82
+ request_count = 0
83
+ response = lambda { |env|
84
+ [200, { 'Content-Type' => 'text/plain',
85
+ 'Cache-Control' => 'public, max-age=900',
86
+ },
87
+ "request:#{request_count+=1}"]
88
+ }
89
+
90
+ @conn = Faraday.new do |b|
91
+ b.use FaradayStack::RackCompatible, Rack::Cache::Context,
92
+ :metastore => "file:#{CACHE_DIR}/rack/meta",
93
+ :entitystore => "file:#{CACHE_DIR}/rack/body",
94
+ :verbose => true
95
+
96
+ b.adapter :test do |stub|
97
+ stub.get('/', &response)
98
+ stub.post('/', &response)
99
+ end
100
+ end
101
+ end
102
+
103
+ extend Forwardable
104
+ def_delegators :@conn, :get, :post
105
+
106
+ def test_cache_get
107
+ assert_equal 'request:1', get('/').body
108
+ response = get('/')
109
+ assert_equal 'request:1', response.body
110
+ assert_equal 'text/plain', response['content-type']
111
+
112
+ assert_equal 'request:2', post('/').body
113
+ end
114
+
115
+ def test_doesnt_cache_post
116
+ assert_equal 'request:1', get('/').body
117
+ assert_equal 'request:2', post('/').body
118
+ assert_equal 'request:3', post('/').body
119
+ end
120
+ end
@@ -0,0 +1,33 @@
1
+ require 'test_helper'
2
+ require 'forwardable'
3
+
4
+ class FollowRedirectsTest < Test::Unit::TestCase
5
+ def setup
6
+ @conn = Faraday.new do |b|
7
+ b.use FaradayStack::FollowRedirects
8
+ b.adapter :test do |stub|
9
+ stub.get('/') { [301, {'Location' => '/found'}, ''] }
10
+ stub.post('/create') { [302, {'Location' => '/'}, ''] }
11
+ stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] }
12
+ stub.get('/loop') { [302, {'Location' => '/loop'}, ''] }
13
+ end
14
+ end
15
+ end
16
+
17
+ extend Forwardable
18
+ def_delegators :@conn, :get, :post
19
+
20
+ def test_redirected
21
+ assert_equal 'fin', get('/').body
22
+ end
23
+
24
+ def test_redirected_twice
25
+ assert_equal 'fin', post('/create').body
26
+ end
27
+
28
+ def test_redirect_limit
29
+ assert_raises FaradayStack::RedirectLimitReached do
30
+ get('/loop')
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,72 @@
1
+ require 'test_helper'
2
+
3
+ class ResponseMiddlewareTest < Test::Unit::TestCase
4
+ def setup
5
+ @json_handler = FaradayStack::ResponseJSON
6
+ @conn = Faraday.new do |b|
7
+ b.use @json_handler
8
+ b.adapter :test do |stub|
9
+ stub.get('json') { [200, {'Content-Type' => 'application/json; charset=utf-8'}, "[1,2,3]"] }
10
+ stub.get('blank') { [200, {'Content-Type' => 'application/json'}, ''] }
11
+ stub.get('nil') { [200, {'Content-Type' => 'application/json'}, nil] }
12
+ stub.get('bad_json') { [200, {'Content-Type' => 'application/json'}, '<body></body>']}
13
+ stub.get('non_json') { [200, {'Content-Type' => 'text/html'}, '<body></body>']}
14
+ end
15
+ end
16
+ end
17
+
18
+ def process_only(*types)
19
+ @conn.builder.swap @json_handler, @json_handler, :content_type => types
20
+ end
21
+
22
+ def test_uses_json_to_parse_json_content
23
+ response = @conn.get('json')
24
+ assert response.success?
25
+ assert_equal [1,2,3], response.body
26
+ end
27
+
28
+ def test_uses_json_to_parse_json_content_conditional
29
+ process_only('application/json')
30
+ response = @conn.get('json')
31
+ assert response.success?
32
+ assert_equal [1,2,3], response.body
33
+ end
34
+
35
+ def test_uses_json_to_parse_json_content_conditional_with_regexp
36
+ process_only(%r{/(x-)?json$})
37
+ response = @conn.get('json')
38
+ assert response.success?
39
+ assert_equal [1,2,3], response.body
40
+ end
41
+
42
+ def test_uses_json_to_skip_blank_content
43
+ response = @conn.get('blank')
44
+ assert response.success?
45
+ assert_nil response.body
46
+ end
47
+
48
+ def test_uses_json_to_skip_nil_content
49
+ response = @conn.get('nil')
50
+ assert response.success?
51
+ assert_nil response.body
52
+ end
53
+
54
+ def test_uses_json_to_raise_Faraday_Error_Parsing_with_no_json_content
55
+ assert_raises Faraday::Error::ParsingError do
56
+ @conn.get('bad_json')
57
+ end
58
+ end
59
+
60
+ def test_non_json_response
61
+ assert_raises Faraday::Error::ParsingError do
62
+ @conn.get('non_json')
63
+ end
64
+ end
65
+
66
+ def test_non_json_response_conditional
67
+ process_only('application/json')
68
+ response = @conn.get('non_json')
69
+ assert_equal 'text/html', response.headers['Content-Type']
70
+ assert_equal '<body></body>', response.body
71
+ end
72
+ end
@@ -0,0 +1,2 @@
1
+ require 'test/unit'
2
+ require 'faraday_stack'
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: faraday-stack
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Mislav Marohnić
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-04-01 00:00:00.000000000 +02:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: faraday
17
+ requirement: &2164745380 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ~>
21
+ - !ruby/object:Gem::Version
22
+ version: '0.6'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *2164745380
26
+ description:
27
+ email: mislav.marohnic@gmail.com
28
+ executables: []
29
+ extensions: []
30
+ extra_rdoc_files: []
31
+ files:
32
+ - lib/faraday-stack.rb
33
+ - lib/faraday_stack/addressable_patch.rb
34
+ - lib/faraday_stack/caching.rb
35
+ - lib/faraday_stack/follow_redirects.rb
36
+ - lib/faraday_stack/instrumentation.rb
37
+ - lib/faraday_stack/rack_compatible.rb
38
+ - lib/faraday_stack/response_html.rb
39
+ - lib/faraday_stack/response_json.rb
40
+ - lib/faraday_stack/response_middleware.rb
41
+ - lib/faraday_stack/response_xml.rb
42
+ - lib/faraday_stack.rb
43
+ - test/caching_test.rb
44
+ - test/follow_redirects_test.rb
45
+ - test/response_middleware_test.rb
46
+ - test/test_helper.rb
47
+ - README.md
48
+ has_rdoc: false
49
+ homepage: https://github.com/mislav/faraday-stack
50
+ licenses: []
51
+ post_install_message:
52
+ rdoc_options: []
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ! '>='
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ requirements: []
68
+ rubyforge_project:
69
+ rubygems_version: 1.5.3
70
+ signing_key:
71
+ specification_version: 3
72
+ summary: Great Faraday stack for consuming all kinds of APIs
73
+ test_files: []