Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Google AppEngine memcache entity store and metastore implementations

To use GAE's memcache with rack-cache, set the :metastore and
:entitystore options as follows:

    use Rack::Cache,
      :metastore   => 'gae://cache-meta',
      :entitystore => 'gae://cache-body'

The 'cache-meta' and 'cache-body' parts are memcache namespace
prefixes and should be set to different values.
  • Loading branch information...
commit 9afdb945f72396424bc1e4c9954ac7797a7cab07 1 parent 42f7266
Alexander A. Portnov authored rtomayko committed
View
52 lib/rack/cache/appengine.rb
@@ -0,0 +1,52 @@
+require 'base64'
+
+module Rack::Cache::AppEngine
+
+ module MC
+ require 'java'
+
+ import com.google.appengine.api.memcache.Expiration;
+ import com.google.appengine.api.memcache.MemcacheService;
+ import com.google.appengine.api.memcache.MemcacheServiceFactory;
+ import com.google.appengine.api.memcache.Stats;
+
+ Service = MemcacheServiceFactory.getMemcacheService
+ end unless defined?(Rack::Cache::AppEngine::MC)
+
+ class MemCache
+
+ def initialize(options = {})
+ @cache = MC::Service
+ @cache.namespace = options[:namespace] if options[:namespace]
+ end
+
+ def contains?(key)
+ MC::Service.contains(key)
+ end
+
+ def get(key)
+ value = MC::Service.get(key)
+ Marshal.load(Base64.decode64(value)) if value
+ end
+
+ def put(key, value, ttl = nil)
+ expiration = ttl ? MC::Expiration.byDeltaSeconds(ttl) : nil
+ value = Base64.encode64(Marshal.dump(value)).gsub(/\n/, '')
+ MC::Service.put(key, value, expiration)
+ end
+
+ def namespace
+ MC::Service.getNamespace
+ end
+
+ def namespace=(value)
+ MC::Service.setNamespace(value.to_s)
+ end
+
+ def delete(key)
+ MC::Service.delete(key)
+ end
+
+ end
+
+end
View
46 lib/rack/cache/entitystore.rb
@@ -282,6 +282,52 @@ def purge(key)
end
MEMCACHED = MEMCACHE
+
+ class GAEStore < EntityStore
+ attr_reader :cache
+
+ def initialize(options = {})
+ require 'rack/cache/appengine'
+ @cache = Rack::Cache::AppEngine::MemCache.new(options)
+ end
+
+ def exist?(key)
+ cache.contains?(key)
+ end
+
+ def read(key)
+ cache.get(key)
+ end
+
+ def open(key)
+ if data = read(key)
+ [data]
+ else
+ nil
+ end
+ end
+
+ def write(body)
+ buf = StringIO.new
+ key, size = slurp(body){|part| buf.write(part) }
+ cache.put(key, buf.string)
+ [key, size]
+ end
+
+ def purge(key)
+ cache.delete(key)
+ nil
+ end
+
+ def self.resolve(uri)
+ self.new(:namespace => uri.host)
+ end
+
+ end
+
+ GAECACHE = GAEStore
+ GAE = GAEStore
+
end
end
View
34 lib/rack/cache/metastore.rb
@@ -360,6 +360,40 @@ def purge(key)
MemCache
end
MEMCACHED = MemCache
+
+ class GAEStore < MetaStore
+ attr_reader :cache
+
+ def initialize(options = {})
+ require 'rack/cache/appengine'
+ @cache = Rack::Cache::AppEngine::MemCache.new(options)
+ end
+
+ def read(key)
+ key = hexdigest(key)
+ cache.get(key) || []
+ end
+
+ def write(key, entries)
+ key = hexdigest(key)
+ cache.put(key, entries)
+ end
+
+ def purge(key)
+ key = hexdigest(key)
+ cache.delete(key)
+ nil
+ end
+
+ def self.resolve(uri)
+ self.new(:namespace => uri.host)
+ end
+
+ end
+
+ GAECACHE = GAEStore
+ GAE = GAEStore
+
end
end
View
3  rack-cache.gemspec
@@ -4,7 +4,7 @@ Gem::Specification.new do |s|
s.name = 'rack-cache'
s.version = '0.4'
- s.date = '2009-03-16'
+ s.date = '2009-04-28'
s.description = "HTTP Caching for Rack"
s.summary = "HTTP Caching for Rack"
@@ -30,6 +30,7 @@ Gem::Specification.new do |s|
example/sinatra/app.rb
example/sinatra/views/index.erb
lib/rack/cache.rb
+ lib/rack/cache/appengine.rb
lib/rack/cache/cachecontrol.rb
lib/rack/cache/context.rb
lib/rack/cache/entitystore.rb
View
27 test/entitystore_test.rb
@@ -200,4 +200,31 @@ def sha_like?
end
end
end
+
+ need_java 'entity store testing' do
+ module Rack::Cache::AppEngine
+ module MC
+ class << (Service = {})
+
+ def contains(key); include?(key); end
+ def get(key); self[key]; end;
+ def put(key, value, ttl = nil)
+ self[key] = value
+ end
+ end
+
+ end
+ end
+
+ describe 'GAEStore' do
+ it_should_behave_like 'A Rack::Cache::EntityStore Implementation'
+ before do
+ puts Rack::Cache::AppEngine::MC::Service.inspect
+ @store = Rack::Cache::EntityStore::GAEStore.new
+ end
+ after do
+ @store = nil
+ end
+ end
+ end
end
View
27 test/metastore_test.rb
@@ -272,4 +272,31 @@ def self.call(request); request.path_info.reverse end
end
end
end
+
+ need_java 'entity store testing' do
+ module Rack::Cache::AppEngine
+ module MC
+ class << (Service = {})
+
+ def contains(key); include?(key); end
+ def get(key); self[key]; end;
+ def put(key, value, ttl = nil)
+ self[key] = value
+ end
+
+ end
+ end
+ end
+
+ describe 'GAEStore' do
+ it_should_behave_like 'A Rack::Cache::MetaStore Implementation'
+ before :each do
+ Rack::Cache::AppEngine::MC::Service.clear
+ @store = Rack::Cache::MetaStore::GAEStore.new
+ @entity_store = Rack::Cache::EntityStore::Heap.new
+ end
+ end
+
+ end
+
end
View
10 test/spec_setup.rb
@@ -69,6 +69,16 @@ def need_memcache(forwhat)
end
end
+def need_java(forwhat)
+
+ if RUBY_PLATFORM =~ /java/
+ yield
+ else
+ STDERR.puts "skipping app engine #{forwhat}"
+ end
+end
+
+
# Setup the load path ..
$LOAD_PATH.unshift File.dirname(File.dirname(__FILE__)) + '/lib'
$LOAD_PATH.unshift File.dirname(__FILE__)
Please sign in to comment.
Something went wrong with that request. Please try again.