faraday_middleware 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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