Skip to content

Commit

Permalink
Merge pull request #130 from amatriain/nil-entity-store
Browse files Browse the repository at this point in the history
Added a new Dummy backend for Entity Store
  • Loading branch information
grosser committed Jan 27, 2016
2 parents d16e44d + 8e9cdb5 commit 37c43a7
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 2 deletions.
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,43 @@ use Rack::Cache,
run app
```

Using the Noop backend for the entity store
-------------------------------------------

The Noop backend can be used for the entity store if you're not interested in the bodies of cached
responses. This backend does not actually persist response bodies, so no memory or disk space has
to be allocated for them (the metastore backend still needs those resources).

Be careful: when using this backend, responses retrieved from the cache always have an empty body. You must
check if the response comes from the cache (by looking at the presence of 'fresh' or 'valid' in the
```X-Rack-Cache``` HTTP header) and in this case you should ignore the response body.

There are two ways of configuring rack-cache to use this backend:

```Ruby
require 'rack/cache'

use Rack::Cache,
:verbose => true,
:metastore => "file:/var/cache/rack/meta", # you can use any backend here
:entitystore => nil

run app
```

or

```Ruby
require 'rack/cache'

use Rack::Cache,
:verbose => true,
:metastore => "file:/var/cache/rack/meta", # you can use any backend here
:entitystore => "noop://"

run app
```

License: MIT<br/>
[![Build Status](https://travis-ci.org/rtomayko/rack-cache.png)](https://travis-ci.org/rtomayko/rack-cache)

39 changes: 39 additions & 0 deletions lib/rack/cache/entitystore.rb
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,45 @@ def self.resolve(uri)
GAECACHE = GAEStore
GAE = GAEStore

# Noop Entity Store backend that does not persist entities.
# When this entity store is used (by setting the entitystore option to nil or to a 'noop:' URL),
# response bodies will not be persisted. Responses retrieved from the cache will have an empty body.
#
# This backend is only useful for use cases in which the bodies of responses retrieved from the cache are of
# no interest. The code making the request MUST check if the response comes from the cache (e.g. looking at
# the X-Rack-Cache header in the response) and in this case ignore the response body.
#
# Using this backend means that no space must be allocated in disk or memory to store response bodies.
class Noop < EntityStore
def exist?(key)
true
end

def read(key)
''
end

def open(key)
[]
end

def write(body, ttl=nil)
key, size = slurp(body) { |part| part }
[key, size]
end

def purge(key)
nil
end

def self.resolve(uri)
new
end
end

NIL = Noop
NOOP = Noop

end

end
5 changes: 3 additions & 2 deletions lib/rack/cache/metastore.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ def store(request, response, entity_store)
end
response.headers['X-Content-Digest'] = digest
response.headers['Content-Length'] = size.to_s unless response.headers['Transfer-Encoding']
response.body = entity_store.open(digest) || response.body
# If the entitystore backend is a Noop, do not try to read the body from the backend, it always returns an empty array
response.body = entity_store.open(digest) || response.body unless entity_store.is_a? Rack::Cache::EntityStore::Noop
end

# read existing cache entries, remove non-varying, and add this one to
Expand Down Expand Up @@ -121,7 +122,7 @@ def persist_request(request)

# Converts a stored response hash into a Response object. The caller
# is responsible for loading and passing the body if needed.
def restore_response(hash, body=nil)
def restore_response(hash, body=[])
status = hash.delete('X-Status').to_i
Rack::Cache::Response.new(status, hash, body)
end
Expand Down
3 changes: 3 additions & 0 deletions lib/rack/cache/storage.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ def create_store(type, uri)
else
fail "Unknown storage provider: #{uri.to_s}"
end
elsif uri.nil?
# hack to support setting entitystore to nil, which doesn't persist request bodies
type.const_get(nil.inspect.upcase).resolve(uri)
else
# hack in support for passing a Dalli::Client or Memcached object
# as the storage URI.
Expand Down
65 changes: 65 additions & 0 deletions test/entitystore_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -272,5 +272,70 @@ def put(key, value, ttl = nil)
end
include RackCacheEntityStoreImplementation
end

end

describe 'Noop' do
before {@store = Rack::Cache::EntityStore::Noop.new}

it 'responds to all required messages' do
%w[read open write exist?].each do |message|
assert @store.respond_to? message
end
end

it 'accepts bodies with #write' do
key, size = @store.write(['My wild love went riding,'])
refute key.nil?
assert key.length == 40 && key =~ /^[0-9a-z]+$/
end

it 'takes a ttl parameter for #write' do
key, size = @store.write(['My wild love went riding,'], 0)
refute key.nil?
assert key.length == 40 && key =~ /^[0-9a-z]+$/
end

it 'always responds to #exist? with true, regardless of the content having been saved before' do
key, size = @store.write(['She rode to the devil,'])
assert @store.exist? key
assert @store.exist? '938jasddj83jasdh4438021ksdfjsdfjsdsf'
end

it 'always responds to #read with an empty String' do
key, size = @store.write(['And asked him to pay.'])
data = @store.read(key)
data.must_equal ''
data = @store.read('938jasddj83jasdh4438021ksdfjsdfjsdsf')
data.must_equal ''
end

it 'gives a 40 character SHA1 hex digest from #write' do
key, size = @store.write(['she rode to the sea;'])
refute key.nil?
key.length.must_equal 40
key.must_match /^[0-9a-z]+$/
key.must_equal '90a4c84d51a277f3dafc34693ca264531b9f51b6'
end

it 'always responds to #open with empty array' do
key, size = @store.write(['And asked him to pay.'])
data = @store.open(key)
data.must_equal []
data = @store.open('938jasddj83jasdh4438021ksdfjsdfjsdsf')
data.must_equal []
end

it 'returns a Rack compatible body from #open' do
key, size = @store.write(['Some shells for her hair.'])
body = @store.open(key)
assert body.respond_to? :each
body.must_equal []
end

it 'responds to #purge and returns nil' do
key, size = @store.write(['My wild love went riding,'])
@store.purge(key).must_equal nil
end
end
end

0 comments on commit 37c43a7

Please sign in to comment.