pax_global_header00006660000000000000000000000064121507131170014510gustar00rootroot0000000000000052 comment=651c75f55b0c2709986e8511f0c15df03493b545 ruby-faraday-middleware-0.9.0/000077500000000000000000000000001215071311700162175ustar00rootroot00000000000000ruby-faraday-middleware-0.9.0/.gemtest000066400000000000000000000000001215071311700176560ustar00rootroot00000000000000ruby-faraday-middleware-0.9.0/.gitignore000066400000000000000000000002551215071311700202110ustar00rootroot00000000000000# TextMate *.tmproj tmtags # emails *~ \#* .\#* # vim *.swp # General coverage rdoc doc log .yardoc tmp # Bundler *.gem .bundle Gemfile.lock pkg *.gem # Rubinius *.rbc ruby-faraday-middleware-0.9.0/.rspec000066400000000000000000000000241215071311700173300ustar00rootroot00000000000000--color --backtrace ruby-faraday-middleware-0.9.0/.travis.yml000066400000000000000000000002001215071311700203200ustar00rootroot00000000000000language: ruby rvm: - rbx-18mode - rbx-19mode - jruby-18mode - jruby-19mode - 1.8.7 - 1.9.2 - 1.9.3 - ruby-head ruby-faraday-middleware-0.9.0/CHANGELOG.md000066400000000000000000000002051215071311700200250ustar00rootroot00000000000000# Changelog ### 0.0.2 September 25, 2010 * Mashify now handles arrays of non-hashes ### 0.0.1 June 27, 2010 * MultiJSON * Mashifyruby-faraday-middleware-0.9.0/Gemfile000066400000000000000000000003151215071311700175110ustar00rootroot00000000000000source 'http://rubygems.org' platforms :mri_19 do gem 'simplecov' gem 'cane', '~> 2.2.2' end gem 'json', :platforms => [:ruby_18, :jruby] gem 'jruby-openssl', '~> 0.7', :platforms => :jruby gemspec ruby-faraday-middleware-0.9.0/LICENSE.md000066400000000000000000000020771215071311700176310ustar00rootroot00000000000000Copyright (c) 2011 Erik Michaels-Ober, Wynn Netherland, et al. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ruby-faraday-middleware-0.9.0/README.md000066400000000000000000000027121215071311700175000ustar00rootroot00000000000000Faraday Middleware ================== A collection of useful [Faraday][] middleware. [See the documentation][docs]. gem install faraday_middleware Dependencies ------------ Some dependent libraries are needed only when using specific middleware: * FaradayMiddleware::EncodeJson & FaradayMiddleware::ParseJson: "json" for ruby older than 1.9 * FaradayMiddleware::ParseXml: "multi_xml" * FaradayMiddleware::OAuth: "simple_oauth" * FaradayMiddleware::Mashify: "hashie" * FaradayMiddleware::Rashify: "rash" * FaradayMiddleware::Instrumentation: "activesupport" Examples -------- ``` rb require 'faraday_middleware' ## in Faraday 0.8 or above: connection = Faraday.new 'http://example.com/api' do |conn| conn.request :oauth2, 'TOKEN' conn.request :json conn.response :xml, :content_type => /\bxml$/ conn.response :json, :content_type => /\bjson$/ conn.use :instrumentation conn.adapter Faraday.default_adapter end ## with Faraday 0.7: connection = Faraday.new 'http://example.com/api' do |builder| builder.use FaradayMiddleware::OAuth2, 'TOKEN' builder.use FaradayMiddleware::EncodeJson builder.use FaradayMiddleware::ParseXml, :content_type => /\bxml$/ builder.use FaradayMiddleware::ParseJson, :content_type => /\bjson$/ builder.use FaradayMiddleware::Instrumentation builder.adapter Faraday.default_adapter end ``` [faraday]: https://github.com/technoweenie/faraday#readme [docs]: https://github.com/pengwynn/faraday_middleware/wiki ruby-faraday-middleware-0.9.0/Rakefile000066400000000000000000000012111215071311700176570ustar00rootroot00000000000000if defined? RUBY_ENGINE and 'ruby' == RUBY_ENGINE and RUBY_VERSION.index('1.9') == 0 task :default => [:enable_coverage, :spec, :test, :quality] else task :default => [:spec, :test] end require 'bundler' Bundler::GemHelper.install_tasks require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) task :enable_coverage do ENV['COVERAGE'] = 'yes' end desc %(Run Test::Unit tests) task :test do sh 'ruby', '-Ilib', 'spec/caching_test.rb' end desc %(Check code quality metrics with Cane) task :quality do sh 'cane', '--abc-max=15', '--style-measure=110', '--gte=coverage/covered_percent,99', '--max-violations=0' end ruby-faraday-middleware-0.9.0/faraday_middleware.gemspec000066400000000000000000000021131215071311700233650ustar00rootroot00000000000000require File.expand_path('../lib/faraday_middleware/version', __FILE__) Gem::Specification.new do |gem| gem.add_dependency 'faraday', ['>= 0.7.4', '< 0.9'] gem.add_development_dependency 'multi_xml', '~> 0.2' gem.add_development_dependency 'rake', '~> 0.9' gem.add_development_dependency 'hashie', '~> 1.2' gem.add_development_dependency 'rash', '~> 0.3' gem.add_development_dependency 'rspec', '~> 2.6' gem.add_development_dependency 'simple_oauth', '~> 0.1' gem.add_development_dependency 'rack-cache', '~> 1.1' gem.authors = ["Erik Michaels-Ober", "Wynn Netherland"] gem.description = %q{Various middleware for Faraday} gem.email = ['sferik@gmail.com', 'wynn.netherland@gmail.com'] gem.files = `git ls-files`.split("\n") gem.homepage = 'https://github.com/pengwynn/faraday_middleware' gem.name = 'faraday_middleware' gem.require_paths = ['lib'] gem.required_rubygems_version = Gem::Requirement.new('>= 1.3.6') gem.summary = gem.description gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") gem.version = FaradayMiddleware::VERSION end ruby-faraday-middleware-0.9.0/lib/000077500000000000000000000000001215071311700167655ustar00rootroot00000000000000ruby-faraday-middleware-0.9.0/lib/faraday_middleware.rb000066400000000000000000000040301215071311700231130ustar00rootroot00000000000000require 'faraday' module FaradayMiddleware autoload :OAuth, 'faraday_middleware/request/oauth' autoload :OAuth2, 'faraday_middleware/request/oauth2' autoload :EncodeJson, 'faraday_middleware/request/encode_json' autoload :MethodOverride, 'faraday_middleware/request/method_override' autoload :Mashify, 'faraday_middleware/response/mashify' autoload :Rashify, 'faraday_middleware/response/rashify' autoload :ParseJson, 'faraday_middleware/response/parse_json' autoload :ParseXml, 'faraday_middleware/response/parse_xml' autoload :ParseMarshal, 'faraday_middleware/response/parse_marshal' autoload :ParseYaml, 'faraday_middleware/response/parse_yaml' autoload :ParseDates, 'faraday_middleware/response/parse_dates' autoload :Caching, 'faraday_middleware/response/caching' autoload :Chunked, 'faraday_middleware/response/chunked' autoload :RackCompatible, 'faraday_middleware/rack_compatible' autoload :FollowRedirects, 'faraday_middleware/response/follow_redirects' autoload :Instrumentation, 'faraday_middleware/instrumentation' if Faraday.respond_to? :register_middleware Faraday.register_middleware :request, :oauth => lambda { OAuth }, :oauth2 => lambda { OAuth2 }, :json => lambda { EncodeJson }, :method_override => lambda { MethodOverride } Faraday.register_middleware :response, :mashify => lambda { Mashify }, :rashify => lambda { Rashify }, :json => lambda { ParseJson }, :json_fix => lambda { ParseJson::MimeTypeFix }, :xml => lambda { ParseXml }, :marshal => lambda { ParseMarshal }, :yaml => lambda { ParseYaml }, :dates => lambda { ParseDates }, :caching => lambda { Caching }, :follow_redirects => lambda { FollowRedirects }, :chunked => lambda { Chunked } Faraday.register_middleware \ :instrumentation => lambda { Instrumentation } end end require 'faraday_middleware/backwards_compatibility' ruby-faraday-middleware-0.9.0/lib/faraday_middleware/000077500000000000000000000000001215071311700225715ustar00rootroot00000000000000ruby-faraday-middleware-0.9.0/lib/faraday_middleware/addressable_patch.rb000066400000000000000000000011551215071311700265500ustar00rootroot00000000000000require 'addressable/uri' # feature-detect the bug unless Addressable::URI.parse('/?a=1&b=2') === '/?b=2&a=1' # fix `normalized_query` by sorting query key-value pairs # (rejected: https://github.com/sporkmonger/addressable/issues/28) class Addressable::URI alias normalized_query_without_ordering_fix normalized_query def normalized_query fresh = @normalized_query.nil? query = normalized_query_without_ordering_fix if query && fresh @normalized_query = query.split('&', -1).sort_by {|q| q[0..(q.index('=')||-1)] }.join('&') else query end end end end ruby-faraday-middleware-0.9.0/lib/faraday_middleware/backwards_compatibility.rb000066400000000000000000000011511215071311700300060ustar00rootroot00000000000000# deprecated constants Faraday::Request.class_eval do autoload :OAuth, 'faraday_middleware/request/oauth' autoload :OAuth2, 'faraday_middleware/request/oauth2' end Faraday::Response.class_eval do autoload :Mashify, 'faraday_middleware/response/mashify' autoload :Rashify, 'faraday_middleware/response/rashify' autoload :ParseJson, 'faraday_middleware/response/parse_json' autoload :ParseXml, 'faraday_middleware/response/parse_xml' autoload :ParseMarshal, 'faraday_middleware/response/parse_marshal' autoload :ParseYaml, 'faraday_middleware/response/parse_yaml' end ruby-faraday-middleware-0.9.0/lib/faraday_middleware/instrumentation.rb000066400000000000000000000014721215071311700263650ustar00rootroot00000000000000require 'faraday' module FaradayMiddleware # Public: Instruments requests using Active Support. # # Measures time spent only for synchronous requests. # # Examples # # ActiveSupport::Notifications.subscribe('request.faraday') do |name, starts, ends, _, env| # url = env[:url] # http_method = env[:method].to_s.upcase # duration = ends - starts # $stderr.puts '[%s] %s %s (%.3f s)' % [url.host, http_method, url.request_uri, duration] # end class Instrumentation < Faraday::Middleware dependency 'active_support/notifications' def initialize(app, options = {}) super(app) @name = options.fetch(:name, 'request.faraday') end def call(env) ActiveSupport::Notifications.instrument(@name, env) do @app.call(env) end end end end ruby-faraday-middleware-0.9.0/lib/faraday_middleware/rack_compatible.rb000066400000000000000000000045031215071311700262370ustar00rootroot00000000000000require 'stringio' module FaradayMiddleware # Wraps a handler originally written for Rack to make it compatible with Faraday. # # Experimental. Only handles changes in request headers. class RackCompatible def initialize(app, rack_handler, *args) # tiny middleware that decomposes a Faraday::Response to standard Rack # array: [status, headers, body] compatible_app = lambda do |env| restore_env(env) response = app.call(env) [response.status, response.headers, Array(response.body)] end @rack = rack_handler.new(compatible_app, *args) end def call(env) prepare_env(env) rack_response = @rack.call(env) finalize_response(env, rack_response) end NonPrefixedHeaders = %w[CONTENT_LENGTH CONTENT_TYPE] # faraday to rack-compatible def prepare_env(env) headers_to_rack(env) url = env[:url] env['rack.url_scheme'] = url.scheme env['PATH_INFO'] = url.path env['SERVER_PORT'] = url.respond_to?(:inferred_port) ? url.inferred_port : url.port env['QUERY_STRING'] = url.query env['REQUEST_METHOD'] = env[:method].to_s.upcase env['rack.errors'] ||= StringIO.new env end def headers_to_rack(env) env[:request_headers].each do |name, value| name = name.upcase.tr('-', '_') name = "HTTP_#{name}" unless NonPrefixedHeaders.include? name env[name] = value end end # rack to faraday-compatible def restore_env(env) headers = env[:request_headers] headers.clear env.each do |name, value| next unless String === name if NonPrefixedHeaders.include? name or name.index('HTTP_') == 0 name = name.sub(/^HTTP_/, '').downcase.tr('_', '-') headers[name] = value end end env[:method] = env['REQUEST_METHOD'].downcase.to_sym env end def finalize_response(env, rack_response) status, headers, body = rack_response body = body.inject() { |str, part| str << part } headers = Faraday::Utils::Headers.new(headers) unless Faraday::Utils::Headers === headers env.update :status => status.to_i, :body => body, :response_headers => headers env[:response] ||= Faraday::Response.new(env) env[:response] end end end ruby-faraday-middleware-0.9.0/lib/faraday_middleware/request/000077500000000000000000000000001215071311700242615ustar00rootroot00000000000000ruby-faraday-middleware-0.9.0/lib/faraday_middleware/request/encode_json.rb000066400000000000000000000024751215071311700271040ustar00rootroot00000000000000require 'faraday' module FaradayMiddleware # Request middleware that encodes the body as JSON. # # Processes only requests with matching Content-type or those without a type. # If a request doesn't have a type but has a body, it sets the Content-type # to JSON MIME-type. # # Doesn't try to encode bodies that already are in string form. class EncodeJson < Faraday::Middleware CONTENT_TYPE = 'Content-Type'.freeze MIME_TYPE = 'application/json'.freeze dependency do require 'json' unless defined?(::JSON) end def call(env) match_content_type(env) do |data| env[:body] = encode data end @app.call env end def encode(data) ::JSON.dump data end def match_content_type(env) if process_request?(env) env[:request_headers][CONTENT_TYPE] ||= MIME_TYPE yield env[:body] unless env[:body].respond_to?(:to_str) end end def process_request?(env) type = request_type(env) has_body?(env) and (type.empty? or type == MIME_TYPE) end def has_body?(env) body = env[:body] and !(body.respond_to?(:to_str) and body.empty?) end def request_type(env) type = env[:request_headers][CONTENT_TYPE].to_s type = type.split(';', 2).first if type.index(';') type end end end ruby-faraday-middleware-0.9.0/lib/faraday_middleware/request/method_override.rb000066400000000000000000000030551215071311700277700ustar00rootroot00000000000000require 'faraday' module FaradayMiddleware # Public: Writes the original HTTP method to "X-Http-Method-Override" header # and sends the request as POST. # # This can be used to work around technical issues with making non-POST # requests, e.g. faulty HTTP client or server router. # # This header is recognized in Rack apps by default, courtesy of the # Rack::MethodOverride module. See # http://rack.rubyforge.org/doc/classes/Rack/MethodOverride.html class MethodOverride < Faraday::Middleware HEADER = "X-Http-Method-Override".freeze # Public: Initialize the middleware. # # app - the Faraday app to wrap # options - (optional) # :rewrite - Array of HTTP methods to rewrite # (default: all but GET and POST) def initialize(app, options = nil) super(app) @methods = options && options.fetch(:rewrite).map { |method| method = method.downcase if method.respond_to? :downcase method.to_sym } end def call(env) method = env[:method] rewrite_request(env, method) if rewrite_request?(method) @app.call(env) end def rewrite_request?(method) if @methods.nil? or @methods.empty? method != :get and method != :post else @methods.include? method end end # Internal: Write the original HTTP method to header, change method to POST. def rewrite_request(env, original_method) env[:request_headers][HEADER] = original_method.to_s.upcase env[:method] = :post end end end ruby-faraday-middleware-0.9.0/lib/faraday_middleware/request/oauth.rb000066400000000000000000000052241215071311700257310ustar00rootroot00000000000000require 'faraday' require 'forwardable' module FaradayMiddleware # Public: Uses the simple_oauth library to sign requests according the # OAuth protocol. # # The options for this middleware are forwarded to SimpleOAuth::Header: # :consumer_key, :consumer_secret, :token, :token_secret. All these # parameters are optional. # # The signature is added to the "Authorization" HTTP request header. If the # value for this header already exists, it is not overriden. # # If no Content-Type header is specified, this middleware assumes that # request body parameters should be included while signing the request. # Otherwise, it only includes them if the Content-Type is # "application/x-www-form-urlencoded", as per OAuth 1.0. # # For better performance while signing requests, this middleware should be # positioned before UrlEncoded middleware on the stack, but after any other # body-encoding middleware (such as EncodeJson). class OAuth < Faraday::Middleware dependency 'simple_oauth' AUTH_HEADER = 'Authorization'.freeze CONTENT_TYPE = 'Content-Type'.freeze TYPE_URLENCODED = 'application/x-www-form-urlencoded'.freeze extend Forwardable parser_method = :parse_nested_query parser_module = ::Faraday::Utils.respond_to?(parser_method) ? 'Faraday::Utils' : 'Rack::Utils' def_delegator parser_module, parser_method def initialize(app, options) super(app) @options = options end def call(env) env[:request_headers][AUTH_HEADER] ||= oauth_header(env).to_s if sign_request?(env) @app.call(env) end def oauth_header(env) SimpleOAuth::Header.new env[:method], env[:url].to_s, signature_params(body_params(env)), oauth_options(env) end def sign_request?(env) !!env[:request].fetch(:oauth, true) end def oauth_options(env) if extra = env[:request][:oauth] and extra.is_a? Hash and !extra.empty? @options.merge extra else @options end end def body_params(env) if include_body_params?(env) if env[:body].respond_to?(:to_str) parse_nested_query env[:body] else env[:body] end end || {} end def include_body_params?(env) # see RFC 5489, section 3.4.1.3.1 for details !(type = env[:request_headers][CONTENT_TYPE]) or type == TYPE_URLENCODED end def signature_params(params) params.empty? ? params : params.reject {|k,v| v.respond_to?(:content_type) } end end end # deprecated alias Faraday::Request::OAuth = FaradayMiddleware::OAuth ruby-faraday-middleware-0.9.0/lib/faraday_middleware/request/oauth2.rb000066400000000000000000000033211215071311700260070ustar00rootroot00000000000000require 'faraday' require 'forwardable' module FaradayMiddleware # Public: A simple middleware that adds an access token to each request. # # The token is added as both "access_token" query parameter and the # "Authorization" HTTP request header. However, an explicit "access_token" # parameter or "Authorization" header for the current request are not # overriden. # # Examples # # # configure default token: # OAuth2.new(app, 'abc123') # # # configure query parameter name: # OAuth2.new(app, 'abc123', :param_name => 'my_oauth_token') # # # default token value is optional: # OAuth2.new(app, :param_name => 'my_oauth_token') class OAuth2 < Faraday::Middleware PARAM_NAME = 'access_token'.freeze AUTH_HEADER = 'Authorization'.freeze attr_reader :param_name extend Forwardable def_delegators :'Faraday::Utils', :parse_query, :build_query def call(env) params = { param_name => @token }.update query_params(env[:url]) if token = params[param_name] and !token.empty? env[:url].query = build_query params env[:request_headers][AUTH_HEADER] ||= %(Token token="#{token}") end @app.call env end def initialize(app, token = nil, options = {}) super(app) options, token = token, nil if token.is_a? Hash @token = token && token.to_s @param_name = options.fetch(:param_name, PARAM_NAME).to_s raise ArgumentError, ":param_name can't be blank" if @param_name.empty? end def query_params(url) if url.query.nil? or url.query.empty? {} else parse_query url.query end end end end # deprecated alias Faraday::Request::OAuth2 = FaradayMiddleware::OAuth2 ruby-faraday-middleware-0.9.0/lib/faraday_middleware/response/000077500000000000000000000000001215071311700244275ustar00rootroot00000000000000ruby-faraday-middleware-0.9.0/lib/faraday_middleware/response/caching.rb000066400000000000000000000045631215071311700263600ustar00rootroot00000000000000require 'faraday' require 'forwardable' # fixes normalizing query strings: require 'faraday_middleware/addressable_patch' if defined? ::Addressable::URI module FaradayMiddleware # Public: Caches GET responses and pulls subsequent ones from the cache. class Caching < Faraday::Middleware attr_reader :cache extend Forwardable def_delegators :'Faraday::Utils', :parse_query, :build_query # Public: initialize the middleware. # # cache - An object that responds to read, write and fetch (default: nil). # options - An options Hash (default: {}): # :ignore_params - String name or Array names of query params # that should be ignored when forming the cache # key (default: []). # # Yields if no cache is given. The block should return a cache object. def initialize(app, cache = nil, options = {}) super(app) options, cache = cache, nil if cache.is_a? Hash and block_given? @cache = cache || yield @options = options end def call(env) if :get == env[:method] if env[:parallel_manager] # callback mode cache_on_complete(env) else # synchronous mode response = cache.fetch(cache_key(env)) { @app.call(env) } finalize_response(response, env) end else @app.call(env) end end def cache_key(env) url = env[:url].dup if url.query && params_to_ignore.any? params = parse_query url.query params.reject! {|k,| params_to_ignore.include? k } url.query = build_query params end url.normalize! url.request_uri end def params_to_ignore @params_to_ignore ||= Array(@options[:ignore_params]).map { |p| p.to_s } end def cache_on_complete(env) key = cache_key(env) if cached_response = cache.read(key) finalize_response(cached_response, env) else response = @app.call(env) response.on_complete { cache.write(key, response) } end end def finalize_response(response, env) response = response.dup if response.frozen? env[:response] = response unless env[:response_headers] env.update response.env # FIXME: omg hax response.instance_variable_set('@env', env) end response end end end ruby-faraday-middleware-0.9.0/lib/faraday_middleware/response/chunked.rb000066400000000000000000000017041215071311700263770ustar00rootroot00000000000000require 'faraday_middleware/response_middleware' module FaradayMiddleware # Public: Parse a Transfer-Encoding: Chunked response to just the original data class Chunked < FaradayMiddleware::ResponseMiddleware TRANSFER_ENCODING = 'transfer-encoding'.freeze define_parser do |raw_body| decoded_body = [] until raw_body.empty? chunk_len, raw_body = raw_body.split("\r\n", 2) chunk_len = chunk_len.split(';',2).first.hex break if chunk_len == 0 decoded_body << raw_body[0, chunk_len] # The 2 is to strip the extra CRLF at the end of the chunk raw_body = raw_body[chunk_len + 2, raw_body.length - chunk_len - 2] end decoded_body.join('') end def parse_response?(env) super and chunked_encoding?(env[:response_headers]) end def chunked_encoding?(headers) encoding = headers[TRANSFER_ENCODING] and encoding.split(',').include?('chunked') end end end ruby-faraday-middleware-0.9.0/lib/faraday_middleware/response/follow_redirects.rb000066400000000000000000000112001215071311700303140ustar00rootroot00000000000000require 'faraday' require 'set' module FaradayMiddleware # Public: Exception thrown when the maximum amount of requests is exceeded. class RedirectLimitReached < Faraday::Error::ClientError attr_reader :response def initialize(response) super "too many redirects; last one to: #{response['location']}" @response = response end end # Public: Follow HTTP 301, 302, 303, and 307 redirects for GET, PATCH, POST, # PUT, and DELETE requests. # # This middleware does not follow the HTTP specification for HTTP 302, by # default, in that it follows the improper implementation used by most major # web browsers which forces the redirected request to become a GET request # regardless of the original request method. # # For HTTP 301, 302, and 303, the original request is transformed into a # GET request to the response Location, by default. However, with standards # compliance enabled, a 302 will instead act in accordance with the HTTP # specification, which will replay the original request to the received # Location, just as with a 307. # # For HTTP 307, the original request is replayed to the response Location, # including original HTTP request method (GET, POST, PUT, DELETE, PATCH), # original headers, and original body. # # This middleware currently only works with synchronous requests; in other # words, it doesn't support parallelism. class FollowRedirects < Faraday::Middleware # HTTP methods for which 30x redirects can be followed ALLOWED_METHODS = Set.new [:head, :options, :get, :post, :put, :patch, :delete] # HTTP redirect status codes that this middleware implements REDIRECT_CODES = Set.new [301, 302, 303, 307] # Keys in env hash which will get cleared between requests ENV_TO_CLEAR = Set.new [:status, :response, :response_headers] # Default value for max redirects followed FOLLOW_LIMIT = 3 # Public: Initialize the middleware. # # options - An options Hash (default: {}): # limit - A Numeric redirect limit (default: 3) # standards_compliant - A Boolean indicating whether to respect # the HTTP spec when following 302 # (default: false) # cookie - Use either an array of strings # (e.g. ['cookie1', 'cookie2']) to choose kept cookies # or :all to keep all cookies. def initialize(app, options = {}) super(app) @options = options @replay_request_codes = Set.new [307] @replay_request_codes << 302 if standards_compliant? end def call(env) perform_with_redirection(env, follow_limit) end private def transform_into_get?(response) return false if [:head, :options].include? response.env[:method] # Never convert head or options to a get. That would just be silly. !@replay_request_codes.include? response.status end def perform_with_redirection(env, follows) request_body = env[:body] response = @app.call(env) response.on_complete do |env| if follow_redirect?(env, response) raise RedirectLimitReached, response if follows.zero? env = update_env(env, request_body, response) response = perform_with_redirection(env, follows - 1) end end response end def update_env(env, request_body, response) env[:url] += response['location'] if @options[:cookies] cookies = keep_cookies(env) env[:request_headers][:cookies] = cookies unless cookies.nil? end if transform_into_get?(response) env[:method] = :get env[:body] = nil else env[:body] = request_body end ENV_TO_CLEAR.each {|key| env.delete key } env end def follow_redirect?(env, response) ALLOWED_METHODS.include? env[:method] and REDIRECT_CODES.include? response.status end def follow_limit @options.fetch(:limit, FOLLOW_LIMIT) end def keep_cookies(env) cookies = @options.fetch(:cookies, []) response_cookies = env[:response_headers][:cookies] cookies == :all ? response_cookies : selected_request_cookies(response_cookies) end def selected_request_cookies(cookies) selected_cookies(cookies)[0...-1] end def selected_cookies(cookies) "".tap do |cookie_string| @options[:cookies].each do |cookie| string = /#{cookie}=?[^;]*/.match(cookies)[0] + ';' cookie_string << string end end end def standards_compliant? @options.fetch(:standards_compliant, false) end end end ruby-faraday-middleware-0.9.0/lib/faraday_middleware/response/mashify.rb000066400000000000000000000014201215071311700264110ustar00rootroot00000000000000require 'faraday' module FaradayMiddleware # Public: Converts parsed response bodies to a Hashie::Mash if they were of # Hash or Array type. class Mashify < Faraday::Response::Middleware attr_accessor :mash_class class << self attr_accessor :mash_class end dependency do require 'hashie/mash' self.mash_class = ::Hashie::Mash end def initialize(app = nil, options = {}) super(app) self.mash_class = options[:mash_class] || self.class.mash_class end def parse(body) case body when Hash mash_class.new(body) when Array body.map { |item| parse(item) } else body end end end end # deprecated alias Faraday::Response::Mashify = FaradayMiddleware::Mashify ruby-faraday-middleware-0.9.0/lib/faraday_middleware/response/parse_dates.rb000066400000000000000000000015021215071311700272440ustar00rootroot00000000000000require "time" require "faraday" module FaradayMiddleware # Parse dates from response body class ParseDates < ::Faraday::Response::Middleware ISO_DATE_FORMAT = /\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z\Z/m def initialize(app, options = {}) @regexp = options[:match] || ISO_DATE_FORMAT super(app) end def call(env) response = @app.call(env) parse_dates! response.env[:body] response end private def parse_dates!(value) case value when Hash value.each do |key, element| value[key] = parse_dates!(element) end when Array value.each_with_index do |element, index| value[index] = parse_dates!(element) end when @regexp Time.parse(value) else value end end end end ruby-faraday-middleware-0.9.0/lib/faraday_middleware/response/parse_json.rb000066400000000000000000000026741215071311700271300ustar00rootroot00000000000000require 'faraday_middleware/response_middleware' module FaradayMiddleware # Public: Parse response bodies as JSON. class ParseJson < ResponseMiddleware dependency do require 'json' unless defined?(::JSON) end define_parser do |body| ::JSON.parse body unless body.strip.empty? end # Public: Override the content-type of the response with "application/json" # if the response body looks like it might be JSON, i.e. starts with an # open bracket. # # This is to fix responses from certain API providers that insist on serving # JSON with wrong MIME-types such as "text/javascript". class MimeTypeFix < ResponseMiddleware MIME_TYPE = 'application/json'.freeze def process_response(env) old_type = env[:response_headers][CONTENT_TYPE].to_s new_type = MIME_TYPE.dup new_type << ';' << old_type.split(';', 2).last if old_type.index(';') env[:response_headers][CONTENT_TYPE] = new_type end BRACKETS = %w- [ { - WHITESPACE = [ " ", "\n", "\r", "\t" ] def parse_response?(env) super and BRACKETS.include? first_char(env[:body]) end def first_char(body) idx = -1 begin char = body[idx += 1] char = char.chr if char end while char and WHITESPACE.include? char char end end end end # deprecated alias Faraday::Response::ParseJson = FaradayMiddleware::ParseJson ruby-faraday-middleware-0.9.0/lib/faraday_middleware/response/parse_marshal.rb000066400000000000000000000005461215071311700276020ustar00rootroot00000000000000require 'faraday_middleware/response_middleware' module FaradayMiddleware # Public: Restore marshalled Ruby objects in response bodies. class ParseMarshal < ResponseMiddleware define_parser do |body| ::Marshal.load body unless body.empty? end end end # deprecated alias Faraday::Response::ParseMarshal = FaradayMiddleware::ParseMarshal ruby-faraday-middleware-0.9.0/lib/faraday_middleware/response/parse_xml.rb000066400000000000000000000005301215071311700267440ustar00rootroot00000000000000require 'faraday_middleware/response_middleware' module FaradayMiddleware # Public: parses response bodies with MultiXml. class ParseXml < ResponseMiddleware dependency 'multi_xml' define_parser do |body| ::MultiXml.parse(body) end end end # deprecated alias Faraday::Response::ParseXml = FaradayMiddleware::ParseXml ruby-faraday-middleware-0.9.0/lib/faraday_middleware/response/parse_yaml.rb000066400000000000000000000005111215071311700271050ustar00rootroot00000000000000require 'faraday_middleware/response_middleware' module FaradayMiddleware # Public: Parse response bodies as YAML. class ParseYaml < ResponseMiddleware dependency 'yaml' define_parser do |body| ::YAML.load body end end end # deprecated alias Faraday::Response::ParseYaml = FaradayMiddleware::ParseYaml ruby-faraday-middleware-0.9.0/lib/faraday_middleware/response/rashify.rb000066400000000000000000000005641215071311700264260ustar00rootroot00000000000000require 'faraday_middleware/response/mashify' module FaradayMiddleware # Public: Converts parsed response bodies to a Hashie::Rash if they were of # Hash or Array type. class Rashify < Mashify dependency do require 'rash' self.mash_class = ::Hashie::Rash end end end # deprecated alias Faraday::Response::Rashify = FaradayMiddleware::Rashify ruby-faraday-middleware-0.9.0/lib/faraday_middleware/response_middleware.rb000066400000000000000000000040201215071311700271450ustar00rootroot00000000000000require 'faraday' module FaradayMiddleware # Internal: The base class for middleware that parses responses. class ResponseMiddleware < Faraday::Middleware CONTENT_TYPE = 'Content-Type'.freeze class << self attr_accessor :parser end # Store a Proc that receives the body and returns the parsed result. def self.define_parser(parser = nil) @parser = parser || Proc.new end def self.inherited(subclass) super subclass.load_error = self.load_error if subclass.respond_to? :load_error= subclass.parser = self.parser end def initialize(app = nil, options = {}) super(app) @options = options @content_types = Array(options[:content_type]) end def call(environment) @app.call(environment).on_complete do |env| if process_response_type?(response_type(env)) and parse_response?(env) process_response(env) end end end def process_response(env) env[:raw_body] = env[:body] if preserve_raw?(env) env[:body] = parse(env[:body]) end # Parse the response body. # # Instead of overriding this method, consider using `define_parser`. def parse(body) if self.class.parser begin self.class.parser.call(body) rescue StandardError, SyntaxError => err raise err if err.is_a? SyntaxError and err.class.name != 'Psych::SyntaxError' raise Faraday::Error::ParsingError, err end else body end end def response_type(env) type = env[:response_headers][CONTENT_TYPE].to_s type = type.split(';', 2).first if type.index(';') type end def process_response_type?(type) @content_types.empty? or @content_types.any? { |pattern| pattern.is_a?(Regexp) ? type =~ pattern : type == pattern } end def parse_response?(env) env[:body].respond_to? :to_str end def preserve_raw?(env) env[:request].fetch(:preserve_raw, @options[:preserve_raw]) end end end ruby-faraday-middleware-0.9.0/lib/faraday_middleware/version.rb000066400000000000000000000000611215071311700246000ustar00rootroot00000000000000module FaradayMiddleware VERSION = "0.9.0" end ruby-faraday-middleware-0.9.0/metadata.yml000066400000000000000000000140061215071311700205230ustar00rootroot00000000000000--- !ruby/object:Gem::Specification name: faraday_middleware version: !ruby/object:Gem::Version version: 0.9.0 prerelease: platform: ruby authors: - Erik Michaels-Ober - Wynn Netherland autorequire: bindir: bin cert_chain: [] date: 2012-10-26 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: faraday requirement: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: 0.7.4 - - < - !ruby/object:Gem::Version version: '0.9' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: 0.7.4 - - < - !ruby/object:Gem::Version version: '0.9' - !ruby/object:Gem::Dependency name: multi_xml requirement: !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version version: '0.2' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version version: '0.2' - !ruby/object:Gem::Dependency name: rake requirement: !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version version: '0.9' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version version: '0.9' - !ruby/object:Gem::Dependency name: hashie requirement: !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version version: '1.2' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version version: '1.2' - !ruby/object:Gem::Dependency name: rash requirement: !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version version: '0.3' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version version: '0.3' - !ruby/object:Gem::Dependency name: rspec requirement: !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version version: '2.6' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version version: '2.6' - !ruby/object:Gem::Dependency name: simple_oauth requirement: !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version version: '0.1' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version version: '0.1' - !ruby/object:Gem::Dependency name: rack-cache requirement: !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version version: '1.1' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version version: '1.1' description: Various middleware for Faraday email: - sferik@gmail.com - wynn.netherland@gmail.com executables: [] extensions: [] extra_rdoc_files: [] files: - .gemtest - .gitignore - .rspec - .travis.yml - CHANGELOG.md - Gemfile - LICENSE.md - README.md - Rakefile - faraday_middleware.gemspec - lib/faraday_middleware.rb - lib/faraday_middleware/addressable_patch.rb - lib/faraday_middleware/backwards_compatibility.rb - lib/faraday_middleware/instrumentation.rb - lib/faraday_middleware/rack_compatible.rb - lib/faraday_middleware/request/encode_json.rb - lib/faraday_middleware/request/method_override.rb - lib/faraday_middleware/request/oauth.rb - lib/faraday_middleware/request/oauth2.rb - lib/faraday_middleware/response/caching.rb - lib/faraday_middleware/response/chunked.rb - lib/faraday_middleware/response/follow_redirects.rb - lib/faraday_middleware/response/mashify.rb - lib/faraday_middleware/response/parse_dates.rb - lib/faraday_middleware/response/parse_json.rb - lib/faraday_middleware/response/parse_marshal.rb - lib/faraday_middleware/response/parse_xml.rb - lib/faraday_middleware/response/parse_yaml.rb - lib/faraday_middleware/response/rashify.rb - lib/faraday_middleware/response_middleware.rb - lib/faraday_middleware/version.rb - spec/caching_test.rb - spec/chunked_spec.rb - spec/encode_json_spec.rb - spec/follow_redirects_spec.rb - spec/helper.rb - spec/mashify_spec.rb - spec/method_override_spec.rb - spec/oauth2_spec.rb - spec/oauth_spec.rb - spec/parse_dates_spec.rb - spec/parse_json_spec.rb - spec/parse_marshal_spec.rb - spec/parse_xml_spec.rb - spec/parse_yaml_spec.rb - spec/rashify_spec.rb homepage: https://github.com/pengwynn/faraday_middleware licenses: [] post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' required_rubygems_version: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: 1.3.6 requirements: [] rubyforge_project: rubygems_version: 1.8.23 signing_key: specification_version: 3 summary: Various middleware for Faraday test_files: - spec/caching_test.rb - spec/chunked_spec.rb - spec/encode_json_spec.rb - spec/follow_redirects_spec.rb - spec/helper.rb - spec/mashify_spec.rb - spec/method_override_spec.rb - spec/oauth2_spec.rb - spec/oauth_spec.rb - spec/parse_dates_spec.rb - spec/parse_json_spec.rb - spec/parse_marshal_spec.rb - spec/parse_xml_spec.rb - spec/parse_yaml_spec.rb - spec/rashify_spec.rb has_rdoc: ruby-faraday-middleware-0.9.0/spec/000077500000000000000000000000001215071311700171515ustar00rootroot00000000000000ruby-faraday-middleware-0.9.0/spec/caching_test.rb000066400000000000000000000102571215071311700221360ustar00rootroot00000000000000require 'test/unit' require 'forwardable' require 'fileutils' require 'rack/cache' require 'faraday' require 'faraday_middleware/response/caching' require 'faraday_middleware/rack_compatible' class CachingTest < Test::Unit::TestCase class TestCache < Hash def read(key) if cached = self[key] Marshal.load(cached) end end def write(key, data) self[key] = Marshal.dump(data) end def fetch(key) read(key) || yield.tap { |data| write(key, data) } end end class Lint < Struct.new(:app) def call(env) app.call(env).on_complete do raise "no headers" unless env[:response_headers].is_a? Hash raise "no response" unless env[:response].is_a? Faraday::Response raise "env not identical" unless env[:response].env.object_id == env.object_id end end end def setup @cache = TestCache.new request_count = 0 response = lambda { |env| [200, {'Content-Type' => 'text/plain'}, "request:#{request_count+=1}"] } @conn = Faraday.new do |b| b.use Lint b.use FaradayMiddleware::Caching, @cache b.adapter :test do |stub| stub.get('/', &response) stub.get('/?foo=bar', &response) stub.post('/', &response) stub.get('/other', &response) end end end extend Forwardable def_delegators :@conn, :get, :post def test_cache_get assert_equal 'request:1', get('/').body assert_equal 'request:1', get('/').body assert_equal 'request:2', get('/other').body assert_equal 'request:2', get('/other').body end def test_response_has_request_params get('/') # make cache response = get('/') assert_equal :get, response.env[:method] assert_equal '/', response.env[:url].request_uri end def test_cache_query_params assert_equal 'request:1', get('/').body assert_equal 'request:2', get('/?foo=bar').body assert_equal 'request:2', get('/?foo=bar').body assert_equal 'request:1', get('/').body end def test_doesnt_cache_post assert_equal 'request:1', post('/').body assert_equal 'request:2', post('/').body assert_equal 'request:3', post('/').body end end # RackCompatible + Rack::Cache class HttpCachingTest < Test::Unit::TestCase include FileUtils CACHE_DIR = File.expand_path('../../tmp/cache', __FILE__) # middleware to check whether "rack.errors" is free of error reports class RackErrorsComplainer < Struct.new(:app) def call(env) response = app.call(env) error_stream = env['rack.errors'].string raise %(unexpected error in 'rack.errors') if error_stream.include? 'error' response end end def setup rm_r CACHE_DIR if File.exists? CACHE_DIR # force reinitializing cache dirs Rack::Cache::Storage.instance.clear request_count = 0 response = lambda { |env| [200, { 'Content-Type' => 'text/plain', 'Cache-Control' => 'public, max-age=900', }, "request:#{request_count+=1}"] } @conn = Faraday.new do |b| b.use RackErrorsComplainer b.use FaradayMiddleware::RackCompatible, Rack::Cache::Context, :metastore => "file:#{CACHE_DIR}/rack/meta", :entitystore => "file:#{CACHE_DIR}/rack/body", :verbose => true b.adapter :test do |stub| stub.get('/', &response) stub.post('/', &response) end end end extend Forwardable def_delegators :@conn, :get, :post def test_cache_get response = get('/', :user_agent => 'test') assert_equal 'request:1', response.body assert_equal :get, response.env[:method] assert_equal 200, response.status response = get('/', :user_agent => 'test') assert_equal 'request:1', response.body assert_equal 'text/plain', response['content-type'] assert_equal :get, response.env[:method] assert response.env[:request].respond_to?(:fetch) assert_equal 200, response.status assert_equal 'request:2', post('/').body end def test_doesnt_cache_post assert_equal 'request:1', get('/').body assert_equal 'request:2', post('/').body assert_equal 'request:3', post('/').body end end unless defined? RUBY_ENGINE and "rbx" == RUBY_ENGINE # rbx bug #1522 ruby-faraday-middleware-0.9.0/spec/chunked_spec.rb000066400000000000000000000043611215071311700221350ustar00rootroot00000000000000require 'helper' require 'faraday_middleware/response/chunked' describe FaradayMiddleware::Chunked, :type => :response do context "no transfer-encoding" do it "doesn't change nil body" do expect(process(nil).body).to be_nil end it "doesn't change an empty body" do expect(process('').body).to eq('') end it "doesn't change a normal body" do expect(process('asdf').body).to eq('asdf') end end context "transfer-encoding gzip" do let(:headers) { {"transfer-encoding" => "gzip"}} it "doesn't change nil body" do expect(process(nil).body).to be_nil end it "doesn't change an empty body" do expect(process('').body).to eq('') end it "doesn't change a normal body" do expect(process('asdf').body).to eq('asdf') end end context "transfer-encoding chunked" do let(:headers) { {"transfer-encoding" => "chunked"}} it "doesn't change nil body" do expect(process(nil).body).to be_nil end it "doesn't change an empty body" do expect(process('').body).to eq('') end it "parses a basic chunked body" do expect(process("10\r\nasdfghjklasdfghj\r\n0\r\n").body).to eq('asdfghjklasdfghj') end it "parses a chunked body with no ending chunk" do expect(process("10\r\nasdfghjklasdfghj\r\n").body).to eq('asdfghjklasdfghj') end it "parses a chunked body with no trailing CRLF on the data chunk" do expect(process("10\r\nasdfghjklasdfghj0\r\n").body).to eq('asdfghjklasdfghj') end it "parses a chunked body with an extension" do expect(process("10;foo=bar\r\nasdfghjklasdfghj\r\n0\r\n").body).to eq('asdfghjklasdfghj') end it "parses a chunked body with two extensions" do expect(process("10;foo=bar;bar=baz\r\nasdfghjklasdfghj\r\n0\r\n").body).to eq('asdfghjklasdfghj') end it "parses a chunked body with two chunks" do expect(process("8\r\nasdfghjk\r\n8\r\nlasdfghj\r\n0\r\n").body).to eq('asdfghjklasdfghj') end end context "transfer-encoding chunked,chunked" do let(:headers) { {"transfer-encoding" => "chunked,chunked"}} it "parses a basic chunked body" do expect(process("10\r\nasdfghjklasdfghj\r\n0\r\n").body).to eq('asdfghjklasdfghj') end end end ruby-faraday-middleware-0.9.0/spec/encode_json_spec.rb000066400000000000000000000042701215071311700230010ustar00rootroot00000000000000require 'helper' require 'faraday_middleware/request/encode_json' describe FaradayMiddleware::EncodeJson do let(:middleware) { described_class.new(lambda{|env| env}) } def process(body, content_type = nil) env = {:body => body, :request_headers => Faraday::Utils::Headers.new} env[:request_headers]['content-type'] = content_type if content_type middleware.call(env) end def result_body() result[:body] end def result_type() result[:request_headers]['content-type'] end context "no body" do let(:result) { process(nil) } it "doesn't change body" do expect(result_body).to be_nil end it "doesn't add content type" do expect(result_type).to be_nil end end context "empty body" do let(:result) { process('') } it "doesn't change body" do expect(result_body).to be_empty end it "doesn't add content type" do expect(result_type).to be_nil end end context "string body" do let(:result) { process('{"a":1}') } it "doesn't change body" do expect(result_body).to eq('{"a":1}') end it "adds content type" do expect(result_type).to eq('application/json') end end context "object body" do let(:result) { process({:a => 1}) } it "encodes body" do expect(result_body).to eq('{"a":1}') end it "adds content type" do expect(result_type).to eq('application/json') end end context "empty object body" do let(:result) { process({}) } it "encodes body" do expect(result_body).to eq('{}') end end context "object body with json type" do let(:result) { process({:a => 1}, 'application/json; charset=utf-8') } it "encodes body" do expect(result_body).to eq('{"a":1}') end it "doesn't change content type" do expect(result_type).to eq('application/json; charset=utf-8') end end context "object body with incompatible type" do let(:result) { process({:a => 1}, 'application/xml; charset=utf-8') } it "doesn't change body" do expect(result_body).to eq({:a => 1}) end it "doesn't change content type" do expect(result_type).to eq('application/xml; charset=utf-8') end end end ruby-faraday-middleware-0.9.0/spec/follow_redirects_spec.rb000066400000000000000000000177161215071311700240720ustar00rootroot00000000000000require 'helper' require 'faraday_middleware/response/follow_redirects' require 'faraday' # expose a method in Test adapter that should have been public Faraday::Adapter::Test::Stubs.class_eval { public :new_stub } describe FaradayMiddleware::FollowRedirects do let(:middleware_options) { Hash.new } shared_examples_for "a successful redirection" do |status_code| it "follows the redirection for a GET request" do expect(connection do |stub| stub.get('/permanent') { [status_code, {'Location' => '/found'}, ''] } stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] } end.get('/permanent').body).to eq 'fin' end it "follows the redirection for a HEAD request" do expect(connection do |stub| stub.head('/permanent') { [status_code, {'Location' => '/found'}, ''] } stub.head('/found') { [200, {'Content-Type' => 'text/plain'}, ''] } end.head('/permanent').status).to eq 200 end it "follows the redirection for a OPTIONS request" do expect(connection do |stub| stub.new_stub(:options, '/permanent') { [status_code, {'Location' => '/found'}, ''] } stub.new_stub(:options, '/found') { [200, {'Content-Type' => 'text/plain'}, ''] } end.run_request(:options, '/permanent', nil, nil).status).to eq 200 end end shared_examples_for "a forced GET redirection" do |status_code| [:put, :post, :delete, :patch].each do |method| it "a #{method.to_s.upcase} request is converted to a GET" do expect(connection do |stub| stub.new_stub(method, '/redirect') { [status_code, {'Location' => '/found'}, 'elsewhere'] } stub.get('/found') { |env| body = env[:body] and body.empty? && (body = nil) [200, {'Content-Type' => 'text/plain'}, body.inspect] } end.run_request(method, '/redirect', 'request data', nil).body).to eq('nil') end end end shared_examples_for "a replayed redirection" do |status_code| it "redirects with the original request headers" do conn = connection do |stub| stub.get('/redirect') { [status_code, {'Location' => '/found'}, ''] } stub.get('/found') { |env| [200, {'Content-Type' => 'text/plain'}, env[:request_headers]['X-Test-Value']] } end response = conn.get('/redirect') { |req| req.headers['X-Test-Value'] = 'success' } expect(response.body).to eq('success') end [:put, :post, :delete, :patch].each do |method| it "replays a #{method.to_s.upcase} request" do expect(connection do |stub| stub.new_stub(method, '/redirect') { [status_code, {'Location' => '/found'}, ''] } stub.new_stub(method, '/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] } end.run_request(method, '/redirect', nil, nil).body).to eq 'fin' end end [:put, :post, :patch].each do |method| it "forwards request body for a #{method.to_s.upcase} request" do conn = connection do |stub| stub.new_stub(method, '/redirect') { [status_code, {'Location' => '/found'}, ''] } stub.new_stub(method, '/found') { |env| [200, {'Content-Type' => 'text/plain'}, env[:body]] } end response = conn.run_request(method, '/redirect', 'original data', nil) expect(response.body).to eq('original data') end end end it "returns non-redirect response results" do expect(connection do |stub| stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] } end.get('/found').body).to eq 'fin' end it "follows a single redirection" do expect(connection do |stub| stub.get('/') { [301, {'Location' => '/found'}, ''] } stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] } end.get('/').body).to eq 'fin' end it "follows many redirections" do expect(connection do |stub| stub.get('/') { [301, {'Location' => '/redirect1'}, ''] } stub.get('/redirect1') { [301, {'Location' => '/redirect2'}, ''] } stub.get('/redirect2') { [301, {'Location' => '/found'}, ''] } stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] } end.get('/').body).to eq 'fin' end it "raises a FaradayMiddleware::RedirectLimitReached after 3 redirections (by default)" do conn = connection do |stub| stub.get('/') { [301, {'Location' => '/redirect1'}, ''] } stub.get('/redirect1') { [301, {'Location' => '/redirect2'}, ''] } stub.get('/redirect2') { [301, {'Location' => '/redirect3'}, ''] } stub.get('/redirect3') { [301, {'Location' => '/found'}, ''] } stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] } end expect{ conn.get('/') }.to raise_error(FaradayMiddleware::RedirectLimitReached) end it "raises a FaradayMiddleware::RedirectLimitReached after the initialized limit" do conn = connection(:limit => 1) do |stub| stub.get('/') { [301, {'Location' => '/redirect1'}, ''] } stub.get('/redirect1') { [301, {'Location' => '/found'}, ''] } stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] } end expect{ conn.get('/') }.to raise_error(FaradayMiddleware::RedirectLimitReached) end context "when cookies option" do let(:cookies) { 'cookie1=abcdefg; cookie2=1234567; cookie3=awesome' } context "is :all" do it "puts all cookies from the response into the next request" do expect(connection(:cookies => :all) do |stub| stub.get('/') { [301, {'Location' => '/found', 'Cookies' => cookies }, ''] } stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, ''] } end.get('/').env[:request_headers][:cookies]).to eq(cookies) end it "not set cookies header on request when response has no cookies" do expect(connection(:cookies => :all) do |stub| stub.get('/') { [301, {'Location' => '/found'}, ''] } stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, ''] } end.get('/').env[:request_headers].has_key?('Cookies')).to eq(false) end end context "is an array of cookie names" do it "puts selected cookies from the response into the next request" do expect(connection(:cookies => ['cookie2']) do |stub| stub.get('/') { [301, {'Location' => '/found', 'Cookies' => cookies }, ''] } stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, ''] } end.get('/').env[:request_headers][:cookies]).to eq('cookie2=1234567') end end end context "for an HTTP 301 response" do it_behaves_like 'a successful redirection', 301 it_behaves_like 'a forced GET redirection', 301 end context "for an HTTP 302 response" do it_behaves_like 'a successful redirection', 302 context "by default" do it_behaves_like 'a forced GET redirection', 302 end context "with standards compliancy enabled" do let(:middleware_options) { { :standards_compliant => true } } it_behaves_like 'a replayed redirection', 302 end end context "for an HTTP 303 response" do it_behaves_like 'a successful redirection', 303 it_behaves_like 'a forced GET redirection', 303 end context "for an HTTP 307 response" do it_behaves_like 'a successful redirection', 307 it_behaves_like 'a replayed redirection', 307 end # checks env hash in request phase for basic validity class Lint < Struct.new(:app) def call(env) if env[:status] or env[:response] or env[:response_headers] raise "invalid request: #{env.inspect}" end app.call(env) end end private def connection(options = middleware_options) Faraday.new do |c| c.use described_class, options c.use Lint c.adapter :test do |stub| yield(stub) if block_given? end end end end ruby-faraday-middleware-0.9.0/spec/helper.rb000066400000000000000000000022251215071311700207560ustar00rootroot00000000000000if ENV['COVERAGE'] require 'simplecov' SimpleCov.formatter = Class.new do def format(result) SimpleCov::Formatter::HTMLFormatter.new.format(result) unless ENV['CI'] File.open('coverage/covered_percent', 'w') do |f| f.puts result.source_files.covered_percent.to_i end end end SimpleCov.start do # add_filter 'faraday_middleware.rb' add_filter 'backwards_compatibility.rb' end end require 'rspec' module ResponseMiddlewareExampleGroup def self.included(base) base.let(:options) { Hash.new } base.let(:headers) { Hash.new } base.let(:middleware) { described_class.new(lambda {|env| Faraday::Response.new(env) }, options) } end def process(body, content_type = nil, options = {}) env = { :body => body, :request => options, :response_headers => Faraday::Utils::Headers.new(headers) } env[:response_headers]['content-type'] = content_type if content_type middleware.call(env) end end RSpec.configure do |config| config.include ResponseMiddlewareExampleGroup, :type => :response config.expect_with :rspec do |c| c.syntax = :expect end end ruby-faraday-middleware-0.9.0/spec/mashify_spec.rb000066400000000000000000000063761215071311700221640ustar00rootroot00000000000000require 'helper' require 'faraday_middleware/response/mashify' describe FaradayMiddleware::Mashify do context "during configuration" do it "allows for a custom Mash class to be set" do expect(described_class).to respond_to(:mash_class) expect(described_class).to respond_to(:mash_class=) end end context "when used" do before(:each) { described_class.mash_class = ::Hashie::Mash } let(:mashify) { described_class.new } it "creates a Hashie::Mash from the body" do env = { :body => { "name" => "Erik Michaels-Ober", "username" => "sferik" } } me = mashify.on_complete(env) expect(me.class).to eq(Hashie::Mash) end it "handles strings" do env = { :body => "Most amazing string EVER" } me = mashify.on_complete(env) expect(me).to eq("Most amazing string EVER") end it "handles arrays" do env = { :body => [123, 456] } values = mashify.on_complete(env) expect(values.first).to eq(123) expect(values.last).to eq(456) end it "handles arrays of hashes" do env = { :body => [{ "username" => "sferik" }, { "username" => "pengwynn" }] } us = mashify.on_complete(env) expect(us.first.username).to eq('sferik') expect(us.last.username).to eq('pengwynn') end it "handles nested arrays of hashes" do env = { :body => [[{ "username" => "sferik" }, { "username" => "pengwynn" }]] } us = mashify.on_complete(env) expect(us.first.first.username).to eq('sferik') expect(us.first.last.username).to eq('pengwynn') end it "handles mixed arrays" do env = { :body => [123, { "username" => "sferik" }, 456] } values = mashify.on_complete(env) expect(values.first).to eq(123) expect(values.last).to eq(456) expect(values[1].username).to eq('sferik') end it "allows for use of custom Mash subclasses at the class level" do class MyMash < ::Hashie::Mash; end described_class.mash_class = MyMash env = { :body => { "name" => "Erik Michaels-Ober", "username" => "sferik" } } me = mashify.on_complete(env) expect(me.class).to eq(MyMash) end it "allows for use of custom Mash subclasses at the instance level" do class MyMash < ::Hashie::Mash; end mashify = described_class.new(nil, :mash_class => MyMash) env = { :body => { "name" => "Erik Michaels-Ober", "username" => "sferik" } } me = mashify.on_complete(env) expect(me.class).to eq(MyMash) end end context "integration test" do let(:stubs) { Faraday::Adapter::Test::Stubs.new } let(:connection) do Faraday::Connection.new do |builder| builder.adapter :test, stubs builder.use described_class end end # although it is not good practice to pass a hash as the body, if we add ParseJson # to the middleware stack we end up testing two middlewares instead of one it "creates a Hash from the body" do stubs.get('/hash') { data = { 'name' => 'Erik Michaels-Ober', 'username' => 'sferik' } [200, {'content-type' => 'application/json; charset=utf-8'}, data] } me = connection.get('/hash').body expect(me.name).to eq('Erik Michaels-Ober') expect(me.username).to eq('sferik') end end end ruby-faraday-middleware-0.9.0/spec/method_override_spec.rb000066400000000000000000000043451215071311700236750ustar00rootroot00000000000000require 'helper' require 'faraday_middleware/request/method_override' describe FaradayMiddleware::MethodOverride do let(:middleware) { described_class.new(lambda {|env| env }, *options) } let(:env) { middleware.call request_env(request_method) } def request_env(method) { :method => method, :request_headers => Faraday::Utils::Headers.new } end shared_examples "overrides method" do |method| it "sets physical method to POST" do expect(env[:method]).to eq(:post) end it "sets header to #{method}" do expect(env[:request_headers]['X-Http-Method-Override']).to eq(method) end end shared_examples "doesn't override method" do |method| it "keeps original method" do expect(env[:method]).to eq(method) end it "doesn't set header value" do expect(env[:request_headers]).not_to have_key('X-Http-Method-Override') end end context "with default options" do let(:options) { nil } context "GET" do let(:request_method) { :get } include_examples "doesn't override method", :get end context "POST" do let(:request_method) { :post } include_examples "doesn't override method", :post end context "PUT" do let(:request_method) { :put } include_examples "overrides method", 'PUT' end end context "configured to rewrite [:patch, :delete]" do let(:options) { [{ :rewrite => [:patch, :delete] }] } context "PUT" do let(:request_method) { :put } include_examples "doesn't override method", :put end context "PATCH" do let(:request_method) { :patch } include_examples "overrides method", 'PATCH' end context "DELETE" do let(:request_method) { :delete } include_examples "overrides method", 'DELETE' end end context "configured to rewrite ['PATCH']" do let(:options) { [{ :rewrite => %w[PATCH] }] } context "PATCH" do let(:request_method) { :patch } include_examples "overrides method", 'PATCH' end end context "with invalid option" do let(:options) { [{ :hello => 'world' }] } let(:request_method) { :get } it "raises key error" do expect{ env }.to raise_error(IndexError, /key not found/) end end end ruby-faraday-middleware-0.9.0/spec/oauth2_spec.rb000066400000000000000000000065111215071311700217150ustar00rootroot00000000000000require 'helper' require 'uri' require 'faraday_middleware/request/oauth2' require 'faraday/utils' describe FaradayMiddleware::OAuth2 do def query_params(env) Faraday::Utils.parse_query env[:url].query end def auth_header(env) env[:request_headers]['Authorization'] end def perform(params = {}, headers = {}) env = { :url => URI('http://example.com/?' + Faraday::Utils.build_query(params)), :request_headers => Faraday::Utils::Headers.new.update(headers) } app = make_app app.call(env) end def make_app described_class.new(lambda{|env| env}, *Array(options)) end context "no token configured" do let(:options) { nil } it "doesn't add params" do request = perform(:q => 'hello') expect(query_params(request)).to eq('q' => 'hello') end it "doesn't add headers" do expect(auth_header(perform)).to be_nil end it "creates header for explicit token" do request = perform(:q => 'hello', :access_token => 'abc123') expect(query_params(request)).to eq('q' => 'hello', 'access_token' => 'abc123') expect(auth_header(request)).to eq(%(Token token="abc123")) end end context "default token configured" do let(:options) { 'XYZ' } it "adds token param" do expect(query_params(perform(:q => 'hello'))).to eq('q' => 'hello', 'access_token' => 'XYZ') end it "adds token header" do expect(auth_header(perform)).to eq(%(Token token="XYZ")) end it "overrides default with explicit token" do request = perform(:q => 'hello', :access_token => 'abc123') expect(query_params(request)).to eq('q' => 'hello', 'access_token' => 'abc123') expect(auth_header(request)).to eq(%(Token token="abc123")) end it "clears default with empty explicit token" do request = perform(:q => 'hello', :access_token => nil) expect(query_params(request)).to eq('q' => 'hello', 'access_token' => nil) expect(auth_header(request)).to be_nil end end context "existing Authorization header" do let(:options) { 'XYZ' } subject { perform({:q => 'hello'}, 'Authorization' => 'custom') } it "adds token param" do expect(query_params(subject)).to eq('q' => 'hello', 'access_token' => 'XYZ') end it "doesn't override existing header" do expect(auth_header(subject)).to eq('custom') end end context "custom param name configured" do let(:options) { ['XYZ', {:param_name => :oauth}] } it "adds token param" do expect(query_params(perform)).to eq('oauth' => 'XYZ') end it "overrides default with explicit token" do request = perform(:oauth => 'abc123') expect(query_params(request)).to eq('oauth' => 'abc123') expect(auth_header(request)).to eq(%(Token token="abc123")) end end context "options without token configuration" do let(:options) { [{:param_name => :oauth}] } it "doesn't add param" do expect(query_params(perform)).to be_empty end it "overrides default with explicit token" do expect(query_params(perform(:oauth => 'abc123'))).to eq('oauth' => 'abc123') end end context "invalid param name configured" do let(:options) { ['XYZ', {:param_name => nil}] } it "raises error" do expect{ make_app }.to raise_error(ArgumentError, ":param_name can't be blank") end end end ruby-faraday-middleware-0.9.0/spec/oauth_spec.rb000066400000000000000000000113561215071311700216360ustar00rootroot00000000000000require 'helper' require 'faraday_middleware/request/oauth' require 'uri' require 'forwardable' describe FaradayMiddleware::OAuth do def auth_header(env) env[:request_headers]['Authorization'] end def auth_values(env) if auth = auth_header(env) raise "invalid header: #{auth.inspect}" unless auth.sub!('OAuth ', '') Hash[*auth.split(/, |=/)] end end def perform(oauth_options = {}, headers = {}, params = {}) env = { :url => URI('http://example.com/'), :request_headers => Faraday::Utils::Headers.new.update(headers), :request => {}, :body => params } unless oauth_options.is_a? Hash and oauth_options.empty? env[:request][:oauth] = oauth_options end app = make_app app.call(env) end def make_app described_class.new(lambda{|env| env}, *Array(options)) end context "invalid options" do let(:options) { nil } it "errors out" do expect{ make_app }.to raise_error(ArgumentError) end end context "empty options" do let(:options) { [{}] } it "signs request" do auth = auth_values(perform) expected_keys = %w[ oauth_nonce oauth_signature oauth_signature_method oauth_timestamp oauth_version ] expect(auth.keys).to match_array expected_keys end end context "configured with consumer and token" do let(:options) do [{ :consumer_key => 'CKEY', :consumer_secret => 'CSECRET', :token => 'TOKEN', :token_secret => 'TSECRET' }] end it "adds auth info to the header" do auth = auth_values(perform) expected_keys = %w[ oauth_consumer_key oauth_nonce oauth_signature oauth_signature_method oauth_timestamp oauth_token oauth_version ] expect(auth.keys).to match_array expected_keys expect(auth['oauth_version']).to eq(%("1.0")) expect(auth['oauth_signature_method']).to eq(%("HMAC-SHA1")) expect(auth['oauth_consumer_key']).to eq(%("CKEY")) expect(auth['oauth_token']).to eq(%("TOKEN")) end it "doesn't override existing header" do request = perform({}, "Authorization" => "iz me!") expect(auth_header(request)).to eq("iz me!") end it "can override oauth options per-request" do auth = auth_values(perform(:consumer_key => 'CKEY2')) expect(auth['oauth_consumer_key']).to eq(%("CKEY2")) expect(auth['oauth_token']).to eq(%("TOKEN")) end it "can turn off oauth signing per-request" do expect(auth_header(perform(false))).to be_nil end end context "configured without token" do let(:options) { [{ :consumer_key => 'CKEY', :consumer_secret => 'CSECRET' }] } it "adds auth info to the header" do auth = auth_values(perform) expect(auth).to include('oauth_consumer_key') expect(auth).not_to include('oauth_token') end end context "handling body parameters" do let(:options) { [{ :consumer_key => 'CKEY', :consumer_secret => 'CSECRET', :nonce => '547fed103e122eecf84c080843eedfe6', :timestamp => '1286830180'}] } let(:value) { {'foo' => 'bar'} } let(:type_json) { {'Content-Type' => 'application/json'} } let(:type_form) { {'Content-Type' => 'application/x-www-form-urlencoded'} } extend Forwardable query_method = :build_nested_query query_module = ::Faraday::Utils.respond_to?(query_method) ? 'Faraday::Utils' : 'Rack::Utils' def_delegator query_module, query_method it "does not include the body for JSON" do auth_header_with = auth_header(perform({}, type_json, '{"foo":"bar"}')) auth_header_without = auth_header(perform({}, type_json, {})) expect(auth_header_with).to eq(auth_header_without) end it "includes the body parameters with form Content-Type" do auth_header_with = auth_header(perform({}, type_form, {})) auth_header_without = auth_header(perform({}, type_form, value)) expect(auth_header_with).not_to eq(auth_header_without) end it "includes the body parameters with an unspecified Content-Type" do auth_header_with = auth_header(perform({}, {}, value)) auth_header_without = auth_header(perform({}, type_form, value)) expect(auth_header_with).to eq(auth_header_without) end it "includes the body parameters for form type with string body" do # simulates the behavior of Faraday::MiddleWare::UrlEncoded value = { 'foo' => ['bar', 'baz', 'wat'] } auth_header_hash = auth_header(perform({}, type_form, value)) auth_header_string = auth_header(perform({}, type_form, build_nested_query(value))) expect(auth_header_string).to eq(auth_header_hash) end end end ruby-faraday-middleware-0.9.0/spec/parse_dates_spec.rb000066400000000000000000000023351215071311700230050ustar00rootroot00000000000000require 'helper' require 'faraday_middleware/response/parse_dates' require 'json' describe FaradayMiddleware::ParseDates, :type => :response do let(:parsed){ if RUBY_VERSION > "1.9" "2012-02-01 13:14:15 UTC" else "Wed Feb 01 13:14:15 UTC 2012" end } it "parses dates" do expect(process({"x" => "2012-02-01T13:14:15Z"}).body["x"].to_s).to eq(parsed) end it "parses nested dates in hash" do expect(process({"x" => {"y" => "2012-02-01T13:14:15Z"}}).body["x"]["y"].to_s).to eq(parsed) end it "parses nested dates in arrays" do expect(process({"x" => [{"y" =>"2012-02-01T13:14:15Z"}]}).body["x"][0]["y"].to_s).to eq(parsed) end it "returns nil when body is empty" do expect(process(nil).body).to eq(nil) end it "leaves arrays with ids alone" do expect(process({"x" => [1,2,3]}).body).to eq({"x" => [1,2,3]}) end it "does not parse date-like things" do expect(process({"x" => "2012-02-01T13:14:15Z bla"}).body["x"].to_s).to eq "2012-02-01T13:14:15Z bla" expect(process({"x" => "12012-02-01T13:14:15Z"}).body["x"].to_s).to eq "12012-02-01T13:14:15Z" expect(process({"x" => "2012-02-01T13:14:15Z\nfoo"}).body["x"].to_s).to eq "2012-02-01T13:14:15Z\nfoo" end end ruby-faraday-middleware-0.9.0/spec/parse_json_spec.rb000066400000000000000000000064001215071311700226530ustar00rootroot00000000000000require 'helper' require 'faraday_middleware/response/parse_json' describe FaradayMiddleware::ParseJson, :type => :response do context "no type matching" do it "doesn't change nil body" do expect(process(nil).body).to be_nil end it "nullifies empty body" do expect(process('').body).to be_nil end it "parses json body" do response = process('{"a":1}') expect(response.body).to eq('a' => 1) expect(response.env[:raw_body]).to be_nil end end context "with preserving raw" do let(:options) { {:preserve_raw => true} } it "parses json body" do response = process('{"a":1}') expect(response.body).to eq('a' => 1) expect(response.env[:raw_body]).to eq('{"a":1}') end it "can opt out of preserving raw" do response = process('{"a":1}', nil, :preserve_raw => false) expect(response.env[:raw_body]).to be_nil end end context "with regexp type matching" do let(:options) { {:content_type => /\bjson$/} } it "parses json body of correct type" do response = process('{"a":1}', 'application/x-json') expect(response.body).to eq('a' => 1) end it "ignores json body of incorrect type" do response = process('{"a":1}', 'text/json-xml') expect(response.body).to eq('{"a":1}') end end context "with array type matching" do let(:options) { {:content_type => %w[a/b c/d]} } it "parses json body of correct type" do expect(process('{"a":1}', 'a/b').body).to be_a(Hash) expect(process('{"a":1}', 'c/d').body).to be_a(Hash) end it "ignores json body of incorrect type" do expect(process('{"a":1}', 'a/d').body).not_to be_a(Hash) end end it "chokes on invalid json" do ['{!', '"a"', 'true', 'null', '1'].each do |data| expect{ process(data) }.to raise_error(Faraday::Error::ParsingError) end end context "with mime type fix" do let(:middleware) { app = described_class::MimeTypeFix.new(lambda {|env| Faraday::Response.new(env) }, :content_type => /^text\//) described_class.new(app, :content_type => 'application/json') } it "ignores completely incompatible type" do response = process('{"a":1}', 'application/xml') expect(response.body).to eq('{"a":1}') end it "ignores compatible type with bad data" do response = process('var a = 1', 'text/javascript') expect(response.body).to eq('var a = 1') expect(response['content-type']).to eq('text/javascript') end it "corrects compatible type and data" do response = process('{"a":1}', 'text/javascript') expect(response.body).to be_a(Hash) expect(response['content-type']).to eq('application/json') end it "corrects compatible type even when data starts with whitespace" do response = process(%( \r\n\t{"a":1}), 'text/javascript') expect(response.body).to be_a(Hash) expect(response['content-type']).to eq('application/json') end end context "HEAD responses" do it "nullifies the body if it's only one space" do response = process(' ') expect(response.body).to be_nil end it "nullifies the body if it's two spaces" do response = process(' ') expect(response.body).to be_nil end end end ruby-faraday-middleware-0.9.0/spec/parse_marshal_spec.rb000066400000000000000000000006741215071311700233400ustar00rootroot00000000000000require 'helper' require 'faraday_middleware/response/parse_marshal' describe FaradayMiddleware::ParseMarshal, :type => :response do it "restores a marshaled dump" do expect(process(Marshal.dump(:a => 1)).body).to be_eql(:a => 1) end it "nulifies blank response" do expect(process('').body).to be_nil end it "chokes on invalid content" do expect{ process('abc') }.to raise_error(Faraday::Error::ParsingError) end end ruby-faraday-middleware-0.9.0/spec/parse_xml_spec.rb000066400000000000000000000037501215071311700225070ustar00rootroot00000000000000require 'helper' require 'faraday_middleware/response/parse_xml' describe FaradayMiddleware::ParseXml, :type => :response do let(:xml) { 'Erik Michaels-Obersferik' } let(:user) { {'user' => {'name' => 'Erik Michaels-Ober', 'screen_name' => 'sferik'} } } context "no type matching" do it "doesn't change nil body" do expect(process(nil).body).to be_nil end it "turns empty body into empty hash" do expect(process('').body).to be_eql({}) end it "parses xml body" do response = process(xml) expect(response.body).to eq(user) expect(response.env[:raw_body]).to be_nil end end context "with preserving raw" do let(:options) { {:preserve_raw => true} } it "parses xml body" do response = process(xml) expect(response.body).to eq(user) expect(response.env[:raw_body]).to eq(xml) end it "can opt out of preserving raw" do response = process(xml, nil, :preserve_raw => false) expect(response.env[:raw_body]).to be_nil end end context "with regexp type matching" do let(:options) { {:content_type => /\bxml$/} } it "parses xml body of correct type" do response = process(xml, 'application/xml') expect(response.body).to eq(user) end it "ignores xml body of incorrect type" do response = process(xml, 'text/html') expect(response.body).to eq(xml) end end context "with array type matching" do let(:options) { {:content_type => %w[a/b c/d]} } it "parses xml body of correct type" do expect(process(xml, 'a/b').body).to be_a(Hash) expect(process(xml, 'c/d').body).to be_a(Hash) end it "ignores xml body of incorrect type" do expect(process(xml, 'a/d').body).not_to be_a(Hash) end end it "chokes on invalid xml" do ['{!', '"a"', 'true', 'null', '1'].each do |data| expect{ process(data) }.to raise_error(Faraday::Error::ParsingError) end end end ruby-faraday-middleware-0.9.0/spec/parse_yaml_spec.rb000066400000000000000000000026421215071311700226500ustar00rootroot00000000000000require 'helper' require 'faraday_middleware/response/parse_yaml' describe FaradayMiddleware::ParseYaml, :type => :response do context "no type matching" do it "doesn't change nil body" do expect(process(nil).body).to be_nil end it "returns false for empty body" do expect(process('').body).to be_false end it "parses yaml body" do response = process('a: 1') expect(response.body).to eq('a' => 1) expect(response.env[:raw_body]).to be_nil end end context "with preserving raw" do let(:options) { {:preserve_raw => true} } it "parses yaml body" do response = process('a: 1') expect(response.body).to eq('a' => 1) expect(response.env[:raw_body]).to eq('a: 1') end it "can opt out of preserving raw" do response = process('a: 1', nil, :preserve_raw => false) expect(response.env[:raw_body]).to be_nil end end context "with regexp type matching" do let(:options) { {:content_type => /\byaml$/} } it "parses json body of correct type" do response = process('a: 1', 'application/x-yaml') expect(response.body).to eq('a' => 1) end it "ignores json body of incorrect type" do response = process('a: 1', 'text/yaml-xml') expect(response.body).to eq('a: 1') end end it "chokes on invalid yaml" do expect{ process('{!') }.to raise_error(Faraday::Error::ParsingError) end end ruby-faraday-middleware-0.9.0/spec/rashify_spec.rb000066400000000000000000000044671215071311700221700ustar00rootroot00000000000000require 'helper' require 'faraday_middleware/response/rashify' describe FaradayMiddleware::Rashify do context "when used" do let(:rashify) { described_class.new } it "creates a Hashie::Rash from the body" do env = { :body => { "name" => "Erik Michaels-Ober", "username" => "sferik" } } me = rashify.on_complete(env) expect(me.class).to eq(Hashie::Rash) end it "handles strings" do env = { :body => "Most amazing string EVER" } me = rashify.on_complete(env) expect(me).to eq("Most amazing string EVER") end it "handles hashes and decamelcase the keys" do env = { :body => { "name" => "Erik Michaels-Ober", "userName" => "sferik" } } me = rashify.on_complete(env) expect(me.name).to eq('Erik Michaels-Ober') expect(me.user_name).to eq('sferik') end it "handles arrays" do env = { :body => [123, 456] } values = rashify.on_complete(env) expect(values.first).to eq(123) expect(values.last).to eq(456) end it "handles arrays of hashes" do env = { :body => [{ "username" => "sferik" }, { "username" => "pengwynn" }] } us = rashify.on_complete(env) expect(us.first.username).to eq('sferik') expect(us.last.username).to eq('pengwynn') end it "handles mixed arrays" do env = { :body => [123, { "username" => "sferik" }, 456] } values = rashify.on_complete(env) expect(values.first).to eq(123) expect(values.last).to eq(456) expect(values[1].username).to eq('sferik') end end context "integration test" do let(:stubs) { Faraday::Adapter::Test::Stubs.new } let(:connection) do Faraday::Connection.new do |builder| builder.adapter :test, stubs builder.use described_class end end # although it is not good practice to pass a hash as the body, if we add ParseJson # to the middleware stack we end up testing two middlewares instead of one it "creates a Hash from the body" do stubs.get('/hash') { data = { 'name' => 'Erik Michaels-Ober', 'username' => 'sferik' } [200, {'content-type' => 'application/json; charset=utf-8'}, data] } me = connection.get('/hash').body expect(me.name).to eq('Erik Michaels-Ober') expect(me.username).to eq('sferik') end end end