Skip to content

Commit

Permalink
API cache extracted from mloughran.com.
Browse files Browse the repository at this point in the history
Some major limitations right now:

* Only the :cache and :timeout options are implemented
* There is only one cache store and it's an in memory hash
  • Loading branch information
Martyn Loughran committed May 29, 2008
0 parents commit de636b0
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pkg
20 changes: 20 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
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.
3 changes: 3 additions & 0 deletions README
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
api_cache
=========

44 changes: 44 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
require 'rubygems'
require 'rake/gempackagetask'
require 'rubygems/specification'
require 'date'

PLUGIN = "api_cache"
NAME = "api_cache"
GEM_VERSION = "0.0.1"
AUTHOR = "Martyn Loughran"
EMAIL = "me@mloughran.com"
HOMEPAGE = "http://merb-plugins.rubyforge.org/api_cache/"
SUMMARY = "Library to handle caching external API calls"

spec = Gem::Specification.new do |s|
s.name = NAME
s.version = GEM_VERSION
s.platform = Gem::Platform::RUBY
s.has_rdoc = true
s.extra_rdoc_files = ["README", "LICENSE", 'TODO']
s.summary = SUMMARY
s.description = s.summary
s.author = AUTHOR
s.email = EMAIL
s.homepage = HOMEPAGE
s.require_path = 'lib'
s.autorequire = PLUGIN
s.files = %w(LICENSE README Rakefile TODO) + Dir.glob("{lib,spec}/**/*")
end

Rake::GemPackageTask.new(spec) do |pkg|
pkg.gem_spec = spec
end

desc "install the plugin locally"
task :install => [:package] do
sh %{sudo gem install pkg/#{NAME}-#{GEM_VERSION} --no-update-sources}
end

desc "create a gemspec file"
task :make_spec do
File.open("#{GEM}.gemspec", "w") do |file|
file.puts spec.to_ruby
end
end
Empty file added TODO
Empty file.
59 changes: 59 additions & 0 deletions lib/api_cache.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
class APICache
class Error < RuntimeError; end
class Invalid < RuntimeError; end

class << self
attr_accessor :cache
end

# Initializes the cache
def self.start
APICache.cache = APICache::MemoryStore.new
end

# Raises an APICache::Error if it can't get a value. You should rescue this.
#
# Optionally call with a block. The value of the block is then used to
# set the cache rather than calling the url. Use it for example if you need
# to make another type of request, catch custom error codes etc. To signal
# that the call failed just throw :invalid - the value will then not be
# cached and the api will not be called again for options[:timeout] seconds.
#
# For example:
# [Add example here]
def self.get(url, options = {}, &block)
options = {
:cache => 600, # 10 minutes After this time fetch new value
:valid => 86400, # 1 day Expire even if not fetched new data
:period => 60, # 1 minute Don't call API more frequently than this
:timeout => 5 # 5 seconds Timeout to wait for response
}.merge(options)

if cache.exists?(url) && !cache.expired?(url, options[:cache])
# Cache is populated and not expired
cache.get(url)
else
# Cache is not populated or is expired
begin
r = Timeout::timeout(options[:timeout]) do
if block_given?
# This should raise APICache::Invalid if it is not correct
yield
else
r = Net::HTTP.get_response(URI.parse(url)).body
# TODO: Check that it's a 200 response
end
end
cache.set(url, r)
rescue Timeout::Error, APICache::Invalid
if cache.exists?(url)
cache.get(url)
else
raise APICache::Error
end
end
end
end
end

require 'api_cache/memory_store'
31 changes: 31 additions & 0 deletions lib/api_cache/memory_store.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
class APICache::MemoryStore
def initialize
puts "Init cache"
@cache = {}
end

def expired?(name, timeout)
Time.now - created(name) > timeout
end

def set(name, value)
puts "Setting the cache"
@cache[name] = [Time.now, value]
value
end

def get(name)
puts "Serving from cache"
@cache[name][1]
end

def exists?(name)
!@cache[name].nil?
end

private

def created(name)
@cache[name][0]
end
end
7 changes: 7 additions & 0 deletions spec/api_cache_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
require File.dirname(__FILE__) + '/spec_helper'

describe "api_cache" do
it "should do nothing" do
true.should == true
end
end
2 changes: 2 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
$TESTING=true
$:.push File.join(File.dirname(__FILE__), '..', 'lib')

0 comments on commit de636b0

Please sign in to comment.