really-broken-geocoder 1.5.1

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.
Files changed (136) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +557 -0
  3. data/LICENSE +20 -0
  4. data/README.md +3 -0
  5. data/bin/geocode +5 -0
  6. data/examples/autoexpire_cache_dalli.rb +62 -0
  7. data/examples/autoexpire_cache_redis.rb +28 -0
  8. data/examples/cache_bypass.rb +48 -0
  9. data/examples/reverse_geocode_job.rb +40 -0
  10. data/lib/generators/geocoder/config/config_generator.rb +14 -0
  11. data/lib/generators/geocoder/config/templates/initializer.rb +22 -0
  12. data/lib/generators/geocoder/maxmind/geolite_city_generator.rb +30 -0
  13. data/lib/generators/geocoder/maxmind/geolite_country_generator.rb +30 -0
  14. data/lib/generators/geocoder/maxmind/templates/migration/geolite_city.rb +30 -0
  15. data/lib/generators/geocoder/maxmind/templates/migration/geolite_country.rb +17 -0
  16. data/lib/generators/geocoder/migration_version.rb +15 -0
  17. data/lib/geocoder.rb +48 -0
  18. data/lib/geocoder/cache.rb +94 -0
  19. data/lib/geocoder/calculations.rb +420 -0
  20. data/lib/geocoder/cli.rb +121 -0
  21. data/lib/geocoder/configuration.rb +137 -0
  22. data/lib/geocoder/configuration_hash.rb +11 -0
  23. data/lib/geocoder/esri_token.rb +38 -0
  24. data/lib/geocoder/exceptions.rb +40 -0
  25. data/lib/geocoder/ip_address.rb +26 -0
  26. data/lib/geocoder/kernel_logger.rb +25 -0
  27. data/lib/geocoder/logger.rb +47 -0
  28. data/lib/geocoder/lookup.rb +118 -0
  29. data/lib/geocoder/lookups/amap.rb +63 -0
  30. data/lib/geocoder/lookups/baidu.rb +63 -0
  31. data/lib/geocoder/lookups/baidu_ip.rb +30 -0
  32. data/lib/geocoder/lookups/ban_data_gouv_fr.rb +130 -0
  33. data/lib/geocoder/lookups/base.rb +348 -0
  34. data/lib/geocoder/lookups/bing.rb +82 -0
  35. data/lib/geocoder/lookups/db_ip_com.rb +52 -0
  36. data/lib/geocoder/lookups/dstk.rb +22 -0
  37. data/lib/geocoder/lookups/esri.rb +95 -0
  38. data/lib/geocoder/lookups/freegeoip.rb +60 -0
  39. data/lib/geocoder/lookups/geocoder_ca.rb +53 -0
  40. data/lib/geocoder/lookups/geocoder_us.rb +51 -0
  41. data/lib/geocoder/lookups/geocodio.rb +42 -0
  42. data/lib/geocoder/lookups/geoip2.rb +45 -0
  43. data/lib/geocoder/lookups/geoportail_lu.rb +65 -0
  44. data/lib/geocoder/lookups/google.rb +95 -0
  45. data/lib/geocoder/lookups/google_places_details.rb +50 -0
  46. data/lib/geocoder/lookups/google_places_search.rb +33 -0
  47. data/lib/geocoder/lookups/google_premier.rb +57 -0
  48. data/lib/geocoder/lookups/here.rb +77 -0
  49. data/lib/geocoder/lookups/ip2location.rb +75 -0
  50. data/lib/geocoder/lookups/ipapi_com.rb +82 -0
  51. data/lib/geocoder/lookups/ipdata_co.rb +62 -0
  52. data/lib/geocoder/lookups/ipinfo_io.rb +44 -0
  53. data/lib/geocoder/lookups/ipstack.rb +63 -0
  54. data/lib/geocoder/lookups/latlon.rb +59 -0
  55. data/lib/geocoder/lookups/location_iq.rb +50 -0
  56. data/lib/geocoder/lookups/mapbox.rb +59 -0
  57. data/lib/geocoder/lookups/mapquest.rb +58 -0
  58. data/lib/geocoder/lookups/maxmind.rb +90 -0
  59. data/lib/geocoder/lookups/maxmind_geoip2.rb +70 -0
  60. data/lib/geocoder/lookups/maxmind_local.rb +65 -0
  61. data/lib/geocoder/lookups/nominatim.rb +64 -0
  62. data/lib/geocoder/lookups/opencagedata.rb +65 -0
  63. data/lib/geocoder/lookups/pelias.rb +63 -0
  64. data/lib/geocoder/lookups/pickpoint.rb +41 -0
  65. data/lib/geocoder/lookups/pointpin.rb +69 -0
  66. data/lib/geocoder/lookups/postcode_anywhere_uk.rb +50 -0
  67. data/lib/geocoder/lookups/postcodes_io.rb +31 -0
  68. data/lib/geocoder/lookups/smarty_streets.rb +63 -0
  69. data/lib/geocoder/lookups/telize.rb +75 -0
  70. data/lib/geocoder/lookups/tencent.rb +59 -0
  71. data/lib/geocoder/lookups/test.rb +44 -0
  72. data/lib/geocoder/lookups/yandex.rb +62 -0
  73. data/lib/geocoder/models/active_record.rb +51 -0
  74. data/lib/geocoder/models/base.rb +39 -0
  75. data/lib/geocoder/models/mongo_base.rb +62 -0
  76. data/lib/geocoder/models/mongo_mapper.rb +26 -0
  77. data/lib/geocoder/models/mongoid.rb +32 -0
  78. data/lib/geocoder/query.rb +125 -0
  79. data/lib/geocoder/railtie.rb +26 -0
  80. data/lib/geocoder/request.rb +114 -0
  81. data/lib/geocoder/results/amap.rb +87 -0
  82. data/lib/geocoder/results/baidu.rb +79 -0
  83. data/lib/geocoder/results/baidu_ip.rb +62 -0
  84. data/lib/geocoder/results/ban_data_gouv_fr.rb +257 -0
  85. data/lib/geocoder/results/base.rb +79 -0
  86. data/lib/geocoder/results/bing.rb +52 -0
  87. data/lib/geocoder/results/db_ip_com.rb +58 -0
  88. data/lib/geocoder/results/dstk.rb +6 -0
  89. data/lib/geocoder/results/esri.rb +75 -0
  90. data/lib/geocoder/results/freegeoip.rb +40 -0
  91. data/lib/geocoder/results/geocoder_ca.rb +60 -0
  92. data/lib/geocoder/results/geocoder_us.rb +39 -0
  93. data/lib/geocoder/results/geocodio.rb +78 -0
  94. data/lib/geocoder/results/geoip2.rb +76 -0
  95. data/lib/geocoder/results/geoportail_lu.rb +71 -0
  96. data/lib/geocoder/results/google.rb +150 -0
  97. data/lib/geocoder/results/google_places_details.rb +39 -0
  98. data/lib/geocoder/results/google_places_search.rb +52 -0
  99. data/lib/geocoder/results/google_premier.rb +6 -0
  100. data/lib/geocoder/results/here.rb +79 -0
  101. data/lib/geocoder/results/ip2location.rb +22 -0
  102. data/lib/geocoder/results/ipapi_com.rb +45 -0
  103. data/lib/geocoder/results/ipdata_co.rb +40 -0
  104. data/lib/geocoder/results/ipinfo_io.rb +48 -0
  105. data/lib/geocoder/results/ipstack.rb +60 -0
  106. data/lib/geocoder/results/latlon.rb +71 -0
  107. data/lib/geocoder/results/location_iq.rb +6 -0
  108. data/lib/geocoder/results/mapbox.rb +57 -0
  109. data/lib/geocoder/results/mapquest.rb +48 -0
  110. data/lib/geocoder/results/maxmind.rb +130 -0
  111. data/lib/geocoder/results/maxmind_geoip2.rb +9 -0
  112. data/lib/geocoder/results/maxmind_local.rb +44 -0
  113. data/lib/geocoder/results/nominatim.rb +109 -0
  114. data/lib/geocoder/results/opencagedata.rb +100 -0
  115. data/lib/geocoder/results/pelias.rb +58 -0
  116. data/lib/geocoder/results/pickpoint.rb +6 -0
  117. data/lib/geocoder/results/pointpin.rb +40 -0
  118. data/lib/geocoder/results/postcode_anywhere_uk.rb +42 -0
  119. data/lib/geocoder/results/postcodes_io.rb +40 -0
  120. data/lib/geocoder/results/smarty_streets.rb +142 -0
  121. data/lib/geocoder/results/telize.rb +40 -0
  122. data/lib/geocoder/results/tencent.rb +72 -0
  123. data/lib/geocoder/results/test.rb +33 -0
  124. data/lib/geocoder/results/yandex.rb +134 -0
  125. data/lib/geocoder/sql.rb +110 -0
  126. data/lib/geocoder/stores/active_record.rb +328 -0
  127. data/lib/geocoder/stores/base.rb +115 -0
  128. data/lib/geocoder/stores/mongo_base.rb +58 -0
  129. data/lib/geocoder/stores/mongo_mapper.rb +13 -0
  130. data/lib/geocoder/stores/mongoid.rb +13 -0
  131. data/lib/geocoder/version.rb +3 -0
  132. data/lib/hash_recursive_merge.rb +74 -0
  133. data/lib/maxmind_database.rb +109 -0
  134. data/lib/tasks/geocoder.rake +54 -0
  135. data/lib/tasks/maxmind.rake +73 -0
  136. metadata +186 -0
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009-11 Alex Reisner
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,3 @@
1
+ This is a fork of the excellent [Geocoder](https://github.com/alexreisner/geocoder) gem, that we have broken to give our learners a debugging experience.
2
+
3
+ DO NOT USE THIS GEM IT IS REALLY BROKEN!!!
data/bin/geocode ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'geocoder/cli'
4
+
5
+ Geocoder::Cli.run ARGV
@@ -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 = 'GeocoderDalliClientKeys'
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
+ Geocoder.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.get(url)
12
+ end
13
+
14
+ def []=(url, value)
15
+ @store.set(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
+ Geocoder.configure(:cache => AutoexpireCacheRedis.new(Redis.new))
@@ -0,0 +1,48 @@
1
+ # This class allows you to configure how Geocoder should treat errors that occur when
2
+ # the cache is not available.
3
+ # Configure it like this
4
+ # config/initializers/geocoder.rb
5
+ # Geocoder.configure(
6
+ # :cache => Geocoder::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 Geocoder::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,40 @@
1
+ # This class implements an ActiveJob job for performing reverse-geocoding
2
+ # asynchronously. Example usage:
3
+
4
+ # if @location.save && @location.address.blank?
5
+ # ReverseGeocodeJob.perform_later(@location)
6
+ # end
7
+
8
+ # Be sure to configure the queue adapter in config/application.rb:
9
+ # config.active_job.queue_adapter = :sidekiq
10
+
11
+ # You can read the Rails docs for more information on configuring ActiveJob:
12
+ # http://edgeguides.rubyonrails.org/active_job_basics.html
13
+
14
+ class ReverseGeocodeJob < ActiveJob::Base
15
+ queue_as :high
16
+
17
+ def perform(location)
18
+ address = address(location)
19
+
20
+ if address.present?
21
+ location.update(address: address)
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def address(location)
28
+ Geocoder.address(location.coordinates)
29
+ rescue => exception
30
+ MonitoringService.notify(exception, location: { id: location.id })
31
+
32
+ if retryable?(exception)
33
+ raise exception
34
+ end
35
+ end
36
+
37
+ def retryable?(exception)
38
+ exception.is_a?(Timeout::Error) || exception.is_a?(SocketError)
39
+ end
40
+ end
@@ -0,0 +1,14 @@
1
+ require 'rails/generators'
2
+
3
+ module Geocoder
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 Geocoder."
9
+ def add_initializer
10
+ template "initializer.rb", "config/initializers/geocoder.rb"
11
+ end
12
+ end
13
+ end
14
+
@@ -0,0 +1,22 @@
1
+ Geocoder.configure(
2
+ # Geocoding options
3
+ # timeout: 3, # geocoding service timeout (secs)
4
+ # lookup: :nominatim, # name of geocoding service (symbol)
5
+ # ip_lookup: :ipinfo_io, # name of IP address geocoding service (symbol)
6
+ # language: :en, # ISO-639 language code
7
+ # use_https: false, # use HTTPS for lookup requests? (if supported)
8
+ # http_proxy: nil, # HTTP proxy server (user:pass@host:port)
9
+ # https_proxy: nil, # HTTPS proxy server (user:pass@host:port)
10
+ # api_key: nil, # API key for geocoding service
11
+ # cache: nil, # cache object (must respond to #[], #[]=, and #del)
12
+ # cache_prefix: 'geocoder:', # prefix (string) to use for all cache keys
13
+
14
+ # Exceptions that should not be rescued by default
15
+ # (if you want to implement custom error handling);
16
+ # supports SocketError and Timeout::Error
17
+ # always_raise: [],
18
+
19
+ # Calculation options
20
+ # units: :mi, # :km for kilometers or :mi for miles
21
+ # distances: :linear # :spherical or :linear
22
+ )
@@ -0,0 +1,30 @@
1
+ require 'rails/generators/migration'
2
+ require 'generators/geocoder/migration_version'
3
+
4
+ module Geocoder
5
+ module Generators
6
+ module Maxmind
7
+ class GeoliteCityGenerator < Rails::Generators::Base
8
+ include Rails::Generators::Migration
9
+ include Generators::MigrationVersion
10
+
11
+ source_root File.expand_path('../templates', __FILE__)
12
+
13
+ def copy_migration_files
14
+ migration_template "migration/geolite_city.rb", "db/migrate/geocoder_maxmind_geolite_city.rb"
15
+ end
16
+
17
+ # Define the next_migration_number method (necessary for the
18
+ # migration_template method to work)
19
+ def self.next_migration_number(dirname)
20
+ if ActiveRecord::Base.timestamped_migrations
21
+ sleep 1 # make sure each time we get a different timestamp
22
+ Time.new.utc.strftime("%Y%m%d%H%M%S")
23
+ else
24
+ "%.3d" % (current_migration_number(dirname) + 1)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ require 'rails/generators/migration'
2
+ require 'generators/geocoder/migration_version'
3
+
4
+ module Geocoder
5
+ module Generators
6
+ module Maxmind
7
+ class GeoliteCountryGenerator < Rails::Generators::Base
8
+ include Rails::Generators::Migration
9
+ include Generators::MigrationVersion
10
+
11
+ source_root File.expand_path('../templates', __FILE__)
12
+
13
+ def copy_migration_files
14
+ migration_template "migration/geolite_country.rb", "db/migrate/geocoder_maxmind_geolite_country.rb"
15
+ end
16
+
17
+ # Define the next_migration_number method (necessary for the
18
+ # migration_template method to work)
19
+ def self.next_migration_number(dirname)
20
+ if ActiveRecord::Base.timestamped_migrations
21
+ sleep 1 # make sure each time we get a different timestamp
22
+ Time.new.utc.strftime("%Y%m%d%H%M%S")
23
+ else
24
+ "%.3d" % (current_migration_number(dirname) + 1)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ class GeocoderMaxmindGeoliteCity < ActiveRecord::Migration<%= migration_version %>
2
+ def self.up
3
+ create_table :maxmind_geolite_city_blocks, id: false do |t|
4
+ t.column :start_ip_num, :bigint, null: false
5
+ t.column :end_ip_num, :bigint, null: false
6
+ t.column :loc_id, :bigint, null: false
7
+ end
8
+ add_index :maxmind_geolite_city_blocks, :loc_id
9
+ add_index :maxmind_geolite_city_blocks, :start_ip_num, unique: true
10
+ add_index :maxmind_geolite_city_blocks, [:end_ip_num, :start_ip_num], unique: true, name: 'index_maxmind_geolite_city_blocks_on_end_ip_num_range'
11
+
12
+ create_table :maxmind_geolite_city_location, id: false do |t|
13
+ t.column :loc_id, :bigint, null: false
14
+ t.string :country, null: false
15
+ t.string :region, null: false
16
+ t.string :city
17
+ t.string :postal_code, null: false
18
+ t.float :latitude
19
+ t.float :longitude
20
+ t.integer :metro_code
21
+ t.integer :area_code
22
+ end
23
+ add_index :maxmind_geolite_city_location, :loc_id, unique: true
24
+ end
25
+
26
+ def self.down
27
+ drop_table :maxmind_geolite_city_location
28
+ drop_table :maxmind_geolite_city_blocks
29
+ end
30
+ end
@@ -0,0 +1,17 @@
1
+ class GeocoderMaxmindGeoliteCountry < ActiveRecord::Migration<%= migration_version %>
2
+ def self.up
3
+ create_table :maxmind_geolite_country, id: false do |t|
4
+ t.column :start_ip, :string
5
+ t.column :end_ip, :string
6
+ t.column :start_ip_num, :bigint, null: false
7
+ t.column :end_ip_num, :bigint, null: false
8
+ t.column :country_code, :string, null: false
9
+ t.column :country, :string, null: false
10
+ end
11
+ add_index :maxmind_geolite_country, :start_ip_num, unique: true
12
+ end
13
+
14
+ def self.down
15
+ drop_table :maxmind_geolite_country
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ module Geocoder
2
+ module Generators
3
+ module MigrationVersion
4
+ def rails_5?
5
+ Rails::VERSION::MAJOR == 5
6
+ end
7
+
8
+ def migration_version
9
+ if rails_5?
10
+ "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
data/lib/geocoder.rb ADDED
@@ -0,0 +1,48 @@
1
+ require "geocoder/configuration"
2
+ require "geocoder/logger"
3
+ require "geocoder/kernel_logger"
4
+ require "geocoder/query"
5
+ require "geocoder/calculations"
6
+ require "geocoder/exceptions"
7
+ require "geocoder/cache"
8
+ require "geocoder/request"
9
+ require "geocoder/lookup"
10
+ require "geocoder/ip_address"
11
+ require "geocoder/models/active_record" if defined?(::ActiveRecord)
12
+ require "geocoder/models/mongoid" if defined?(::Mongoid)
13
+ require "geocoder/models/mongo_mapper" if defined?(::MongoMapper)
14
+
15
+ module Geocoder
16
+
17
+ ##
18
+ # Search for information about an address or a set of coordinates.
19
+ #
20
+ def self.search(query, options = {})
21
+ query = Geocoder::Query.new(query, options) unless query.is_a?(Geocoder::Query)
22
+ query.blank? ? [] : query.execute
23
+ end
24
+
25
+ ##
26
+ # Look up the coordinates of the given street or IP address.
27
+ #
28
+ def self.coordinates(address, options = {})
29
+ if (results = search(address, options)).size > 0
30
+ results.first.coordinates
31
+ end
32
+ end
33
+
34
+ ##
35
+ # Look up the address of the given coordinates ([lat,lon])
36
+ # or IP address (string).
37
+ #
38
+ def self.address(query, options = {})
39
+ if (results = search(query, options)).size > 0
40
+ results.first.address
41
+ end
42
+ end
43
+ end
44
+
45
+ # load Railtie if Rails exists
46
+ if defined?(Rails)
47
+ require "geocoder/railtie"
48
+ end
@@ -0,0 +1,94 @@
1
+ module Geocoder
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
+ if store.respond_to?(:keys)
44
+ urls.each{ |u| expire(u) }
45
+ else
46
+ raise(NoMethodError, "The Geocoder cache store must implement `#keys` for `expire(:all)` to work")
47
+ end
48
+ else
49
+ expire_single_url(url)
50
+ end
51
+ end
52
+
53
+
54
+ private # ----------------------------------------------------------------
55
+
56
+ def prefix; @prefix; end
57
+ def store; @store; end
58
+
59
+ ##
60
+ # Cache key for a given URL.
61
+ #
62
+ def key_for(url)
63
+ [prefix, url].join
64
+ end
65
+
66
+ ##
67
+ # Array of keys with the currently configured prefix
68
+ # that have non-nil values.
69
+ #
70
+ def keys
71
+ store.keys.select{ |k| k.match(/^#{prefix}/) and self[k] }
72
+ end
73
+
74
+ ##
75
+ # Array of cached URLs.
76
+ #
77
+ def urls
78
+ keys.map{ |k| k[/^#{prefix}(.*)/, 1] }
79
+ end
80
+
81
+ ##
82
+ # Clean up value before returning. Namely, convert empty string to nil.
83
+ # (Some key/value stores return empty string instead of nil.)
84
+ #
85
+ def interpret(value)
86
+ value == "" ? nil : value
87
+ end
88
+
89
+ def expire_single_url(url)
90
+ key = key_for(url)
91
+ store.respond_to?(:del) ? store.del(key) : store.delete(key)
92
+ end
93
+ end
94
+ end