Permalink
Browse files

Added Cache Parser Strategy. Fix #821

Now different than before the we store data as plain text, otherwise
we bundle also the marshal serializer or you can simply provide yours.

Example:

    Padrino.cache = Padrino::Cache::File.new(...)
    Padrino.cache.parser # => Padrino::Cache::Parser:Plain
    Padrino.cache.parser = :marshal

Or you can write your own:

    require 'oj'
    module PadrinoJSON
      def self.encode(code)
        Oj.dump(code)
      end

      def self.decode(code)
        Oj.load(code)
      end
    end

Finally you can load your strategy:

    Padrino.cache.parser = PadrinoJSON

Or if you prefer do something on the fight you can:

    Padrino.cache.parser = Module.new do
      def self.encode
      end
      def self.decode
      end
    end

//cc @padrino/core-members
  • Loading branch information...
DAddYE authored and nesquena committed Mar 17, 2013
1 parent 2770809 commit 24543be6d34899c76eb6f36f62d5bc2dc7843c35
@@ -43,7 +43,7 @@ def cache
#
# @api public
def cache=(value)
@_cache = value
@_cache= value
end
end # self
@@ -54,7 +54,8 @@ def cache=(value)
# of your choosing. Several common caching stores are supported out of the box.
#
module Cache
autoload :Store, 'padrino-cache/store'
autoload :Store, 'padrino-cache/store'
autoload :Parser, 'padrino-cache/parser'
class << self
##
@@ -101,5 +102,7 @@ def padrino_route_added(route, verb, path, args, options, block) # @private
Padrino::Cache::Helpers::Page.padrino_route_added(route, verb, path, args, options, block)
end
end
Padrino.cache = Store::Memory.new(50)
end # Cache
end # Padrino
@@ -95,8 +95,8 @@ def self.padrino_route_added(route, verb, path, args, options, block) # @private
logger.debug "GET Cache", began_at, @route.cache_key || env['PATH_INFO'] if defined?(logger) && value
if value
content_type(value[:content_type]) if value[:content_type]
halt 200, value[:response_buffer].to_s if value[:response_buffer]
# content_type(value[:content_type]) if value[:content_type]
halt 200, value
end
end
end
@@ -105,17 +105,19 @@ def self.padrino_route_added(route, verb, path, args, options, block) # @private
if settings.caching? && @_response_buffer.kind_of?(String)
began_at = Time.now
content = {
:response_buffer => @_response_buffer,
:content_type => @_content_type
}
# content = {
# :response_buffer => @_response_buffer,
# :content_type => @_content_type
# }
content = @_response_buffer
if @_last_expires_in
settings.cache.set(@route.cache_key || env['PATH_INFO'], content, :expires_in => @_last_expires_in)
@_last_expires_in = nil
else
settings.cache.set(@route.cache_key || env['PATH_INFO'], content)
end
logger.debug "SET Cache", began_at, @route.cache_key || env['PATH_INFO'] if defined?(logger)
end
end
@@ -5,48 +5,29 @@ module Cache
# Define a padrino parser for our cache
#
module Parser
extend self
def set(mod)
raise "#{mod} should respond to encode" unless mod.method_defined?(:encode)
raise "#{mod} should respond to decode" unless mod.method_defined?(:decode)
@_parser = mod
end
def decode(code)
@_parser.decode(code)
end
def encode(code)
@_parser.encode(code)
end
##
# With Parser::Plain we will store
# text and object in a text format
#
module Plain
def decode(code)
code
def self.decode(code)
code.to_s
end
def encode(code)
code
def self.encode(code)
code.to_s
end
end
module Marshal
def decode(code)
Marshal.load(code)
def self.decode(code)
::Marshal.load(code.to_s)
end
def encode(code)
Marshal.dump(code)
def self.encode(code)
::Marshal.dump(code)
end
end
# Let's set Plain as default encoder
set Plain
end # Parser
end # Cache
end # Padrino
@@ -7,6 +7,7 @@ module Store
# The defined duration for the expiration edge.
EXPIRES_EDGE = 86400
autoload :Base, 'padrino-cache/store/base'
autoload :File, 'padrino-cache/store/file'
autoload :Memcache, 'padrino-cache/store/memcache'
autoload :Memory, 'padrino-cache/store/memory'
@@ -0,0 +1,63 @@
module Padrino
module Cache
module Store
##
# Abstract Cache Store
#
class Base
##
# Get the cache parser strategy
#
# By default is plain, otherwise you can set **Marshal** or write your own.
#
def parser
@_parser
end
##
# Set the caching parser strategy
#
# @param value
# Module of Padrino::Cache::Parser or any that respond to encode/decode
#
# @example
# Padrino.cache.parser = :plain
# Padrino.cache.parser = :marshal
# # shortcuts for:
# Padrino.cache.parser = Padrino::Cache::Parser::Plain
# Padrino.cache.parser = Padrino::Cache::Parser::Marshal
#
# You can easily write your own:
#
# @example
# require 'oj'
# module FastJSONParser
# def self.encode(value)
# OJ.dump(value)
# end
#
# def self.decode(value)
# Oj.load(value)
# end
# end
#
# Padrino.cache_parser = FastJSONParser
#
def parser=(mod)
mod = Padrino::Cache::Parser.const_get(mod.to_s.camelize) unless mod.is_a?(Module)
raise "#{mod} should respond to encode" unless mod.respond_to?(:encode)
raise "#{mod} should respond to decode" unless mod.respond_to?(:decode)
@_parser=mod
end
# @private
def initialize(options={})
self.parser = options[:parser] || :plain
end
end # Base
end # Store
end # Cache
end # Padrino
@@ -4,7 +4,7 @@ module Store
##
# File based Cache Store
#
class File
class File < Base
##
# Initialize File store with File root
#
@@ -15,10 +15,13 @@ class File
# Padrino.cache = Padrino::Cache::Store::File.new("path/to")
# # or from your app
# set :cache, Padrino::Cache::Store::File.new("path/to")
# # you can provide a marshal parser (to store ruby objects)
# set :cache, Padrino::Cache::Store::File.new("path/to", :parser => :marshal)
#
# @api public
def initialize(root)
def initialize(root, options={})
@root = root
super(options)
end
##
@@ -40,7 +43,7 @@ def get(key)
expires_in, body = contents.split("\n", 2)
expires_in = expires_in.to_i
if expires_in == -1 or Time.new.to_i < expires_in
Marshal.load(body) if body
parser.decode(body) if body
else # expire the key
delete(key)
nil
@@ -72,7 +75,7 @@ def set(key, value, opts = nil)
else
expires_in = -1
end
value = Marshal.dump(value) if value
value = parser.encode(value) if value
::File.open(path_for_key(key), 'wb') { |f| f << expires_in.to_s << "\n" << value } if value
end
@@ -4,7 +4,7 @@ module Store
##
# Memcache Cache Store
#
class Memcache
class Memcache < Base
##
# Initialize Memcache store with client connection.
#
@@ -19,10 +19,9 @@ class Memcache
# set :cache, Padrino::Cache::Store::Memcache.new(::Memcached.new('127.0.0.1:11211', :exception_retry_limit => 1))
#
# @api public
def initialize(client)
def initialize(client, options={})
@backend = client
rescue
raise
super(options)
end
##
@@ -4,7 +4,7 @@ module Store
##
# Memory Cache Store
#
class Memory
class Memory < Base
##
# Initialize Memory Store with memory size
#
@@ -17,8 +17,9 @@ class Memory
# set :cache, Padrino::Cache::Store::Memory.new(10000)
#
# @api public
def initialize(size = 5000)
def initialize(size = 5000, options={})
@size, @entries, @index = size, [], {}
super(options)
end
##
@@ -4,7 +4,7 @@ module Store
##
# MongoDB Cache Store
#
class Mongo
class Mongo < Base
##
# Initialize Mongo store with client connection and optional username and password.
#
@@ -17,21 +17,24 @@ class Mongo
# Padrino.cache = Padrino::Cache::Store::Mongo.new(::Mongo::Connection.new('127.0.0.1', 27017).db('padrino'), :username => 'username', :password => 'password', :size => 64, :max => 100, :collection => 'cache')
# # or from your app
# set :cache, Padrino::Cache::Store::Mongo.new(::Mongo::Connection.new('127.0.0.1', 27017).db('padrino'), :username => 'username', :password => 'password', :size => 64, :max => 100, :collection => 'cache')
# # you can provide a marshal parser (to store ruby objects)
# set :cache, Padrino::Cache::Store::Mongo.new(::Mongo::Connection.new('127.0.0.1', 27017).db('padrino'), :parser => :marshal)
#
# @api public
def initialize(client, opts={})
def initialize(client, options={})
@client = client
@options = {
:capped => true,
:collection => 'cache',
:size => 64,
:max => 100
}.merge(opts)
}.merge(options)
if @options[:username] && @options[:password]
@client.authenticate(@options[:username], @options[:password], true)
end
@backend = get_collection
super(options)
end
##
@@ -47,7 +50,7 @@ def initialize(client, opts={})
def get(key)
doc = @backend.find_one(:_id => key, :expires_in => {'$gt' => Time.now.utc})
return nil if doc.nil?
Marshal.load(doc['value'].to_s) if doc['value'].present?
parser.decode(doc['value'].to_s)
end
##
@@ -66,7 +69,7 @@ def get(key)
# @api public
def set(key, value, opts = nil)
key = key.to_s
value = BSON::Binary.new(Marshal.dump(value)) if value
value = BSON::Binary.new(parser.encode(value)) if value
if opts && opts[:expires_in]
expires_in = opts[:expires_in].to_i
expires_in = Time.now.utc + expires_in if expires_in < EXPIRES_EDGE
@@ -76,7 +79,8 @@ def set(key, value, opts = nil)
@backend.update(
{:_id => key},
{:_id => key, :value => value, :expires_in => expires_in },
{:upsert => true})
{:upsert => true}
)
end
##
@@ -4,7 +4,7 @@ module Store
##
# Redis Cache Store
#
class Redis
class Redis < Base
##
# Initialize Redis store with client connection.
#
@@ -15,10 +15,13 @@ class Redis
# Padrino.cache = Padrino::Cache::Store::Redis.new(::Redis.new(:host => '127.0.0.1', :port => 6379, :db => 0))
# # or from your app
# set :cache, Padrino::Cache::Store::Redis.new(::Redis.new(:host => '127.0.0.1', :port => 6379, :db => 0))
# # you can provide a marshal parser (to store ruby objects)
# set :cache, Padrino::Cache::Store::Redis.new(::Redis.new(:host => '127.0.0.1', :port => 6379, :db => 0), :parser => :marshal)
#
# @api public
def initialize(client)
def initialize(client, options={})
@backend = client
super(options)
end
##
@@ -34,7 +37,8 @@ def initialize(client)
# @api public
def get(key)
code = @backend.get(key)
Parser.decode(code)
return nil unless code
parser.decode(code)
end
##
@@ -52,7 +56,7 @@ def get(key)
#
# @api public
def set(key, value, opts = nil)
value = Parser.encode(value)
value = parser.encode(value)
if opts && opts[:expires_in]
expires_in = opts[:expires_in].to_i
expires_in = expires_in if expires_in < EXPIRES_EDGE
Oops, something went wrong.

0 comments on commit 24543be

Please sign in to comment.