Permalink
Browse files

add Caching and Instrumentation middleware

  • Loading branch information...
1 parent cb85bb0 commit f88408f24ad8e02e6e96d76b4f5074bfdd201196 @mislav committed Mar 28, 2011
Showing with 136 additions and 1 deletion.
  1. +3 −1 lib/faraday_stack.rb
  2. +44 −0 lib/faraday_stack/caching.rb
  3. +20 −0 lib/faraday_stack/instrumentation.rb
  4. +69 −0 test/caching_test.rb
View
4 lib/faraday_stack.rb
@@ -9,7 +9,9 @@ module FaradayStack
:ResponseMiddleware => 'response_middleware',
:ResponseJSON => 'response_json',
:ResponseXML => 'response_xml',
- :ResponseHTML => 'response_html'
+ :ResponseHTML => 'response_html',
+ :Instrumentation => 'instrumentation',
+ :Caching => 'caching'
# THE ÜBER STACK
def self.default_connection
View
44 lib/faraday_stack/caching.rb
@@ -0,0 +1,44 @@
+module FaradayStack
+ class Caching < Faraday::Middleware
+ attr_reader :cache
+
+ def initialize(app, cache = nil)
+ super(app)
+ @cache = cache || Proc.new.call
+ 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)
+ env[:url].request_uri
+ 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.apply_request env unless response.env[:method]
+ response
+ end
+ end
+end
View
20 lib/faraday_stack/instrumentation.rb
@@ -0,0 +1,20 @@
+require 'active_support/notifications'
+
+module FaradayStack
+ # Measures request time only in synchronous request mode.
+ # Sample subscriber:
+ #
+ # ActiveSupport::Notifications.subscribe('request.faraday') do |name, start_time, end_time, _, env|
+ # url = env[:url]
+ # http_method = env[:method].to_s.upcase
+ # duration = end_time - start_time
+ # $stderr.puts '[%s] %s %s (%.3f s)' % [url.host, http_method, url.request_uri, duration]
+ # end
+ class Instrumentation < Faraday::Middleware
+ def call(env)
+ ActiveSupport::Notifications.instrument('request.faraday', env) do
+ @app.call(env)
+ end
+ end
+ end
+end
View
69 test/caching_test.rb
@@ -0,0 +1,69 @@
+require 'test_helper'
+require 'forwardable'
+
+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
+
+ 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 FaradayStack::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].to_s
+ 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

0 comments on commit f88408f

Please sign in to comment.