Skip to content

Instantly share code, notes, and snippets.

@karmi
Created April 19, 2009 11:22
Show Gist options
  • Save karmi/98030 to your computer and use it in GitHub Desktop.
Save karmi/98030 to your computer and use it in GitHub Desktop.
# = Caching for HTTParty
#
# First stab at implementing caching for HTTParty (http://github.com/jnunemaker/httparty/)
# Modeled after Martyn Loughran's APICache (http://github.com/newbamboo/api_cache/)
#
# = Usage
# 1. <tt>include Icebox</tt> in YourPartyAnimalClass
# 2. Use <tt>YourPartyAnimalClass.get_cached()</tt> instead of simple <tt>get()</tt>
require 'logger'
require 'ftools'
require 'pathname'
require 'digest/md5'
module Icebox
LOGGER = Logger.new(STDOUT) unless defined?(LOGGER)
def self.included(receiver)
receiver.extend ClassMethods
end
module ClassMethods
# Get cached API response, fresh from the ice-box,
# if it exists, and is still fresh.
# Otherwise, load from network and cache the response.
def get_cached(path, options={})
if cache.exists?(path) and not cache.stale?(path)
LOGGER.info "Getting data from cache"
cache.get(path)
else
LOGGER.info "Getting data from network"
value = get(path, options)
cache.set(path, value)
return value
end
end
# Cache object reader
# Pass some store (see below) to its constructor
def cache
@@cache ||= Cache.new(Icebox::FileStore)
end
end
# Basic caching for anything (get, set, exists?, stale?)
class Cache
attr_accessor :store
def initialize(store)
@store = store.send(:new)
end
def get(key)
@store.get( encode(key) )
end
def set(key, value)
@store.set( encode(key), value )
end
def exists?(key)
@store.exists?( encode(key) )
end
def stale?(key)
@store.stale?( encode(key) )
end
private
def encode(key)
Digest::MD5.hexdigest( key )
end
end
# Store cached values in memory
class MemoryStore
def initialize
LOGGER.info "Using memory store"
@cache = {}
@timeout = 600 # sec
true
end
def set(key, value)
LOGGER.info("Cache: set (#{key})")
@cache[key] = [Time.now, value]
true
end
def get(key)
data = @cache[key][1] rescue nil?
LOGGER.info("Cache: #{data.nil? ? "miss" : "hit"} (#{key})")
return data
end
def exists?(key)
!@cache[key].nil?
end
def stale?(key)
return true unless exists?(key)
Time.now - created(key) > @timeout
end
private
def created(key)
@cache[key][0]
end
end
# Store cached values on filesystem
class FileStore
def initialize(options={})
@timeout = options[:timeout] || 600 #sec
options[:path] ||= File.join( File.dirname(__FILE__), 'tmp', 'cache' )
@path = Pathname.new( options[:path] )
FileUtils.mkdir_p( @path )
@cache =
LOGGER.info "Using file store in '#{@path}'"
true
end
def set(key, value)
LOGGER.info("Cache: set (#{key})")
File.open( @path.join(key), 'w' ) { |file| file << value }
true
end
def get(key)
data = File.read( @path.join(key) ) rescue nil?
LOGGER.info("Cache: #{data.nil? ? "miss" : "hit"} (#{key})")
return data
end
def exists?(key)
File.exists?( @path.join(key) )
end
def stale?(key)
return true unless exists?(key)
Time.now - created(key) > @timeout
end
private
def created(key)
File.mtime( @path.join(key) )
end
end
end
# Major parts of this code are based on architecture of ApiCache
# http://github.com/newbamboo/api_cache/tree/master
#
# Copyright (c) 2008 Martyn Loughran
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
require 'rubygems'
require 'httparty'
require 'icebox'
class CachedRepresentative
include HTTParty
include Icebox
end
puts CachedRepresentative.get_cached('http://whoismyrepresentative.com/whoismyrep.php?zip=46544').inspect
puts CachedRepresentative.get_cached('http://whoismyrepresentative.com/whoismyrep.php?zip=46544').inspect
puts CachedRepresentative.get_cached('http://whoismyrepresentative.com/whoismyrep.php?zip=46544').inspect
puts CachedRepresentative.get_cached('http://whoismyrepresentative.com/whoismyrep.php?zip=46544').inspect
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment