Permalink
Browse files

Add some configurations and support to cache

  • Loading branch information...
1 parent c2fa592 commit b1272a8aa1933f8b47b01e7282c34f6f7fcb20f7 @macedo committed Oct 31, 2011
View
@@ -5,4 +5,5 @@ pkg/*
.rvmrc
*~
docs
+.idea/
View
@@ -5,6 +5,17 @@
$ require 'usps_standardizer'
$ gem install usps_standardizer
+== Configuration
+
+=== Caching
+
+It’s a good idea, when relying on any external service, to cache retrieved data.
+When implemented correctly it improves your app’s response time and stability.
+It’s easy to cache usps results with USPSStandardizer, just configure a cache store:
+
+ $ USPSStandardizer::Configuration.cache = Redis.new
+ $ USPSStandardizer::Configuration.cache_prefix = "wyw"
+
== Usage
$ result = USPSStandardizer.lookup_for(:address => '6216 eddington drive', :state => 'oh', :city => 'middletown')
View
@@ -1,12 +1,27 @@
# -*- encoding : utf-8 -*-
require 'mechanize'
+
+#TODO: Improve documentation
module USPSStandardizer
autoload :Version, "usps_standardizer/version"
autoload :ZipLookup, "usps_standardizer/zip_lookup"
+ autoload :Configuration, "usps_standardizer/configuration"
+ autoload :Cache, "usps_standardizer/cache"
+
+ class << self
+
+ def lookup_for(options, mechanize = Mechanize.new)
+ z = ZipLookup.new(options, mechanize)
+ z.std_address
+ end
+
+ def cache
+ if @cache.nil? and store = Configuration.cache
+ @cache = Cache.new(store, Configuration.cache_prefix)
+ end
+ @cache
+ end
- def self.lookup_for(options, mechanize = Mechanize.new)
- z = USPSStandardizer::ZipLookup.new(options, mechanize)
- z.std_address
end
end
@@ -0,0 +1,50 @@
+module USPSStandardizer
+ class Cache
+
+ def initialize(store, prefix)
+ @store = store
+ @prefix = prefix
+ end
+
+ def [](url)
+ interpret store[key_for(url)]
+ end
+
+ def []=(url, value)
+ store[key_for(url)] = value
+ end
+
+ def expire(url)
+ if url == :all
+ urls.each{ |u| expire(u) }
+ else
+ expire_single_url(url)
+ end
+ end
+
+
+ private
+
+ attr_reader :prefix, :store
+
+ def key_for(url)
+ [prefix, url].join
+ end
+
+ def keys
+ store.keys.select{ |k| k.match /^#{prefix}/ and interpret(store[k]) }
+ end
+
+ def urls
+ keys.map{ |k| k[/^#{prefix}(.*)/, 1] }
+ end
+
+ def interpret(value)
+ value == "" ? nil : value
+ end
+
+ def expire_single_url(url)
+ store.del(key_for(url))
+ end
+ end
+end
@@ -0,0 +1,33 @@
+module USPSStandardizer
+ class Configuration
+
+ def self.options_and_defaults
+ [
+ [:timeout, 5],
+
+ # cache object (must respond to #[], #[]=, and #keys)
+ [:cache, nil],
+
+ # prefix (string) to use for all cache keys
+ [:cache_prefix, "usps:"]
+ ]
+ end
+
+ # define getters and setters for all configuration settings
+ self.options_and_defaults.each do |option, default|
+ class_eval(<<-END, __FILE__, __LINE__ + 1)
+
+ @@#{option} = default unless defined? @@#{option}
+
+ def self.#{option}
+ @@#{option}
+ end
+
+ def self.#{option}=(obj)
+ @@#{option} = obj
+ end
+
+ END
+ end
+ end
+end
@@ -1,8 +1,8 @@
module USPSStandardizer
module Version
MAJOR = 0
- MINOR = 1
- PATCH = 2
+ MINOR = 2
+ PATCH = 0
STRING = "#{MAJOR}.#{MINOR}.#{PATCH}"
end
end
@@ -4,6 +4,9 @@
module USPSStandardizer
+ #TODO: Change from 'mechanize' to 'net/hhtp'
+ #TODO: Implement 'timeout'
+
class ZipLookup
attr_accessor :address, :state, :city, :zipcode
@@ -17,6 +20,12 @@ def initialize(options = {}, mechanize = Mechanize.new)
end
def std_address
+
+ if(cache and response = cache[redis_key(@address)])
+ address, city, state, county, zipcode = response.split('::')
+ return {:address => address, :city => city, :state => state, :county => county, :zipcode => zipcode}
+ end
+
return {} unless (content = get_std_address_content)
content.gsub!(/\t|\n|\r/, '')
@@ -26,18 +35,21 @@ def std_address
raw_matches.inject({}) do |results, raw_match|
if raw_match[0] =~ /mailingIndustryPopup2\(([^\)]*)/i
- @county = $1.split(',')[1].gsub(/'/, '')
+ @r_county = $1.split(',')[1].gsub(/'/, '')
end
- @address, city_state_zipcode = Sanitize.clean(raw_match[0],
+ @r_address, city_state_zipcode = Sanitize.clean(raw_match[0],
:remove_contents => true,
:elements => %w[br]
).strip.split('<br>')
if city_state_zipcode.sub_nonascii(' ').squeeze!(' ') =~ /^(.*)\s+(\w\w)\s(\d{5})(-\d{4})?/i
- @city, @state, @zipcode = $1, $2, $3
+ @r_city, @r_state, @r_zipcode = $1, $2, $3
end
- results = {:address => @address, :city => @city, :state => @state, :county => @county, :zipcode => @zipcode}
+ results = {:address => @r_address, :city => @r_city, :state => @r_state, :county => @r_county, :zipcode => @r_zipcode}
+ if cache
+ cache[redis_key(@address)] = "#{@r_address}::#{@r_city}::#{@r_state}::#{@r_county}::#{@r_zipcode}"
+ end
results
end
end
@@ -56,6 +68,15 @@ def get_std_address_content
return false unless @mechanize.page.search('p.mainRed').empty?
@mechanize.page.body
end
+
+ def cache
+ USPSStandardizer.cache
+ end
+
+ private
+ def redis_key(address)
+ address.downcase.gsub(' ', '_')
+ end
end
end
@@ -6,6 +6,24 @@
USPSStandardizer::Version::STRING.should match(/^\d+\.\d+\.\d+$/)
end
+ describe "configuration" do
+ context "cache" do
+ it " should be nil as default" do
+ USPSStandardizer::Configuration.cache.should == nil
+ end
+ end
+ context "cache prefix" do
+ it "should be usps: as default" do
+ USPSStandardizer::Configuration.cache_prefix.should == 'usps:'
+ end
+ end
+ context "timeout" do
+ it "should be 5 seconds as default" do
+ USPSStandardizer::Configuration.timeout.should == 5
+ end
+ end
+ end
+
it "standards an address on usps website" do
result = USPSStandardizer.lookup_for(:address => '6216 eddington drive', :state => 'oh', :city => 'middletown')
result[:address].should == '6216 EDDINGTON ST'

0 comments on commit b1272a8

Please sign in to comment.