faraday_middleware 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/.rspec +0 -1
  2. data/.travis.yml +1 -2
  3. data/CHANGELOG.md +9 -9
  4. data/Gemfile +3 -3
  5. data/README.md +2 -2
  6. data/Rakefile +9 -2
  7. data/faraday_middleware.gemspec +16 -24
  8. data/lib/faraday_middleware.rb +51 -11
  9. data/lib/faraday_middleware/addressable_patch.rb +20 -0
  10. data/lib/faraday_middleware/backwards_compatibility.rb +30 -0
  11. data/lib/faraday_middleware/instrumentation.rb +30 -0
  12. data/lib/faraday_middleware/rack_compatible.rb +76 -0
  13. data/lib/faraday_middleware/request/encode_json.rb +50 -0
  14. data/lib/faraday_middleware/request/oauth.rb +61 -0
  15. data/lib/faraday_middleware/request/oauth2.rb +60 -0
  16. data/lib/faraday_middleware/response/caching.rb +76 -0
  17. data/lib/faraday_middleware/response/follow_redirects.rb +53 -0
  18. data/lib/{faraday → faraday_middleware}/response/mashify.rb +2 -2
  19. data/lib/faraday_middleware/response/parse_json.rb +35 -0
  20. data/lib/faraday_middleware/response/parse_marshal.rb +10 -0
  21. data/lib/faraday_middleware/response/parse_xml.rb +11 -0
  22. data/lib/faraday_middleware/response/parse_yaml.rb +10 -0
  23. data/lib/faraday_middleware/response/rashify.rb +9 -0
  24. data/lib/faraday_middleware/response_middleware.rb +78 -0
  25. data/lib/faraday_middleware/version.rb +1 -1
  26. data/spec/caching_test.rb +122 -0
  27. data/spec/encode_json_spec.rb +95 -0
  28. data/spec/follow_redirects_spec.rb +33 -0
  29. data/spec/helper.rb +27 -12
  30. data/spec/mashify_spec.rb +8 -7
  31. data/spec/oauth2_spec.rb +100 -32
  32. data/spec/oauth_spec.rb +83 -28
  33. data/spec/parse_json_spec.rb +71 -46
  34. data/spec/parse_marshal_spec.rb +9 -26
  35. data/spec/parse_xml_spec.rb +56 -24
  36. data/spec/parse_yaml_spec.rb +40 -20
  37. data/spec/rashify_spec.rb +4 -3
  38. metadata +59 -57
  39. data/lib/faraday/request/oauth.rb +0 -23
  40. data/lib/faraday/request/oauth2.rb +0 -24
  41. data/lib/faraday/response/parse_json.rb +0 -20
  42. data/lib/faraday/response/parse_marshal.rb +0 -10
  43. data/lib/faraday/response/parse_xml.rb +0 -11
  44. data/lib/faraday/response/parse_yaml.rb +0 -11
  45. data/lib/faraday/response/rashify.rb +0 -19
data/.rspec CHANGED
@@ -1,3 +1,2 @@
1
1
  --color
2
- --format=nested
3
2
  --backtrace
@@ -1,8 +1,7 @@
1
1
  rvm:
2
2
  - 1.8.7
3
- - 1.9.1
4
3
  - 1.9.2
4
+ - 1.9.3
5
5
  - jruby
6
6
  - rbx
7
7
  - ree
8
- - ruby-head
@@ -1,10 +1,10 @@
1
- # Changelog
2
-
3
- ### 0.0.2 September 25, 2010
4
-
5
- * Mashify now handles arrays of non-hashes
6
-
7
- ### 0.0.1 June 27, 2010
8
-
9
- * MultiJSON
1
+ # Changelog
2
+
3
+ ### 0.0.2 September 25, 2010
4
+
5
+ * Mashify now handles arrays of non-hashes
6
+
7
+ ### 0.0.1 June 27, 2010
8
+
9
+ * MultiJSON
10
10
  * Mashify
data/Gemfile CHANGED
@@ -1,7 +1,7 @@
1
1
  source 'http://rubygems.org'
2
2
 
3
- platforms :jruby do
4
- gem "jruby-openssl", "~> 0.7"
5
- end
3
+ gem 'simplecov' unless ENV['CI']
4
+ gem 'json', :platforms => [:ruby_18, :jruby]
5
+ gem 'jruby-openssl', '~> 0.7', :platforms => :jruby
6
6
 
7
7
  gemspec
data/README.md CHANGED
@@ -3,11 +3,11 @@ Faraday Middleware
3
3
 
