Skip to content

Commit

Permalink
Added Cache Parser Strategy. Fix #821
Browse files Browse the repository at this point in the history
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 18, 2013
1 parent 2770809 commit 24543be
Show file tree
Hide file tree
Showing 13 changed files with 171 additions and 107 deletions.
7 changes: 5 additions & 2 deletions padrino-cache/lib/padrino-cache.rb
Expand Up @@ -43,7 +43,7 @@ def cache
# #
# @api public # @api public
def cache=(value) def cache=(value)
@_cache = value @_cache= value
end end
end # self end # self


Expand All @@ -54,7 +54,8 @@ def cache=(value)
# of your choosing. Several common caching stores are supported out of the box. # of your choosing. Several common caching stores are supported out of the box.
# #
module Cache module Cache
autoload :Store, 'padrino-cache/store' autoload :Store, 'padrino-cache/store'
autoload :Parser, 'padrino-cache/parser'


class << self class << self
## ##
Expand Down Expand Up @@ -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) Padrino::Cache::Helpers::Page.padrino_route_added(route, verb, path, args, options, block)
end end
end end

Padrino.cache = Store::Memory.new(50)
end # Cache end # Cache
end # Padrino end # Padrino
14 changes: 8 additions & 6 deletions padrino-cache/lib/padrino-cache/helpers/page.rb
Expand Up @@ -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 logger.debug "GET Cache", began_at, @route.cache_key || env['PATH_INFO'] if defined?(logger) && value


if value if value
content_type(value[:content_type]) if value[:content_type] # content_type(value[:content_type]) if value[:content_type]
halt 200, value[:response_buffer].to_s if value[:response_buffer] halt 200, value
end end
end end
end end
Expand All @@ -105,17 +105,19 @@ def self.padrino_route_added(route, verb, path, args, options, block) # @private
if settings.caching? && @_response_buffer.kind_of?(String) if settings.caching? && @_response_buffer.kind_of?(String)
began_at = Time.now began_at = Time.now


content = { # content = {
:response_buffer => @_response_buffer, # :response_buffer => @_response_buffer,
:content_type => @_content_type # :content_type => @_content_type
} # }
content = @_response_buffer


if @_last_expires_in if @_last_expires_in
settings.cache.set(@route.cache_key || env['PATH_INFO'], content, :expires_in => @_last_expires_in) settings.cache.set(@route.cache_key || env['PATH_INFO'], content, :expires_in => @_last_expires_in)
@_last_expires_in = nil @_last_expires_in = nil
else else
settings.cache.set(@route.cache_key || env['PATH_INFO'], content) settings.cache.set(@route.cache_key || env['PATH_INFO'], content)
end end

logger.debug "SET Cache", began_at, @route.cache_key || env['PATH_INFO'] if defined?(logger) logger.debug "SET Cache", began_at, @route.cache_key || env['PATH_INFO'] if defined?(logger)
end end
end end
Expand Down
35 changes: 8 additions & 27 deletions padrino-cache/lib/padrino-cache/parser.rb
Expand Up @@ -5,48 +5,29 @@ module Cache
# Define a padrino parser for our cache # Define a padrino parser for our cache
# #
module Parser 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 # With Parser::Plain we will store
# text and object in a text format # text and object in a text format
# #
module Plain module Plain
def decode(code) def self.decode(code)
code code.to_s
end end


def encode(code) def self.encode(code)
code code.to_s
end end
end end


module Marshal module Marshal
def decode(code) def self.decode(code)
Marshal.load(code) ::Marshal.load(code.to_s)
end end


def encode(code) def self.encode(code)
Marshal.dump(code) ::Marshal.dump(code)
end end
end end

# Let's set Plain as default encoder
set Plain
end # Parser end # Parser
end # Cache end # Cache
end # Padrino end # Padrino
1 change: 1 addition & 0 deletions padrino-cache/lib/padrino-cache/store.rb
Expand Up @@ -7,6 +7,7 @@ module Store
# The defined duration for the expiration edge. # The defined duration for the expiration edge.
EXPIRES_EDGE = 86400 EXPIRES_EDGE = 86400


