From 2da7dfe888d34884b8168b2c9f449f555d38c77b Mon Sep 17 00:00:00 2001
From: Alex Reisner <alex@alexreisner.com>
Date: Tue, 15 Mar 2011 22:47:50 -0400
Subject: [PATCH] Refactor: add Geocoder::Cache class.

This allows us to add a method for expiring cache keys and generally
gives us more control over the cache interface.
---
 lib/geocoder.rb              |  1 +
 lib/geocoder/cache.rb        | 81 ++++++++++++++++++++++++++++++++++++
 lib/geocoder/lookups/base.rb | 27 +++---------
 3 files changed, 88 insertions(+), 21 deletions(-)
 create mode 100644 lib/geocoder/cache.rb

diff --git a/lib/geocoder.rb b/lib/geocoder.rb
index 22f14105..594f3df8 100644
--- a/lib/geocoder.rb
+++ b/lib/geocoder.rb
@@ -1,5 +1,6 @@
 require "geocoder/configuration"
 require "geocoder/calculations"
+require "geocoder/cache"
 require "geocoder/railtie"
 require "geocoder/request"
 
diff --git a/lib/geocoder/cache.rb b/lib/geocoder/cache.rb
new file mode 100644
index 00000000..82d47d09
--- /dev/null
+++ b/lib/geocoder/cache.rb
@@ -0,0 +1,81 @@
+module Geocoder
+
+  ##
+  # The working Cache object, or +nil+ if none configured.
+  #
+  def self.cache
+    if @cache.nil? and store = Geocoder::Configuration.cache
+      @cache = Cache.new(store, Geocoder::Configuration.cache_prefix)
+    end
+    @cache
+  end
+
+  class Cache
+
+    def initialize(store, prefix)
+      @store = store
+      @prefix = prefix
+    end
+
+    ##
+    # Read from the Cache.
+    #
+    def [](url)
+      interpret store[key_for(url)]
+    end
+
+    ##
+    # Write to the Cache.
+    #
+    def []=(url, value)
+      store[key_for(url)] = value
+    end
+
+    ##
+    # Expire cache entry for given URL,
+    # or pass <tt>:all</tt> to expire everything.
+    #
+    def expire(url)
+      if url == :all
+        urls.each{ |u| expire(u) }
+      else
+        self[url] = nil
+      end
+    end
+
+
+    private # ----------------------------------------------------------------
+
+    attr_reader :prefix, :store
+
+    ##
+    # Cache key for a given URL.
+    #
+    def key_for(url)
+      [prefix, url].join
+    end
+
+    ##
+    # Array of keys with the currently configured prefix
+    # that have non-nil values.
+    #
+    def keys
+      store.keys.select{ |k| k.match /^#{prefix}/ and interpret(store[k]) }
+    end
+
+    ##
+    # Array of cached URLs.
+    #
+    def urls
+      keys.map{ |k| k[/^#{prefix}(.*)/, 1] }
+    end
+
+    ##
+    # Clean up value before returning. Namely, convert empty string to nil.
+    # (Some key/value stores return empty string instead of nil.)
+    #
+    def interpret(value)
+      value == "" ? nil : value
+    end
+  end
+end
diff --git a/lib/geocoder/lookups/base.rb b/lib/geocoder/lookups/base.rb
index 65ff2128..90c17a30 100644
--- a/lib/geocoder/lookups/base.rb
+++ b/lib/geocoder/lookups/base.rb
@@ -60,7 +60,7 @@ module Geocoder
         rescue TimeoutError
           warn "Geocoding API not responding fast enough " +
             "(see Geocoder::Configuration.timeout to set limit)."
-        end
+      end
       end
 
       ##
@@ -82,13 +82,12 @@ module Geocoder
       # Fetches a raw search result (JSON string).
       #
       def fetch_raw_data(query, reverse = false)
-        url = query_url(query, reverse)
-        key = cache_key(url)
         timeout(Geocoder::Configuration.timeout) do
-          unless cache and (response = cache[key]) and response != ""
+          url = query_url(query, reverse)
+          unless cache and response = cache[url]
             response = Net::HTTP.get_response(URI.parse(url)).body
             if cache
-              cache[key] = response
+              cache[url] = response
             end
           end
           response
@@ -96,24 +95,10 @@ module Geocoder
       end
 
       ##
-      # Cache key for a given URL.
-      #
-      def cache_key(url)
-        [cache_prefix, url].join
-      end
-
-      ##
-      # The configured prefix for cache keys.
-      #
-      def cache_prefix
-        Geocoder::Configuration.cache_prefix || "geocoder:"
-      end
-
-      ##
-      # The configured cache store.
+      # The working Cache object.
       #
       def cache
-        Geocoder::Configuration.cache
+        Geocoder.cache
       end
 
       ##
-- 
GitLab