4
4
  A collection of some useful [Faraday](https://github.com/technoweenie/faraday) middleware
5
5
 
6
- Installation
6
+ <a name="installation">Installation</a>
7
7
  ------------
8
8
  gem install faraday_middleware
9
9
 
10
- Examples
10
+ <a name="examples">Examples</a>
11
11
  --------
12
12
  Let's decode the response body with [MultiJson](https://github.com/intridea/multi_json)!
13
13
 
data/Rakefile CHANGED
@@ -1,10 +1,17 @@
1
1
  #!/usr/bin/env rake
2
2
 
3
+ task :default => [:enable_coverage, :spec, :test]
4
+
3
5
  require 'bundler'
4
6
  Bundler::GemHelper.install_tasks
5
7
 
6
8
  require 'rspec/core/rake_task'
7
9
  RSpec::Core::RakeTask.new(:spec)
8
10
 
9
- task :test => :spec
10
- task :default => :spec
11
+ task :enable_coverage do
12
+ ENV['COVERAGE'] = 'yes' unless ENV['CI']
13
+ end
14
+
15
+ task :test do
16
+ sh 'ruby', '-Ilib', 'spec/caching_test.rb'
17
+ end
@@ -1,31 +1,23 @@
1
1
  require File.expand_path('../lib/faraday_middleware/version', __FILE__)
2
2
 
3
3
  Gem::Specification.new do |gem|
4
- gem.name = 'faraday_middleware'
5
- gem.summary = %q{Various middleware for Faraday}
6
- gem.description = gem.summary
7
-
8
- gem.homepage = 'https://github.com/pengwynn/faraday_middleware'
9
-
10
- gem.authors = ["Erik Michaels-Ober", "Wynn Netherland"]
11
- gem.email = ['sferik@gmail.com', 'wynn.netherland@gmail.com']
12
-
13
- gem.version = FaradayMiddleware::VERSION
14
-
15
- gem.require_paths = ['lib']
16
- gem.files = `git ls-files`.split("\n")
17
- gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
- gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
-
20
- gem.required_rubygems_version = Gem::Requirement.new('>= 1.3.6')
21
-
22
- gem.add_runtime_dependency 'faraday', '~> 0.7.3'
4
+ gem.add_dependency 'faraday', ['>= 0.7.4', '< 0.9']
5
+ gem.add_development_dependency 'multi_xml', '~> 0.2'
23
6
  gem.add_development_dependency 'rake', '~> 0.9'
24
- gem.add_development_dependency 'rspec', '~> 2.6'
25
- gem.add_development_dependency 'simplecov', '~> 0.4'
7
+ gem.add_development_dependency 'hashie', '~> 1.2'
26
8
  gem.add_development_dependency 'rash', '~> 0.3'
27
- gem.add_development_dependency 'multi_json', '~> 1.0'
28
- gem.add_development_dependency 'multi_xml', '~> 0.2'
29
- gem.add_development_dependency 'oauth2', '~> 0.5.0'
9
+ gem.add_development_dependency 'rspec', '~> 2.6'
30
10
  gem.add_development_dependency 'simple_oauth', '~> 0.1'
11
+ gem.add_development_dependency 'rack-cache', '~> 1.1'
12
+ gem.authors = ["Erik Michaels-Ober", "Wynn Netherland"]
13
+ gem.description = %q{Various middleware for Faraday}
14
+ gem.email = ['sferik@gmail.com', 'wynn.netherland@gmail.com']
15
+ gem.files = `git ls-files`.split("\n")
16
+ gem.homepage = 'https://github.com/pengwynn/faraday_middleware'
17
+ gem.name = 'faraday_middleware'
18
+ gem.require_paths = ['lib']
19
+ gem.required_rubygems_version = Gem::Requirement.new('>= 1.3.6')
20
+ gem.summary = gem.description
21
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
22
+ gem.version = FaradayMiddleware::VERSION
31
23
  end
@@ -1,15 +1,55 @@
1
1
  require 'faraday'
2
2
 
3
- class Faraday::Request
4
- autoload :OAuth, 'faraday/request/oauth'
5
- autoload :OAuth2, 'faraday/request/oauth2'
6
- end
3
+ module FaradayMiddleware
4
+ class << self
5
+ middleware = {
6
+ :OAuth => 'request/oauth',
7
+ :OAuth2 => 'request/oauth2',
8
+ :EncodeJson => 'request/encode_json',
9
+ :Mashify => 'response/mashify',
10
+ :Rashify => 'response/rashify',
11
+ :ParseJson => 'response/parse_json',
12
+ :ParseXml => 'response/parse_xml',
13
+ :ParseMarshal => 'response/parse_marshal',
14
+ :ParseYaml => 'response/parse_yaml',
15
+ :Caching => 'response/caching',
16
+ :RackCompatible => 'rack_compatible',
17
+ :FollowRedirects => 'response/follow_redirects',
18
+ :Instrumentation => 'instrumentation'
19
+ }
20
+
21
+ # autoload without the autoload
22
+ define_method(:const_missing) { |const|
23
+ if middleware.member? const
24
+ require "faraday_middleware/#{middleware[const]}"
25
+ raise NameError, "missing #{const} middleware" unless const_defined? const
26
+ const_get const
27
+ else
28
+ super
29
+ end
30
+ }
31
+ end
32
+
33
+ if Faraday.respond_to? :register_middleware
34
+ Faraday.register_middleware :request,
35
+ :oauth => lambda { OAuth },
36
+ :oauth2 => lambda { OAuth2 },
37
+ :json => lambda { EncodeJson }
7
38
 
8
- class Faraday::Response
9
- autoload :Mashify, 'faraday/response/mashify'
10
- autoload :ParseJson, 'faraday/response/parse_json'
11
- autoload :ParseMarshal, 'faraday/response/parse_marshal'
12
- autoload :ParseXml, 'faraday/response/parse_xml'
13
- autoload :ParseYaml, 'faraday/response/parse_yaml'
14
- autoload :Rashify, 'faraday/response/rashify'
39
+ Faraday.register_middleware :response,
40
+ :mashify => lambda { Mashify },
41
+ :rashify => lambda { Rashify },
42
+ :json => lambda { ParseJson },
43
+ :json_fix => lambda { ParseJson::MimeTypeFix },
44
+ :xml => lambda { ParseXml },
45
+ :marshal => lambda { ParseMarshal },
46
+ :yaml => lambda { ParseYaml },
47
+ :caching => lambda { Caching },
48
+ :follow_redirects => lambda { FollowRedirects }
49
+
50
+ Faraday.register_middleware \
51
+ :instrumentation => lambda { Instrumentation }
52
+ end
15
53
  end
54
+
55
+ require 'faraday_middleware/backwards_compatibility'
@@ -0,0 +1,20 @@
1
+ require 'addressable/uri'
2
+
3
+ # feature-detect the bug
4
+ unless Addressable::URI.parse('/?a=1&b=2') === '/?b=2&a=1'
5
+ # fix `normalized_query` by sorting query key-value pairs
6
+ # (rejected: https://github.com/sporkmonger/addressable/issues/28)
7
+ class Addressable::URI
8
+ alias normalized_query_without_ordering_fix normalized_query
9
+
10
+ def normalized_query
11
+ fresh = @normalized_query.nil?
12
+ query = normalized_query_without_ordering_fix
13
+ if query && fresh
14
+ @normalized_query = query.split('&', -1).sort_by {|q| q[0..(q.index('=')||-1)] }.join('&')
15
+ else
16
+ query
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,30 @@
1
+ deprecation_warning = lambda { |old, new, trace|
2
+ warn "Deprecation warning: #{old} is deprecated; use #{new}"
3
+ warn trace[0,10].join("\n") if $DEBUG
4
+ }
5
+
6
+ Faraday::Request.extend Module.new {
7
+ legacy = [:OAuth, :OAuth2]
8
+ define_method(:const_missing) { |const|
9
+ if legacy.include? const
10
+ klass = FaradayMiddleware.const_get(const)
11
+ deprecation_warning.call "Faraday::Request::#{const}", klass.name, caller
12
+ const_set const, klass
13
+ else
14
+ super
15
+ end
16
+ }
17
+ }
18
+
19
+ Faraday::Response.extend Module.new {
20
+ legacy = [:Mashify, :Rashify, :ParseJson, :ParseMarshal, :ParseXml, :ParseYaml]
21
+ define_method(:const_missing) { |const|
22
+ if legacy.include? const
23
+ klass = FaradayMiddleware.const_get(const)
24
+ deprecation_warning.call "Faraday::Response::#{const}", klass.name, caller
25
+ const_set const, klass
26
+ else
27
+ super
28
+ end
29
+ }
30
+ }
@@ -0,0 +1,30 @@
1
+ require 'faraday'
2
+
3
+ module FaradayMiddleware
4
+ # Public: Instruments requests using Active Support.
5
+ #
6
+ # Measures time spent only for synchronous requests.
7
+ #
8
+ # Examples
9
+ #
10
+ # ActiveSupport::Notifications.subscribe('request.faraday') do |name, start_time, end_time, _, env|
11
+ # url = env[:url]
12
+ # http_method = env[:method].to_s.upcase
13
+ # duration = end_time - start_time
14
+ # $stderr.puts '[%s] %s %s (%.3f s)' % [url.host, http_method, url.request_uri, duration]
15
+ # end
16
+ class Instrumentation < Faraday::Middleware
17
+ dependency 'active_support/notifications'
18
+
19
+ def initialize(app, options = {})
20
+ super(app)
21
+ @name = options.fetch(:name, 'request.faraday')
22
+ end
23
+
24
+ def call(env)
25
+ ActiveSupport::Notifications.instrument(@name, env) do
26
+ @app.call(env)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,76 @@
1
+ require 'stringio'
2
+
3
+ module FaradayMiddleware
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.respond_to?(:inferred_port) ? url.inferred_port : url.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,50 @@
1
+ require 'faraday'
2
+
3
+ module FaradayMiddleware
4
+ # Request middleware that encodes the body as JSON.
5
+ #
6
+ # Processes only requests with matching Content-type or those without a type.
7
+ # If a request doesn't have a type but has a body, it sets the Content-type
8
+ # to JSON MIME-type.
9
+ #
10
+ # Doesn't try to encode bodies that already are in string form.
11
+ class EncodeJson < Faraday::Middleware
12
+ CONTENT_TYPE = 'Content-Type'.freeze
13
+ MIME_TYPE = 'application/json'.freeze
14
+
15
+ dependency 'json'
16
+
17
+ def call(env)
18
+ match_content_type(env) do |data|
19
+ env[:body] = encode data
20
+ end
21
+ @app.call env
22
+ end
23
+
24
+ def encode(data)
25
+ JSON.dump data
26
+ end
27
+
28
+ def match_content_type(env)
29
+ if process_request?(env)
30
+ env[:request_headers][CONTENT_TYPE] ||= MIME_TYPE
31
+ yield env[:body] unless env[:body].respond_to?(:to_str)
32
+ end
33
+ end
34
+
35
+ def process_request?(env)
36
+ type = request_type(env)
37
+ has_body?(env) and (type.empty? or type == MIME_TYPE)
38
+ end
39
+
40
+ def has_body?(env)
41
+ body = env[:body] and !(body.respond_to?(:to_str) and body.empty?)
42
+ end
43
+
44
+ def request_type(env)
45
+ type = env[:request_headers][CONTENT_TYPE].to_s
46
+ type = type.split(';', 2).first if type.index(';')
47
+ type
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,61 @@
1
+ require 'faraday'
2
+
3
+ module FaradayMiddleware
4
+ # Public: Uses the simple_oauth library to sign requests according the
5
+ # OAuth protocol.
6
+ #
7
+ # The options for this middleware are forwarded to SimpleOAuth::Header:
8
+ # :consumer_key, :consumer_secret, :token, :token_secret. All these
9
+ # parameters are optional.
10
+ #
11
+ # The signature is added to the "Authorization" HTTP request header. If the
12
+ # value for this header already exists, it is not overriden.
13
+ #
14
+ # For requests that have parameters in the body, such as POST, this
15
+ # middleware expects them to be in Hash form, i.e. not encoded to string.
16
+ # This means this middleware has to be positioned on the stack before any
17
+ # encoding middleware such as UrlEncoded.
18
+ class OAuth < Faraday::Middleware
19
+ dependency 'simple_oauth'
20
+
21
+ AUTH_HEADER = 'Authorization'.freeze
22
+
23
+ def initialize(app, options)
24
+ super(app)
25
+ @options = options
26
+ end
27
+
28
+ def call(env)
29
+ env[:request_headers][AUTH_HEADER] ||= oauth_header(env).to_s if sign_request?(env)
30
+ @app.call(env)
31
+ end
32
+
33
+ def oauth_header(env)
34
+ SimpleOAuth::Header.new env[:method],
35
+ env[:url].to_s,
36
+ signature_params(body_params(env)),
37
+ oauth_options(env)
38
+ end
39
+
40
+ def sign_request?(env)
41
+ !!env[:request].fetch(:oauth, true)
42
+ end
43
+
44
+ def oauth_options(env)
45
+ if extra = env[:request][:oauth] and extra.is_a? Hash and !extra.empty?
46
+ @options.merge extra
47
+ else
48
+ @options
49
+ end
50
+ end
51
+
52
+ def body_params(env)
53
+ env[:body] || {}
54
+ end
55
+
56
+ def signature_params(params)
57
+ params.empty? ? params :
58
+ params.reject {|k,v| v.respond_to?(:content_type) }
59
+ end
60
+ end
61
+ end