autoload :Base, 'padrino-cache/store/base'
autoload :File, 'padrino-cache/store/file' autoload :File, 'padrino-cache/store/file'
autoload :Memcache, 'padrino-cache/store/memcache' autoload :Memcache, 'padrino-cache/store/memcache'
autoload :Memory, 'padrino-cache/store/memory' autoload :Memory, 'padrino-cache/store/memory'
Expand Down
63 changes: 63 additions & 0 deletions padrino-cache/lib/padrino-cache/store/base.rb
@@ -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

11 changes: 7 additions & 4 deletions padrino-cache/lib/padrino-cache/store/file.rb
Expand Up @@ -4,7 +4,7 @@ module Store
## ##
# File based Cache Store # File based Cache Store
# #
class File class File < Base
## ##
# Initialize File store with File root # Initialize File store with File root
# #
Expand All @@ -15,10 +15,13 @@ class File
# Padrino.cache = Padrino::Cache::Store::File.new("path/to") # Padrino.cache = Padrino::Cache::Store::File.new("path/to")
# # or from your app # # or from your app
# set :cache, Padrino::Cache::Store::File.new("path/to") # 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 # @api public
def initialize(root) def initialize(root, options={})
@root = root @root = root
super(options)
end end


## ##
Expand All @@ -40,7 +43,7 @@ def get(key)
expires_in, body = contents.split("\n", 2) expires_in, body = contents.split("\n", 2)
expires_in = expires_in.to_i expires_in = expires_in.to_i
if expires_in == -1 or Time.new.to_i < expires_in 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 else # expire the key
delete(key) delete(key)
nil nil
Expand Down Expand Up @@ -72,7 +75,7 @@ def set(key, value, opts = nil)
else else
expires_in = -1 expires_in = -1
end 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 ::File.open(path_for_key(key), 'wb') { |f| f << expires_in.to_s << "\n" << value } if value
end end


Expand Down
7 changes: 3 additions & 4 deletions padrino-cache/lib/padrino-cache/store/memcache.rb
Expand Up @@ -4,7 +4,7 @@ module Store
## ##
# Memcache Cache Store # Memcache Cache Store
# #
class Memcache class Memcache < Base
## ##
# Initialize Memcache store with client connection. # Initialize Memcache store with client connection.
# #
Expand All @@ -19,10 +19,9 @@ class Memcache
# set :cache, Padrino::Cache::Store::Memcache.new(::Memcached.new('127.0.0.1:11211', :exception_retry_limit => 1)) # set :cache, Padrino::Cache::Store::Memcache.new(::Memcached.new('127.0.0.1:11211', :exception_retry_limit => 1))
# #
# @api public # @api public
def initialize(client) def initialize(client, options={})
@backend = client @backend = client
rescue super(options)
raise
end end


## ##
Expand Down
5 changes: 3 additions & 2 deletions padrino-cache/lib/padrino-cache/store/memory.rb
Expand Up @@ -4,7 +4,7 @@ module Store
## ##
# Memory Cache Store # Memory Cache Store
# #
class Memory class Memory < Base
## ##
# Initialize Memory Store with memory size # Initialize Memory Store with memory size
# #
Expand All @@ -17,8 +17,9 @@ class Memory
# set :cache, Padrino::Cache::Store::Memory.new(10000) # set :cache, Padrino::Cache::Store::Memory.new(10000)
# #
# @api public # @api public
def initialize(size = 5000) def initialize(size = 5000, options={})
@size, @entries, @index = size, [], {} @size, @entries, @index = size, [], {}
super(options)
end end


