geocoder2 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +5 -0
- data/.travis.yml +27 -0
- data/CHANGELOG.md +329 -0
- data/LICENSE +20 -0
- data/README.md +796 -0
- data/Rakefile +25 -0
- data/bin/geocode2 +5 -0
- data/examples/autoexpire_cache_dalli.rb +62 -0
- data/examples/autoexpire_cache_redis.rb +28 -0
- data/examples/cache_bypass.rb +48 -0
- data/gemfiles/Gemfile.mongoid-2.4.x +15 -0
- data/json?address=26+leonard+street%2C+Belmont&key=AIzaSyDoltU6YL8XeIQrSLFGk6ZfpKaWkPukwYQ&language=en +68 -0
- data/lib/generators/geocoder2/config/config_generator.rb +14 -0
- data/lib/generators/geocoder2/config/templates/initializer.rb +21 -0
- data/lib/geocoder2/cache.rb +89 -0
- data/lib/geocoder2/calculations.rb +389 -0
- data/lib/geocoder2/cli.rb +121 -0
- data/lib/geocoder2/configuration.rb +130 -0
- data/lib/geocoder2/configuration_hash.rb +11 -0
- data/lib/geocoder2/exceptions.rb +21 -0
- data/lib/geocoder2/lookup.rb +86 -0
- data/lib/geocoder2/lookups/baidu.rb +54 -0
- data/lib/geocoder2/lookups/base.rb +266 -0
- data/lib/geocoder2/lookups/bing.rb +47 -0
- data/lib/geocoder2/lookups/dstk.rb +20 -0
- data/lib/geocoder2/lookups/esri.rb +48 -0
- data/lib/geocoder2/lookups/freegeoip.rb +43 -0
- data/lib/geocoder2/lookups/geocoder_ca.rb +54 -0
- data/lib/geocoder2/lookups/geocoder_us.rb +39 -0
- data/lib/geocoder2/lookups/google.rb +69 -0
- data/lib/geocoder2/lookups/google_premier.rb +47 -0
- data/lib/geocoder2/lookups/mapquest.rb +59 -0
- data/lib/geocoder2/lookups/maxmind.rb +88 -0
- data/lib/geocoder2/lookups/nominatim.rb +44 -0
- data/lib/geocoder2/lookups/ovi.rb +62 -0
- data/lib/geocoder2/lookups/test.rb +44 -0
- data/lib/geocoder2/lookups/yahoo.rb +86 -0
- data/lib/geocoder2/lookups/yandex.rb +54 -0
- data/lib/geocoder2/models/active_record.rb +46 -0
- data/lib/geocoder2/models/base.rb +42 -0
- data/lib/geocoder2/models/mongo_base.rb +60 -0
- data/lib/geocoder2/models/mongo_mapper.rb +26 -0
- data/lib/geocoder2/models/mongoid.rb +32 -0
- data/lib/geocoder2/query.rb +107 -0
- data/lib/geocoder2/railtie.rb +26 -0
- data/lib/geocoder2/request.rb +23 -0
- data/lib/geocoder2/results/baidu.rb +79 -0
- data/lib/geocoder2/results/base.rb +67 -0
- data/lib/geocoder2/results/bing.rb +48 -0
- data/lib/geocoder2/results/dstk.rb +6 -0
- data/lib/geocoder2/results/esri.rb +51 -0
- data/lib/geocoder2/results/freegeoip.rb +45 -0
- data/lib/geocoder2/results/geocoder_ca.rb +60 -0
- data/lib/geocoder2/results/geocoder_us.rb +39 -0
- data/lib/geocoder2/results/google.rb +124 -0
- data/lib/geocoder2/results/google_premier.rb +6 -0
- data/lib/geocoder2/results/mapquest.rb +51 -0
- data/lib/geocoder2/results/maxmind.rb +135 -0
- data/lib/geocoder2/results/nominatim.rb +94 -0
- data/lib/geocoder2/results/ovi.rb +62 -0
- data/lib/geocoder2/results/test.rb +16 -0
- data/lib/geocoder2/results/yahoo.rb +55 -0
- data/lib/geocoder2/results/yandex.rb +80 -0
- data/lib/geocoder2/sql.rb +106 -0
- data/lib/geocoder2/stores/active_record.rb +272 -0
- data/lib/geocoder2/stores/base.rb +120 -0
- data/lib/geocoder2/stores/mongo_base.rb +89 -0
- data/lib/geocoder2/stores/mongo_mapper.rb +13 -0
- data/lib/geocoder2/stores/mongoid.rb +13 -0
- data/lib/geocoder2/version.rb +3 -0
- data/lib/geocoder2.rb +55 -0
- data/lib/hash_recursive_merge.rb +74 -0
- data/lib/oauth_util.rb +112 -0
- data/lib/tasks/geocoder2.rake +27 -0
- data/test/active_record_test.rb +15 -0
- data/test/cache_test.rb +35 -0
- data/test/calculations_test.rb +211 -0
- data/test/configuration_test.rb +78 -0
- data/test/custom_block_test.rb +32 -0
- data/test/error_handling_test.rb +43 -0
- data/test/fixtures/baidu_invalid_key +1 -0
- data/test/fixtures/baidu_no_results +1 -0
- data/test/fixtures/baidu_reverse +1 -0
- data/test/fixtures/baidu_shanghai_pearl_tower +12 -0
- data/test/fixtures/bing_invalid_key +1 -0
- data/test/fixtures/bing_madison_square_garden +40 -0
- data/test/fixtures/bing_no_results +16 -0
- data/test/fixtures/bing_reverse +42 -0
- data/test/fixtures/esri_madison_square_garden +59 -0
- data/test/fixtures/esri_no_results +8 -0
- data/test/fixtures/esri_reverse +21 -0
- data/test/fixtures/freegeoip_74_200_247_59 +12 -0
- data/test/fixtures/freegeoip_no_results +1 -0
- data/test/fixtures/geocoder_ca_madison_square_garden +1 -0
- data/test/fixtures/geocoder_ca_no_results +1 -0
- data/test/fixtures/geocoder_ca_reverse +34 -0
- data/test/fixtures/geocoder_us_madison_square_garden +1 -0
- data/test/fixtures/geocoder_us_no_results +1 -0
- data/test/fixtures/google_garbage +456 -0
- data/test/fixtures/google_madison_square_garden +57 -0
- data/test/fixtures/google_no_city_data +44 -0
- data/test/fixtures/google_no_locality +51 -0
- data/test/fixtures/google_no_results +4 -0
- data/test/fixtures/google_over_limit +4 -0
- data/test/fixtures/mapquest_error +16 -0
- data/test/fixtures/mapquest_invalid_api_key +16 -0
- data/test/fixtures/mapquest_invalid_request +16 -0
- data/test/fixtures/mapquest_madison_square_garden +52 -0
- data/test/fixtures/mapquest_no_results +16 -0
- data/test/fixtures/maxmind_24_24_24_21 +1 -0
- data/test/fixtures/maxmind_24_24_24_22 +1 -0
- data/test/fixtures/maxmind_24_24_24_23 +1 -0
- data/test/fixtures/maxmind_24_24_24_24 +1 -0
- data/test/fixtures/maxmind_74_200_247_59 +1 -0
- data/test/fixtures/maxmind_invalid_key +1 -0
- data/test/fixtures/maxmind_no_results +1 -0
- data/test/fixtures/nominatim_madison_square_garden +150 -0
- data/test/fixtures/nominatim_no_results +1 -0
- data/test/fixtures/ovi_madison_square_garden +72 -0
- data/test/fixtures/ovi_no_results +8 -0
- data/test/fixtures/yahoo_error +1 -0
- data/test/fixtures/yahoo_invalid_key +2 -0
- data/test/fixtures/yahoo_madison_square_garden +52 -0
- data/test/fixtures/yahoo_no_results +10 -0
- data/test/fixtures/yahoo_over_limit +2 -0
- data/test/fixtures/yandex_invalid_key +1 -0
- data/test/fixtures/yandex_kremlin +48 -0
- data/test/fixtures/yandex_no_city_and_town +112 -0
- data/test/fixtures/yandex_no_results +16 -0
- data/test/geocoder_test.rb +59 -0
- data/test/https_test.rb +16 -0
- data/test/integration/smoke_test.rb +26 -0
- data/test/lookup_test.rb +117 -0
- data/test/method_aliases_test.rb +25 -0
- data/test/mongoid_test.rb +46 -0
- data/test/mongoid_test_helper.rb +43 -0
- data/test/near_test.rb +61 -0
- data/test/oauth_util_test.rb +30 -0
- data/test/proxy_test.rb +36 -0
- data/test/query_test.rb +52 -0
- data/test/request_test.rb +29 -0
- data/test/result_test.rb +42 -0
- data/test/services_test.rb +393 -0
- data/test/test_helper.rb +289 -0
- data/test/test_mode_test.rb +59 -0
- metadata +213 -0
data/Rakefile
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require 'bundler'
|
|
2
|
+
Bundler::GemHelper.install_tasks
|
|
3
|
+
|
|
4
|
+
require 'rake/testtask'
|
|
5
|
+
Rake::TestTask.new(:test) do |test|
|
|
6
|
+
test.libs << 'lib' << 'test'
|
|
7
|
+
test.pattern = 'test/*_test.rb'
|
|
8
|
+
test.verbose = true
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
Rake::TestTask.new(:integration) do |test|
|
|
12
|
+
test.libs << 'lib' << 'test'
|
|
13
|
+
test.pattern = 'test/integration/*_test.rb'
|
|
14
|
+
test.verbose = true
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
task :default => [:test]
|
|
18
|
+
|
|
19
|
+
require 'rdoc/task'
|
|
20
|
+
Rake::RDocTask.new do |rdoc|
|
|
21
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
22
|
+
rdoc.title = "Geocoder2 #{Geocoder2::VERSION}"
|
|
23
|
+
rdoc.rdoc_files.include('*.rdoc')
|
|
24
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
25
|
+
end
|
data/bin/geocode2
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# This class implements a cache with simple delegation to the the Dalli Memcached client
|
|
2
|
+
# https://github.com/mperham/dalli
|
|
3
|
+
#
|
|
4
|
+
# A TTL is set on initialization
|
|
5
|
+
|
|
6
|
+
class AutoexpireCacheDalli
|
|
7
|
+
def initialize(store, ttl = 86400)
|
|
8
|
+
@store = store
|
|
9
|
+
@keys = 'Geocoder2DalliClientKeys'
|
|
10
|
+
@ttl = ttl
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def [](url)
|
|
14
|
+
res = @store.get(url)
|
|
15
|
+
res = YAML::load(res) if res.present?
|
|
16
|
+
res
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def []=(url, value)
|
|
20
|
+
if value.nil?
|
|
21
|
+
del(url)
|
|
22
|
+
else
|
|
23
|
+
key_cache_add(url) if @store.add(url, YAML::dump(value), @ttl)
|
|
24
|
+
end
|
|
25
|
+
value
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def keys
|
|
29
|
+
key_cache
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def del(url)
|
|
33
|
+
key_cache_delete(url) if @store.delete(url)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def key_cache
|
|
39
|
+
the_keys = @store.get(@keys)
|
|
40
|
+
if the_keys.nil?
|
|
41
|
+
@store.add(@keys, YAML::dump([]))
|
|
42
|
+
[]
|
|
43
|
+
else
|
|
44
|
+
YAML::load(the_keys)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def key_cache_add(key)
|
|
49
|
+
@store.replace(@keys, YAML::dump(key_cache << key))
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def key_cache_delete(key)
|
|
53
|
+
tmp = key_cache
|
|
54
|
+
tmp.delete(key)
|
|
55
|
+
@store.replace(@keys, YAML::dump(tmp))
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Here Dalli is set up as on Heroku using the Memcachier gem.
|
|
60
|
+
# https://devcenter.heroku.com/articles/memcachier#ruby
|
|
61
|
+
# On other setups you might have to specify your Memcached server in Dalli::Client.new
|
|
62
|
+
Geocoder2.configure(:cache => AutoexpireCacheDalli.new(Dalli::Client.new))
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# This class implements a cache with simple delegation to the Redis store, but
|
|
2
|
+
# when it creates a key/value pair, it also sends an EXPIRE command with a TTL.
|
|
3
|
+
# It should be fairly simple to do the same thing with Memcached.
|
|
4
|
+
class AutoexpireCacheRedis
|
|
5
|
+
def initialize(store, ttl = 86400)
|
|
6
|
+
@store = store
|
|
7
|
+
@ttl = ttl
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def [](url)
|
|
11
|
+
@store.[](url)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def []=(url, value)
|
|
15
|
+
@store.[]=(url, value)
|
|
16
|
+
@store.expire(url, @ttl)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def keys
|
|
20
|
+
@store.keys
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def del(url)
|
|
24
|
+
@store.del(url)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
Geocoder2.configure(:cache => AutoexpireCacheRedis.new(Redis.new))
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# This class allows you to configure how Geocoder2 should treat errors that occur when
|
|
2
|
+
# the cache is not available.
|
|
3
|
+
# Configure it like this
|
|
4
|
+
# config/initializers/geocoder2.rb
|
|
5
|
+
# Geocoder2.configure(
|
|
6
|
+
# :cache => Geocoder2::CacheBypass.new(Redis.new)
|
|
7
|
+
# )
|
|
8
|
+
#
|
|
9
|
+
# Depending on the value of @bypass this will either
|
|
10
|
+
# raise the exception (true) or swallow it and pretend the cache did not return a hit (false)
|
|
11
|
+
#
|
|
12
|
+
class Geocoder2::CacheBypass
|
|
13
|
+
def initialize(target, bypass = true)
|
|
14
|
+
@target = target
|
|
15
|
+
@bypass = bypass
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def [](key)
|
|
20
|
+
with_bypass { @target[key] }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def []=(key, value)
|
|
24
|
+
with_bypass(value) { @target[key] = value }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def keys
|
|
28
|
+
with_bypass([]) { @target.keys }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def del(key)
|
|
32
|
+
with_bypass { @target.del(key) }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def with_bypass(return_value_if_exception = nil, &block)
|
|
38
|
+
begin
|
|
39
|
+
yield
|
|
40
|
+
rescue
|
|
41
|
+
if @bypass
|
|
42
|
+
return_value_if_exception
|
|
43
|
+
else
|
|
44
|
+
raise # reraise original exception
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"results" : [
|
|
3
|
+
{
|
|
4
|
+
"address_components" : [
|
|
5
|
+
{
|
|
6
|
+
"long_name" : "26",
|
|
7
|
+
"short_name" : "26",
|
|
8
|
+
"types" : [ "street_number" ]
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"long_name" : "Leonard Street",
|
|
12
|
+
"short_name" : "Leonard St",
|
|
13
|
+
"types" : [ "route" ]
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"long_name" : "Belmont",
|
|
17
|
+
"short_name" : "Belmont",
|
|
18
|
+
"types" : [ "locality", "political" ]
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"long_name" : "Greater Geelong City",
|
|
22
|
+
"short_name" : "Greater Geelong",
|
|
23
|
+
"types" : [ "administrative_area_level_2", "political" ]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"long_name" : "Victoria",
|
|
27
|
+
"short_name" : "VIC",
|
|
28
|
+
"types" : [ "administrative_area_level_1", "political" ]
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"long_name" : "Australia",
|
|
32
|
+
"short_name" : "AU",
|
|
33
|
+
"types" : [ "country", "political" ]
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"long_name" : "3216",
|
|
37
|
+
"short_name" : "3216",
|
|
38
|
+
"types" : [ "postal_code" ]
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
"formatted_address" : "26 Leonard St, Belmont VIC 3216, Australia",
|
|
42
|
+
"geometry" : {
|
|
43
|
+
"location" : {
|
|
44
|
+
"lat" : -38.1704428,
|
|
45
|
+
"lng" : 144.3313951
|
|
46
|
+
},
|
|
47
|
+
"location_type" : "ROOFTOP",
|
|
48
|
+
"viewport" : {
|
|
49
|
+
"northeast" : {
|
|
50
|
+
"lat" : -38.1690938197085,
|
|
51
|
+
"lng" : 144.3327440802915
|
|
52
|
+
},
|
|
53
|
+
"southwest" : {
|
|
54
|
+
"lat" : -38.1717917802915,
|
|
55
|
+
"lng" : 144.3300461197085
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"place_id" : "ChIJzz9XwJ0T1GoRKPvo8BszH70",
|
|
60
|
+
"plus_code" : {
|
|
61
|
+
"compound_code" : "R8HJ+RH Geelong, Victoria, Australia",
|
|
62
|
+
"global_code" : "4RH6R8HJ+RH"
|
|
63
|
+
},
|
|
64
|
+
"types" : [ "street_address" ]
|
|
65
|
+
}
|
|
66
|
+
],
|
|
67
|
+
"status" : "OK"
|
|
68
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require 'rails/generators'
|
|
2
|
+
|
|
3
|
+
module Geocoder2
|
|
4
|
+
class ConfigGenerator < Rails::Generators::Base
|
|
5
|
+
source_root File.expand_path("../templates", __FILE__)
|
|
6
|
+
|
|
7
|
+
desc "This generator creates an initializer file at config/initializers, " +
|
|
8
|
+
"with the default configuration options for Geocoder2."
|
|
9
|
+
def add_initializer
|
|
10
|
+
template "initializer.rb", "config/initializers/geocoder2.rb"
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Geocoder2.configure(
|
|
2
|
+
# geocoding options
|
|
3
|
+
# :timeout => 3, # geocoding service timeout (secs)
|
|
4
|
+
# :lookup => :google, # name of geocoding service (symbol)
|
|
5
|
+
# :language => :en, # ISO-639 language code
|
|
6
|
+
# :use_https => false, # use HTTPS for lookup requests? (if supported)
|
|
7
|
+
# :http_proxy => nil, # HTTP proxy server (user:pass@host:port)
|
|
8
|
+
# :https_proxy => nil, # HTTPS proxy server (user:pass@host:port)
|
|
9
|
+
# :api_key => nil, # API key for geocoding service
|
|
10
|
+
# :cache => nil, # cache object (must respond to #[], #[]=, and #keys)
|
|
11
|
+
# :cache_prefix => "geocoder2:", # prefix (string) to use for all cache keys
|
|
12
|
+
|
|
13
|
+
# exceptions that should not be rescued by default
|
|
14
|
+
# (if you want to implement custom error handling);
|
|
15
|
+
# supports SocketError and TimeoutError
|
|
16
|
+
# :always_raise => [],
|
|
17
|
+
|
|
18
|
+
# calculation options
|
|
19
|
+
# :units => :mi, # :km for kilometers or :mi for miles
|
|
20
|
+
# :distances => :linear # :spherical or :linear
|
|
21
|
+
)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
module Geocoder2
|
|
2
|
+
class Cache
|
|
3
|
+
|
|
4
|
+
def initialize(store, prefix)
|
|
5
|
+
@store = store
|
|
6
|
+
@prefix = prefix
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
##
|
|
10
|
+
# Read from the Cache.
|
|
11
|
+
#
|
|
12
|
+
def [](url)
|
|
13
|
+
interpret case
|
|
14
|
+
when store.respond_to?(:[])
|
|
15
|
+
store[key_for(url)]
|
|
16
|
+
when store.respond_to?(:get)
|
|
17
|
+
store.get key_for(url)
|
|
18
|
+
when store.respond_to?(:read)
|
|
19
|
+
store.read key_for(url)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
##
|
|
24
|
+
# Write to the Cache.
|
|
25
|
+
#
|
|
26
|
+
def []=(url, value)
|
|
27
|
+
case
|
|
28
|
+
when store.respond_to?(:[]=)
|
|
29
|
+
store[key_for(url)] = value
|
|
30
|
+
when store.respond_to?(:set)
|
|
31
|
+
store.set key_for(url), value
|
|
32
|
+
when store.respond_to?(:write)
|
|
33
|
+
store.write key_for(url), value
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
##
|
|
38
|
+
# Delete cache entry for given URL,
|
|
39
|
+
# or pass <tt>:all</tt> to clear all URLs.
|
|
40
|
+
#
|
|
41
|
+
def expire(url)
|
|
42
|
+
if url == :all
|
|
43
|
+
urls.each{ |u| expire(u) }
|
|
44
|
+
else
|
|
45
|
+
expire_single_url(url)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
private # ----------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
attr_reader :prefix, :store
|
|
53
|
+
|
|
54
|
+
##
|
|
55
|
+
# Cache key for a given URL.
|
|
56
|
+
#
|
|
57
|
+
def key_for(url)
|
|
58
|
+
[prefix, url].join
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
##
|
|
62
|
+
# Array of keys with the currently configured prefix
|
|
63
|
+
# that have non-nil values.
|
|
64
|
+
#
|
|
65
|
+
def keys
|
|
66
|
+
store.keys.select{ |k| k.match /^#{prefix}/ and interpret(store[k]) }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
##
|
|
70
|
+
# Array of cached URLs.
|
|
71
|
+
#
|
|
72
|
+
def urls
|
|
73
|
+
keys.map{ |k| k[/^#{prefix}(.*)/, 1] }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
##
|
|
77
|
+
# Clean up value before returning. Namely, convert empty string to nil.
|
|
78
|
+
# (Some key/value stores return empty string instead of nil.)
|
|
79
|
+
#
|
|
80
|
+
def interpret(value)
|
|
81
|
+
value == "" ? nil : value
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def expire_single_url(url)
|
|
85
|
+
key = key_for(url)
|
|
86
|
+
store.respond_to?(:del) ? store.del(key) : store.delete(key)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|