Permalink
Browse files

Added single document query cache middleware.

  • Loading branch information...
1 parent 59a5351 commit 9940912bd202a40a7e3fcb75263ce673d7d530b4 @jnunemaker committed Aug 8, 2012
View
@@ -15,5 +15,6 @@ group(:test) do
gem 'timecop'
gem 'bson_ext'
gem 'mongo'
+ gem 'rack-test'
end
@@ -0,0 +1,36 @@
+module Flipper
+ module Middleware
+ class MongoSingleDocumentQueryCache
+ class Body
+ def initialize(target, adapter, original)
+ @target = target
+ @adapter = adapter
+ @original = original
+ end
+
+ def each(&block)
+ @target.each(&block)
+ end
+
+ def close
+ @target.close if @target.respond_to?(:close)
+ ensure
+ @adapter.document_cache = @original
+ end
+ end
+
+ def initialize(app, adapter)
+ @app = app
+ @adapter = adapter
+ end
+
+ def call(env)
+ original = @adapter.using_document_cache?
+ @adapter.document_cache = true
+
+ status, headers, body = @app.call(env)
+ [status, headers, Body.new(body, @adapter, original)]
+ end
+ end
+ end
+end
@@ -0,0 +1,121 @@
+require 'helper'
+require 'rack/test'
+require 'flipper/middleware/mongo_single_document_query_cache'
+
+describe Flipper::Middleware::MongoSingleDocumentQueryCache do
+ include Rack::Test::Methods
+
+ let(:collection) { Mongo::Connection.new.db('testing')['testing'] }
+ let(:adapter) { Flipper::Adapters::MongoSingleDocument.new(collection) }
+ let(:flipper) { Flipper.new(adapter) }
+
+ class Enum < Struct.new(:iter)
+ def each(&b)
+ iter.call(&b)
+ end
+ end
+
+ let(:app) {
+ # ensure scoped for builder block, annoying...
+ instance = adapter
+ middleware = described_class
+
+ Rack::Builder.new do
+ use middleware, instance
+
+ map "/" do
+ run lambda {|env| [200, {}, []] }
+ end
+
+ map "/fail" do
+ run lambda {|env| raise "FAIL!" }
+ end
+ end.to_app
+ }
+
+ it "delegates" do
+ called = false
+ app = lambda { |env|
+ called = true
+ [200, {}, nil]
+ }
+ middleware = described_class.new app, adapter
+ middleware.call({})
+ called.should be_true
+ end
+
+ it "enables document cache during delegation" do
+ app = lambda { |env|
+ adapter.using_document_cache?.should be_true
+ [200, {}, nil]
+ }
+ middleware = described_class.new app, adapter
+ middleware.call({})
+ end
+
+ it "enables document cache for body each" do
+ app = lambda { |env|
+ [200, {}, Enum.new(lambda { |&b|
+ adapter.using_document_cache?.should be_true
+ b.call "hello"
+ })]
+ }
+ middleware = described_class.new app, adapter
+ body = middleware.call({}).last
+ body.each { |x| x.should eql('hello') }
+ end
+
+ it "disables document cache after body close" do
+ app = lambda { |env| [200, {}, []] }
+ middleware = described_class.new app, adapter
+ body = middleware.call({}).last
+
+ adapter.using_document_cache?.should be_true
+ body.close
+ adapter.using_document_cache?.should be_false
+ end
+
+ it "clears document cache after body close" do
+ app = lambda { |env| [200, {}, []] }
+ middleware = described_class.new app, adapter
+ body = middleware.call({}).last
+ adapter.write('hello', 'world')
+
+ adapter.instance_variable_get("@document").should_not be_nil
+ body.close
+ adapter.instance_variable_get("@document").should be_nil
+ end
+
+ it "really does cache" do
+ flipper[:stats].enable
+
+ collection.should_receive(:find_one).once.and_return({})
+
+ app = lambda { |env|
+ flipper[:stats].enabled?
+ flipper[:stats].enabled?
+ flipper[:stats].enabled?
+ flipper[:stats].enabled?
+ flipper[:stats].enabled?
+ flipper[:stats].enabled?
+
+ [200, {}, []]
+ }
+ middleware = described_class.new app, adapter
+ middleware.call({})
+ end
+
+ context "with a successful request" do
+ it "clears the document cache" do
+ adapter.should_receive(:reset_document_cache).twice
+ get '/'
+ end
+ end
+
+ context "when the request raises an error" do
+ it "clears the document cache" do
+ adapter.should_receive(:reset_document_cache).once
+ get '/fail' rescue nil
+ end
+ end
+end

0 comments on commit 9940912

Please sign in to comment.