## ##
Expand Down
16 changes: 10 additions & 6 deletions padrino-cache/lib/padrino-cache/store/mongo.rb
Expand Up @@ -4,7 +4,7 @@ module Store
## ##
# MongoDB Cache Store # MongoDB Cache Store
# #
class Mongo class Mongo < Base
## ##
# Initialize Mongo store with client connection and optional username and password. # Initialize Mongo store with client connection and optional username and password.
# #
Expand All @@ -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') # 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 # # 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') # 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 # @api public
def initialize(client, opts={}) def initialize(client, options={})
@client = client @client = client
@options = { @options = {
:capped => true, :capped => true,
:collection => 'cache', :collection => 'cache',
:size => 64, :size => 64,
:max => 100 :max => 100
}.merge(opts) }.merge(options)


if @options[:username] && @options[:password] if @options[:username] && @options[:password]
@client.authenticate(@options[:username], @options[:password], true) @client.authenticate(@options[:username], @options[:password], true)
end end
@backend = get_collection @backend = get_collection
super(options)
end end


## ##
Expand All @@ -47,7 +50,7 @@ def initialize(client, opts={})
def get(key) def get(key)
doc = @backend.find_one(:_id => key, :expires_in => {'$gt' => Time.now.utc}) doc = @backend.find_one(:_id => key, :expires_in => {'$gt' => Time.now.utc})
return nil if doc.nil? return nil if doc.nil?
Marshal.load(doc['value'].to_s) if doc['value'].present? parser.decode(doc['value'].to_s)
end end


## ##
Expand All @@ -66,7 +69,7 @@ def get(key)
# @api public # @api public
def set(key, value, opts = nil) def set(key, value, opts = nil)
key = key.to_s 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] if opts && opts[:expires_in]
expires_in = opts[:expires_in].to_i expires_in = opts[:expires_in].to_i
expires_in = Time.now.utc + expires_in if expires_in < EXPIRES_EDGE expires_in = Time.now.utc + expires_in if expires_in < EXPIRES_EDGE
Expand All @@ -76,7 +79,8 @@ def set(key, value, opts = nil)
@backend.update( @backend.update(
{:_id => key}, {:_id => key},
{:_id => key, :value => value, :expires_in => expires_in }, {:_id => key, :value => value, :expires_in => expires_in },
{:upsert => true}) {:upsert => true}
)
end end


## ##
Expand Down
12 changes: 8 additions & 4 deletions padrino-cache/lib/padrino-cache/store/redis.rb
Expand Up @@ -4,7 +4,7 @@ module Store
## ##
# Redis Cache Store # Redis Cache Store
# #
class Redis class Redis < Base
## ##
# Initialize Redis store with client connection. # Initialize Redis store with client connection.
# #
Expand All @@ -15,10 +15,13 @@ class Redis
# Padrino.cache = Padrino::Cache::Store::Redis.new(::Redis.new(:host => '127.0.0.1', :port => 6379, :db => 0)) # Padrino.cache = Padrino::Cache::Store::Redis.new(::Redis.new(:host => '127.0.0.1', :port => 6379, :db => 0))
# # or from your app # # or from your app
# set :cache, Padrino::Cache::Store::Redis.new(::Redis.new(:host => '127.0.0.1', :port => 6379, :db => 0)) # 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 # @api public
def initialize(client) def initialize(client, options={})
@backend = client @backend = client
super(options)
end end


## ##
Expand All @@ -34,7 +37,8 @@ def initialize(client)
# @api public # @api public
def get(key) def get(key)
code = @backend.get(key) code = @backend.get(key)
Parser.decode(code) return nil unless code
parser.decode(code)
end end


## ##
Expand All @@ -52,7 +56,7 @@ def get(key)
# #
# @api public # @api public
def set(key, value, opts = nil) def set(key, value, opts = nil)
value = Parser.encode(value) value = parser.encode(value)
if opts && opts[:expires_in] if opts && opts[:expires_in]
expires_in = opts[:expires_in].to_i expires_in = opts[:expires_in].to_i
expires_in = expires_in if expires_in < EXPIRES_EDGE expires_in = expires_in if expires_in < EXPIRES_EDGE
Expand Down

0 comments on commit 24543be

Please sign in to comment.