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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +557 -0
- data/LICENSE +20 -0
- data/README.md +3 -0
- data/bin/geocode +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/examples/reverse_geocode_job.rb +40 -0
- data/lib/generators/geocoder/config/config_generator.rb +14 -0
- data/lib/generators/geocoder/config/templates/initializer.rb +22 -0
- data/lib/generators/geocoder/maxmind/geolite_city_generator.rb +30 -0
- data/lib/generators/geocoder/maxmind/geolite_country_generator.rb +30 -0
- data/lib/generators/geocoder/maxmind/templates/migration/geolite_city.rb +30 -0
- data/lib/generators/geocoder/maxmind/templates/migration/geolite_country.rb +17 -0
- data/lib/generators/geocoder/migration_version.rb +15 -0
- data/lib/geocoder.rb +48 -0
- data/lib/geocoder/cache.rb +94 -0
- data/lib/geocoder/calculations.rb +420 -0
- data/lib/geocoder/cli.rb +121 -0
- data/lib/geocoder/configuration.rb +137 -0
- data/lib/geocoder/configuration_hash.rb +11 -0
- data/lib/geocoder/esri_token.rb +38 -0
- data/lib/geocoder/exceptions.rb +40 -0
- data/lib/geocoder/ip_address.rb +26 -0
- data/lib/geocoder/kernel_logger.rb +25 -0
- data/lib/geocoder/logger.rb +47 -0
- data/lib/geocoder/lookup.rb +118 -0
- data/lib/geocoder/lookups/amap.rb +63 -0
- data/lib/geocoder/lookups/baidu.rb +63 -0
- data/lib/geocoder/lookups/baidu_ip.rb +30 -0
- data/lib/geocoder/lookups/ban_data_gouv_fr.rb +130 -0
- data/lib/geocoder/lookups/base.rb +348 -0
- data/lib/geocoder/lookups/bing.rb +82 -0
- data/lib/geocoder/lookups/db_ip_com.rb +52 -0
- data/lib/geocoder/lookups/dstk.rb +22 -0
- data/lib/geocoder/lookups/esri.rb +95 -0
- data/lib/geocoder/lookups/freegeoip.rb +60 -0
- data/lib/geocoder/lookups/geocoder_ca.rb +53 -0
- data/lib/geocoder/lookups/geocoder_us.rb +51 -0
- data/lib/geocoder/lookups/geocodio.rb +42 -0
- data/lib/geocoder/lookups/geoip2.rb +45 -0
- data/lib/geocoder/lookups/geoportail_lu.rb +65 -0
- data/lib/geocoder/lookups/google.rb +95 -0
- data/lib/geocoder/lookups/google_places_details.rb +50 -0
- data/lib/geocoder/lookups/google_places_search.rb +33 -0
- data/lib/geocoder/lookups/google_premier.rb +57 -0
- data/lib/geocoder/lookups/here.rb +77 -0
- data/lib/geocoder/lookups/ip2location.rb +75 -0
- data/lib/geocoder/lookups/ipapi_com.rb +82 -0
- data/lib/geocoder/lookups/ipdata_co.rb +62 -0
- data/lib/geocoder/lookups/ipinfo_io.rb +44 -0
- data/lib/geocoder/lookups/ipstack.rb +63 -0
- data/lib/geocoder/lookups/latlon.rb +59 -0
- data/lib/geocoder/lookups/location_iq.rb +50 -0
- data/lib/geocoder/lookups/mapbox.rb +59 -0
- data/lib/geocoder/lookups/mapquest.rb +58 -0
- data/lib/geocoder/lookups/maxmind.rb +90 -0
- data/lib/geocoder/lookups/maxmind_geoip2.rb +70 -0
- data/lib/geocoder/lookups/maxmind_local.rb +65 -0
- data/lib/geocoder/lookups/nominatim.rb +64 -0
- data/lib/geocoder/lookups/opencagedata.rb +65 -0
- data/lib/geocoder/lookups/pelias.rb +63 -0
- data/lib/geocoder/lookups/pickpoint.rb +41 -0
- data/lib/geocoder/lookups/pointpin.rb +69 -0
- data/lib/geocoder/lookups/postcode_anywhere_uk.rb +50 -0
- data/lib/geocoder/lookups/postcodes_io.rb +31 -0
- data/lib/geocoder/lookups/smarty_streets.rb +63 -0
- data/lib/geocoder/lookups/telize.rb +75 -0
- data/lib/geocoder/lookups/tencent.rb +59 -0
- data/lib/geocoder/lookups/test.rb +44 -0
- data/lib/geocoder/lookups/yandex.rb +62 -0
- data/lib/geocoder/models/active_record.rb +51 -0
- data/lib/geocoder/models/base.rb +39 -0
- data/lib/geocoder/models/mongo_base.rb +62 -0
- data/lib/geocoder/models/mongo_mapper.rb +26 -0
- data/lib/geocoder/models/mongoid.rb +32 -0
- data/lib/geocoder/query.rb +125 -0
- data/lib/geocoder/railtie.rb +26 -0
- data/lib/geocoder/request.rb +114 -0
- data/lib/geocoder/results/amap.rb +87 -0
- data/lib/geocoder/results/baidu.rb +79 -0
- data/lib/geocoder/results/baidu_ip.rb +62 -0
- data/lib/geocoder/results/ban_data_gouv_fr.rb +257 -0
- data/lib/geocoder/results/base.rb +79 -0
- data/lib/geocoder/results/bing.rb +52 -0
- data/lib/geocoder/results/db_ip_com.rb +58 -0
- data/lib/geocoder/results/dstk.rb +6 -0
- data/lib/geocoder/results/esri.rb +75 -0
- data/lib/geocoder/results/freegeoip.rb +40 -0
- data/lib/geocoder/results/geocoder_ca.rb +60 -0
- data/lib/geocoder/results/geocoder_us.rb +39 -0
- data/lib/geocoder/results/geocodio.rb +78 -0
- data/lib/geocoder/results/geoip2.rb +76 -0
- data/lib/geocoder/results/geoportail_lu.rb +71 -0
- data/lib/geocoder/results/google.rb +150 -0
- data/lib/geocoder/results/google_places_details.rb +39 -0
- data/lib/geocoder/results/google_places_search.rb +52 -0
- data/lib/geocoder/results/google_premier.rb +6 -0
- data/lib/geocoder/results/here.rb +79 -0
- data/lib/geocoder/results/ip2location.rb +22 -0
- data/lib/geocoder/results/ipapi_com.rb +45 -0
- data/lib/geocoder/results/ipdata_co.rb +40 -0
- data/lib/geocoder/results/ipinfo_io.rb +48 -0
- data/lib/geocoder/results/ipstack.rb +60 -0
- data/lib/geocoder/results/latlon.rb +71 -0
- data/lib/geocoder/results/location_iq.rb +6 -0
- data/lib/geocoder/results/mapbox.rb +57 -0
- data/lib/geocoder/results/mapquest.rb +48 -0
- data/lib/geocoder/results/maxmind.rb +130 -0
- data/lib/geocoder/results/maxmind_geoip2.rb +9 -0
- data/lib/geocoder/results/maxmind_local.rb +44 -0
- data/lib/geocoder/results/nominatim.rb +109 -0
- data/lib/geocoder/results/opencagedata.rb +100 -0
- data/lib/geocoder/results/pelias.rb +58 -0
- data/lib/geocoder/results/pickpoint.rb +6 -0
- data/lib/geocoder/results/pointpin.rb +40 -0
- data/lib/geocoder/results/postcode_anywhere_uk.rb +42 -0
- data/lib/geocoder/results/postcodes_io.rb +40 -0
- data/lib/geocoder/results/smarty_streets.rb +142 -0
- data/lib/geocoder/results/telize.rb +40 -0
- data/lib/geocoder/results/tencent.rb +72 -0
- data/lib/geocoder/results/test.rb +33 -0
- data/lib/geocoder/results/yandex.rb +134 -0
- data/lib/geocoder/sql.rb +110 -0
- data/lib/geocoder/stores/active_record.rb +328 -0
- data/lib/geocoder/stores/base.rb +115 -0
- data/lib/geocoder/stores/mongo_base.rb +58 -0
- data/lib/geocoder/stores/mongo_mapper.rb +13 -0
- data/lib/geocoder/stores/mongoid.rb +13 -0
- data/lib/geocoder/version.rb +3 -0
- data/lib/hash_recursive_merge.rb +74 -0
- data/lib/maxmind_database.rb +109 -0
- data/lib/tasks/geocoder.rake +54 -0
- data/lib/tasks/maxmind.rake +73 -0
- 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
data/bin/geocode
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 = '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
|
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
|