broken-geocoder 1.3.4
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 +467 -0
- data/LICENSE +20 -0
- data/README.md +1193 -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 +21 -0
- data/lib/generators/geocoder/maxmind/geolite_city_generator.rb +28 -0
- data/lib/generators/geocoder/maxmind/geolite_country_generator.rb +28 -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/geocoder.rb +48 -0
- data/lib/geocoder/cache.rb +90 -0
- data/lib/geocoder/calculations.rb +431 -0
- data/lib/geocoder/cli.rb +121 -0
- data/lib/geocoder/configuration.rb +129 -0
- data/lib/geocoder/configuration_hash.rb +11 -0
- data/lib/geocoder/esri_token.rb +38 -0
- data/lib/geocoder/exceptions.rb +37 -0
- data/lib/geocoder/ip_address.rb +13 -0
- data/lib/geocoder/kernel_logger.rb +25 -0
- data/lib/geocoder/logger.rb +47 -0
- data/lib/geocoder/lookup.rb +110 -0
- data/lib/geocoder/lookups/baidu.rb +59 -0
- data/lib/geocoder/lookups/baidu_ip.rb +59 -0
- data/lib/geocoder/lookups/base.rb +325 -0
- data/lib/geocoder/lookups/bing.rb +80 -0
- data/lib/geocoder/lookups/dstk.rb +20 -0
- data/lib/geocoder/lookups/esri.rb +64 -0
- data/lib/geocoder/lookups/freegeoip.rb +51 -0
- data/lib/geocoder/lookups/geocoder_ca.rb +53 -0
- data/lib/geocoder/lookups/geocoder_us.rb +43 -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 +91 -0
- data/lib/geocoder/lookups/google_places_details.rb +50 -0
- data/lib/geocoder/lookups/google_premier.rb +47 -0
- data/lib/geocoder/lookups/here.rb +62 -0
- data/lib/geocoder/lookups/ipapi_com.rb +86 -0
- data/lib/geocoder/lookups/ipinfo_io.rb +55 -0
- data/lib/geocoder/lookups/latlon.rb +59 -0
- data/lib/geocoder/lookups/mapbox.rb +53 -0
- data/lib/geocoder/lookups/mapquest.rb +59 -0
- data/lib/geocoder/lookups/mapzen.rb +15 -0
- data/lib/geocoder/lookups/maxmind.rb +90 -0
- data/lib/geocoder/lookups/maxmind_geoip2.rb +69 -0
- data/lib/geocoder/lookups/maxmind_local.rb +65 -0
- data/lib/geocoder/lookups/nominatim.rb +52 -0
- data/lib/geocoder/lookups/okf.rb +44 -0
- data/lib/geocoder/lookups/opencagedata.rb +58 -0
- data/lib/geocoder/lookups/ovi.rb +62 -0
- data/lib/geocoder/lookups/pelias.rb +64 -0
- data/lib/geocoder/lookups/pointpin.rb +68 -0
- data/lib/geocoder/lookups/postcode_anywhere_uk.rb +51 -0
- data/lib/geocoder/lookups/smarty_streets.rb +50 -0
- data/lib/geocoder/lookups/telize.rb +55 -0
- data/lib/geocoder/lookups/test.rb +44 -0
- data/lib/geocoder/lookups/yandex.rb +58 -0
- data/lib/geocoder/models/active_record.rb +50 -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 +111 -0
- data/lib/geocoder/railtie.rb +26 -0
- data/lib/geocoder/request.rb +83 -0
- data/lib/geocoder/results/baidu.rb +79 -0
- data/lib/geocoder/results/baidu_ip.rb +62 -0
- data/lib/geocoder/results/base.rb +67 -0
- data/lib/geocoder/results/bing.rb +52 -0
- data/lib/geocoder/results/dstk.rb +6 -0
- data/lib/geocoder/results/esri.rb +75 -0
- data/lib/geocoder/results/freegeoip.rb +45 -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 +70 -0
- data/lib/geocoder/results/geoip2.rb +62 -0
- data/lib/geocoder/results/geoportail_lu.rb +69 -0
- data/lib/geocoder/results/google.rb +139 -0
- data/lib/geocoder/results/google_places_details.rb +35 -0
- data/lib/geocoder/results/google_premier.rb +6 -0
- data/lib/geocoder/results/here.rb +71 -0
- data/lib/geocoder/results/ipapi_com.rb +45 -0
- data/lib/geocoder/results/ipinfo_io.rb +48 -0
- data/lib/geocoder/results/latlon.rb +71 -0
- data/lib/geocoder/results/mapbox.rb +47 -0
- data/lib/geocoder/results/mapquest.rb +48 -0
- data/lib/geocoder/results/mapzen.rb +5 -0
- data/lib/geocoder/results/maxmind.rb +135 -0
- data/lib/geocoder/results/maxmind_geoip2.rb +9 -0
- data/lib/geocoder/results/maxmind_local.rb +49 -0
- data/lib/geocoder/results/nominatim.rb +99 -0
- data/lib/geocoder/results/okf.rb +106 -0
- data/lib/geocoder/results/opencagedata.rb +90 -0
- data/lib/geocoder/results/ovi.rb +71 -0
- data/lib/geocoder/results/pelias.rb +58 -0
- data/lib/geocoder/results/pointpin.rb +40 -0
- data/lib/geocoder/results/postcode_anywhere_uk.rb +42 -0
- data/lib/geocoder/results/smarty_streets.rb +106 -0
- data/lib/geocoder/results/telize.rb +45 -0
- data/lib/geocoder/results/test.rb +33 -0
- data/lib/geocoder/results/yandex.rb +92 -0
- data/lib/geocoder/sql.rb +107 -0
- data/lib/geocoder/stores/active_record.rb +305 -0
- data/lib/geocoder/stores/base.rb +116 -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 +38 -0
- data/lib/tasks/maxmind.rake +73 -0
- metadata +167 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
module Geocoder
|
|
2
|
+
module Store
|
|
3
|
+
module Base
|
|
4
|
+
|
|
5
|
+
##
|
|
6
|
+
# Is this object geocoded? (Does it have latitude and longitude?)
|
|
7
|
+
#
|
|
8
|
+
def geocoded?
|
|
9
|
+
to_coordinates.compact.size > 0
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
##
|
|
13
|
+
# Coordinates [lat,lon] of the object.
|
|
14
|
+
#
|
|
15
|
+
def to_coordinates
|
|
16
|
+
[:latitude, :longitude].map{ |i| send self.class.geocoder_options[i] }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
##
|
|
20
|
+
# Calculate the distance from the object to an arbitrary point.
|
|
21
|
+
# See Geocoder::Calculations.distance_between for ways of specifying
|
|
22
|
+
# the point. Also takes a symbol specifying the units
|
|
23
|
+
# (:mi or :km; can be specified in Geocoder configuration).
|
|
24
|
+
#
|
|
25
|
+
def distance_to(point, units = nil)
|
|
26
|
+
units ||= self.class.geocoder_options[:units]
|
|
27
|
+
return nil unless geocoded?
|
|
28
|
+
Geocoder::Calculations.distance_between(
|
|
29
|
+
to_coordinates, point, :units => units)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
alias_method :distance_from, :distance_to
|
|
33
|
+
|
|
34
|
+
##
|
|
35
|
+
# Calculate the bearing from the object to another point.
|
|
36
|
+
# See Geocoder::Calculations.distance_between for
|
|
37
|
+
# ways of specifying the point.
|
|
38
|
+
#
|
|
39
|
+
def bearing_to(point, options = {})
|
|
40
|
+
options[:method] ||= self.class.geocoder_options[:method]
|
|
41
|
+
return nil unless geocoded?
|
|
42
|
+
Geocoder::Calculations.bearing_between(
|
|
43
|
+
to_coordinates, point, options)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
##
|
|
47
|
+
# Calculate the bearing from another point to the object.
|
|
48
|
+
# See Geocoder::Calculations.distance_between for
|
|
49
|
+
# ways of specifying the point.
|
|
50
|
+
#
|
|
51
|
+
def bearing_from(point, options = {})
|
|
52
|
+
options[:method] ||= self.class.geocoder_options[:method]
|
|
53
|
+
return nil unless geocoded?
|
|
54
|
+
Geocoder::Calculations.bearing_between(
|
|
55
|
+
point, to_coordinates, options)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
##
|
|
59
|
+
# Look up coordinates and assign to +latitude+ and +longitude+ attributes
|
|
60
|
+
# (or other as specified in +geocoded_by+). Returns coordinates (array).
|
|
61
|
+
#
|
|
62
|
+
def geocode
|
|
63
|
+
fail
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
##
|
|
67
|
+
# Look up address and assign to +address+ attribute (or other as specified
|
|
68
|
+
# in +reverse_geocoded_by+). Returns address (string).
|
|
69
|
+
#
|
|
70
|
+
def reverse_geocode
|
|
71
|
+
fail
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private # --------------------------------------------------------------
|
|
75
|
+
|
|
76
|
+
##
|
|
77
|
+
# Look up geographic data based on object attributes (configured in
|
|
78
|
+
# geocoded_by or reverse_geocoded_by) and handle the results with the
|
|
79
|
+
# block (given to geocoded_by or reverse_geocoded_by). The block is
|
|
80
|
+
# given two-arguments: the object being geocoded and an array of
|
|
81
|
+
# Geocoder::Result objects).
|
|
82
|
+
#
|
|
83
|
+
def do_lookup(reverse = false)
|
|
84
|
+
options = self.class.geocoder_options
|
|
85
|
+
if reverse and options[:reverse_geocode]
|
|
86
|
+
query = to_coordinates
|
|
87
|
+
elsif !reverse and options[:geocode]
|
|
88
|
+
query = send(options[:user_address])
|
|
89
|
+
else
|
|
90
|
+
return
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
query_options = [:lookup, :ip_lookup, :language].inject({}) do |hash, key|
|
|
94
|
+
if options.has_key?(key)
|
|
95
|
+
val = options[key]
|
|
96
|
+
hash[key] = val.respond_to?(:call) ? val.call(self) : val
|
|
97
|
+
end
|
|
98
|
+
hash
|
|
99
|
+
end
|
|
100
|
+
results = Geocoder.search(query, query_options)
|
|
101
|
+
|
|
102
|
+
# execute custom block, if specified in configuration
|
|
103
|
+
block_key = reverse ? :reverse_block : :geocode_block
|
|
104
|
+
if custom_block = options[block_key]
|
|
105
|
+
custom_block.call(self, results)
|
|
106
|
+
|
|
107
|
+
# else execute block passed directly to this method,
|
|
108
|
+
# which generally performs the "auto-assigns"
|
|
109
|
+
elsif block_given?
|
|
110
|
+
yield(self, results)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
module Geocoder::Store
|
|
2
|
+
module MongoBase
|
|
3
|
+
|
|
4
|
+
def self.included_by_model(base)
|
|
5
|
+
base.class_eval do
|
|
6
|
+
|
|
7
|
+
scope :geocoded, lambda {
|
|
8
|
+
where(geocoder_options[:coordinates].ne => nil)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
scope :not_geocoded, lambda {
|
|
12
|
+
where(geocoder_options[:coordinates] => nil)
|
|
13
|
+
}
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
##
|
|
18
|
+
# Coordinates [lat,lon] of the object.
|
|
19
|
+
# This method always returns coordinates in lat,lon order,
|
|
20
|
+
# even though internally they are stored in the opposite order.
|
|
21
|
+
#
|
|
22
|
+
def to_coordinates
|
|
23
|
+
coords = send(self.class.geocoder_options[:coordinates])
|
|
24
|
+
coords.is_a?(Array) ? coords.reverse : []
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
##
|
|
28
|
+
# Look up coordinates and assign to +latitude+ and +longitude+ attributes
|
|
29
|
+
# (or other as specified in +geocoded_by+). Returns coordinates (array).
|
|
30
|
+
#
|
|
31
|
+
def geocode
|
|
32
|
+
do_lookup(false) do |o,rs|
|
|
33
|
+
if r = rs.first
|
|
34
|
+
unless r.coordinates.nil?
|
|
35
|
+
o.__send__ "#{self.class.geocoder_options[:coordinates]}=", r.coordinates.reverse
|
|
36
|
+
end
|
|
37
|
+
r.coordinates
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
##
|
|
43
|
+
# Look up address and assign to +address+ attribute (or other as specified
|
|
44
|
+
# in +reverse_geocoded_by+). Returns address (string).
|
|
45
|
+
#
|
|
46
|
+
def reverse_geocode
|
|
47
|
+
do_lookup(true) do |o,rs|
|
|
48
|
+
if r = rs.first
|
|
49
|
+
unless r.address.nil?
|
|
50
|
+
o.__send__ "#{self.class.geocoder_options[:fetched_address]}=", r.address
|
|
51
|
+
end
|
|
52
|
+
r.address
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#
|
|
2
|
+
# = Hash Recursive Merge
|
|
3
|
+
#
|
|
4
|
+
# Merges a Ruby Hash recursively, Also known as deep merge.
|
|
5
|
+
# Recursive version of Hash#merge and Hash#merge!.
|
|
6
|
+
#
|
|
7
|
+
# Category:: Ruby
|
|
8
|
+
# Package:: Hash
|
|
9
|
+
# Author:: Simone Carletti <weppos@weppos.net>
|
|
10
|
+
# Copyright:: 2007-2008 The Authors
|
|
11
|
+
# License:: MIT License
|
|
12
|
+
# Link:: http://www.simonecarletti.com/
|
|
13
|
+
# Source:: http://gist.github.com/gists/6391/
|
|
14
|
+
#
|
|
15
|
+
module HashRecursiveMerge
|
|
16
|
+
|
|
17
|
+
#
|
|
18
|
+
# Recursive version of Hash#merge!
|
|
19
|
+
#
|
|
20
|
+
# Adds the contents of +other_hash+ to +hsh+,
|
|
21
|
+
# merging entries in +hsh+ with duplicate keys with those from +other_hash+.
|
|
22
|
+
#
|
|
23
|
+
# Compared with Hash#merge!, this method supports nested hashes.
|
|
24
|
+
# When both +hsh+ and +other_hash+ contains an entry with the same key,
|
|
25
|
+
# it merges and returns the values from both arrays.
|
|
26
|
+
#
|
|
27
|
+
# h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}}
|
|
28
|
+
# h2 = {"b" => 254, "c" => {"c1" => 16, "c3" => 94}}
|
|
29
|
+
# h1.rmerge!(h2) #=> {"a" => 100, "b" => 254, "c" => {"c1" => 16, "c2" => 14, "c3" => 94}}
|
|
30
|
+
#
|
|
31
|
+
# Simply using Hash#merge! would return
|
|
32
|
+
#
|
|
33
|
+
# h1.merge!(h2) #=> {"a" => 100, "b" = >254, "c" => {"c1" => 16, "c3" => 94}}
|
|
34
|
+
#
|
|
35
|
+
def rmerge!(other_hash)
|
|
36
|
+
merge!(other_hash) do |key, oldval, newval|
|
|
37
|
+
oldval.class == self.class ? oldval.rmerge!(newval) : newval
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
#
|
|
42
|
+
# Recursive version of Hash#merge
|
|
43
|
+
#
|
|
44
|
+
# Compared with Hash#merge!, this method supports nested hashes.
|
|
45
|
+
# When both +hsh+ and +other_hash+ contains an entry with the same key,
|
|
46
|
+
# it merges and returns the values from both arrays.
|
|
47
|
+
#
|
|
48
|
+
# Compared with Hash#merge, this method provides a different approch
|
|
49
|
+
# for merging nasted hashes.
|
|
50
|
+
# If the value of a given key is an Hash and both +other_hash+ abd +hsh
|
|
51
|
+
# includes the same key, the value is merged instead replaced with
|
|
52
|
+
# +other_hash+ value.
|
|
53
|
+
#
|
|
54
|
+
# h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}}
|
|
55
|
+
# h2 = {"b" => 254, "c" => {"c1" => 16, "c3" => 94}}
|
|
56
|
+
# h1.rmerge(h2) #=> {"a" => 100, "b" => 254, "c" => {"c1" => 16, "c2" => 14, "c3" => 94}}
|
|
57
|
+
#
|
|
58
|
+
# Simply using Hash#merge would return
|
|
59
|
+
#
|
|
60
|
+
# h1.merge(h2) #=> {"a" => 100, "b" = >254, "c" => {"c1" => 16, "c3" => 94}}
|
|
61
|
+
#
|
|
62
|
+
def rmerge(other_hash)
|
|
63
|
+
r = {}
|
|
64
|
+
merge(other_hash) do |key, oldval, newval|
|
|
65
|
+
r[key] = oldval.class == self.class ? oldval.rmerge(newval) : newval
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class Hash
|
|
73
|
+
include HashRecursiveMerge
|
|
74
|
+
end
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
require 'csv'
|
|
2
|
+
require 'net/http'
|
|
3
|
+
|
|
4
|
+
module Geocoder
|
|
5
|
+
module MaxmindDatabase
|
|
6
|
+
extend self
|
|
7
|
+
|
|
8
|
+
def download(package, dir = "tmp")
|
|
9
|
+
filepath = File.expand_path(File.join(dir, archive_filename(package)))
|
|
10
|
+
open(filepath, 'wb') do |file|
|
|
11
|
+
uri = URI.parse(archive_url(package))
|
|
12
|
+
Net::HTTP.start(uri.host, uri.port) do |http|
|
|
13
|
+
http.request_get(uri.path) do |resp|
|
|
14
|
+
# TODO: show progress
|
|
15
|
+
resp.read_body do |segment|
|
|
16
|
+
file.write(segment)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def insert(package, dir = "tmp")
|
|
24
|
+
data_files(package, dir).each do |filepath,table|
|
|
25
|
+
print "Resetting table #{table}..."
|
|
26
|
+
ActiveRecord::Base.connection.execute("DELETE FROM #{table}")
|
|
27
|
+
puts "done"
|
|
28
|
+
insert_into_table(table, filepath)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def archive_filename(package)
|
|
33
|
+
p = archive_url_path(package)
|
|
34
|
+
s = !(pos = p.rindex('/')).nil? && pos + 1 || 0
|
|
35
|
+
p[s..-1]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private # -------------------------------------------------------------
|
|
39
|
+
|
|
40
|
+
def table_columns(table_name)
|
|
41
|
+
{
|
|
42
|
+
maxmind_geolite_city_blocks: %w[start_ip_num end_ip_num loc_id],
|
|
43
|
+
maxmind_geolite_city_location: %w[loc_id country region city postal_code latitude longitude metro_code area_code],
|
|
44
|
+
maxmind_geolite_country: %w[start_ip end_ip start_ip_num end_ip_num country_code country]
|
|
45
|
+
}[table_name.to_sym]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def insert_into_table(table, filepath)
|
|
49
|
+
start_time = Time.now
|
|
50
|
+
print "Loading data for table #{table}"
|
|
51
|
+
rows = []
|
|
52
|
+
columns = table_columns(table)
|
|
53
|
+
CSV.foreach(filepath, encoding: "ISO-8859-1") do |line|
|
|
54
|
+
# Some files have header rows.
|
|
55
|
+
# skip if starts with "Copyright" or "locId" or "startIpNum"
|
|
56
|
+
next if line.first.match(/[A-z]/)
|
|
57
|
+
rows << line.to_a
|
|
58
|
+
if rows.size == 10000
|
|
59
|
+
insert_rows(table, columns, rows)
|
|
60
|
+
rows = []
|
|
61
|
+
print "."
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
insert_rows(table, columns, rows) if rows.size > 0
|
|
65
|
+
puts "done (#{Time.now - start_time} seconds)"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def insert_rows(table, headers, rows)
|
|
69
|
+
value_strings = rows.map do |row|
|
|
70
|
+
"(" + row.map{ |col| sql_escaped_value(col) }.join(',') + ")"
|
|
71
|
+
end
|
|
72
|
+
q = "INSERT INTO #{table} (#{headers.join(',')}) " +
|
|
73
|
+
"VALUES #{value_strings.join(',')}"
|
|
74
|
+
ActiveRecord::Base.connection.execute(q)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def sql_escaped_value(value)
|
|
78
|
+
value.to_i.to_s == value ? value :
|
|
79
|
+
ActiveRecord::Base.connection.quote(value)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def data_files(package, dir = "tmp")
|
|
83
|
+
case package
|
|
84
|
+
when :geolite_city_csv
|
|
85
|
+
# use the last two in case multiple versions exist
|
|
86
|
+
files = Dir.glob(File.join(dir, "GeoLiteCity_*/*.csv"))[-2..-1].sort
|
|
87
|
+
Hash[*files.zip(["maxmind_geolite_city_blocks", "maxmind_geolite_city_location"]).flatten]
|
|
88
|
+
when :geolite_country_csv
|
|
89
|
+
{File.join(dir, "GeoIPCountryWhois.csv") => "maxmind_geolite_country"}
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def archive_url(package)
|
|
94
|
+
base_url + archive_url_path(package)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def archive_url_path(package)
|
|
98
|
+
{
|
|
99
|
+
geolite_country_csv: "GeoIPCountryCSV.zip",
|
|
100
|
+
geolite_city_csv: "GeoLiteCity_CSV/GeoLiteCity-latest.zip",
|
|
101
|
+
geolite_asn_csv: "asnum/GeoIPASNum2.zip"
|
|
102
|
+
}[package]
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def base_url
|
|
106
|
+
"http://geolite.maxmind.com/download/geoip/database/"
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
namespace :geocode do
|
|
2
|
+
desc "Geocode all objects without coordinates."
|
|
3
|
+
task :all => :environment do
|
|
4
|
+
class_name = ENV['CLASS'] || ENV['class']
|
|
5
|
+
sleep_timer = ENV['SLEEP'] || ENV['sleep']
|
|
6
|
+
batch = ENV['BATCH'] || ENV['batch']
|
|
7
|
+
reverse = ENV['REVERSE'] || ENV['reverse']
|
|
8
|
+
raise "Please specify a CLASS (model)" unless class_name
|
|
9
|
+
klass = class_from_string(class_name)
|
|
10
|
+
batch = batch.to_i unless batch.nil?
|
|
11
|
+
reverse = false unless reverse.to_s.downcase == 'true'
|
|
12
|
+
|
|
13
|
+
if reverse
|
|
14
|
+
klass.not_reverse_geocoded.find_each(batch_size: batch) do |obj|
|
|
15
|
+
obj.reverse_geocode; obj.save
|
|
16
|
+
sleep(sleep_timer.to_f) unless sleep_timer.nil?
|
|
17
|
+
end
|
|
18
|
+
else
|
|
19
|
+
klass.not_geocoded.find_each(batch_size: batch) do |obj|
|
|
20
|
+
obj.geocode; obj.save
|
|
21
|
+
sleep(sleep_timer.to_f) unless sleep_timer.nil?
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
##
|
|
28
|
+
# Get a class object from the string given in the shell environment.
|
|
29
|
+
# Similar to ActiveSupport's +constantize+ method.
|
|
30
|
+
#
|
|
31
|
+
def class_from_string(class_name)
|
|
32
|
+
parts = class_name.split("::")
|
|
33
|
+
constant = Object
|
|
34
|
+
parts.each do |part|
|
|
35
|
+
constant = constant.const_get(part)
|
|
36
|
+
end
|
|
37
|
+
constant
|
|
38
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
require 'maxmind_database'
|
|
2
|
+
|
|
3
|
+
namespace :geocoder do
|
|
4
|
+
namespace :maxmind do
|
|
5
|
+
namespace :geolite do
|
|
6
|
+
|
|
7
|
+
desc "Download and load/refresh MaxMind GeoLite City data"
|
|
8
|
+
task load: [:download, :extract, :insert]
|
|
9
|
+
|
|
10
|
+
desc "Download MaxMind GeoLite City data"
|
|
11
|
+
task :download do
|
|
12
|
+
p = MaxmindTask.check_for_package!
|
|
13
|
+
MaxmindTask.download!(p, dir: ENV['DIR'] || "tmp/")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
desc "Extract (unzip) MaxMind GeoLite City data"
|
|
17
|
+
task :extract do
|
|
18
|
+
p = MaxmindTask.check_for_package!
|
|
19
|
+
MaxmindTask.extract!(p, dir: ENV['DIR'] || "tmp/")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
desc "Load/refresh MaxMind GeoLite City data"
|
|
23
|
+
task insert: [:environment] do
|
|
24
|
+
p = MaxmindTask.check_for_package!
|
|
25
|
+
MaxmindTask.insert!(p, dir: ENV['DIR'] || "tmp/")
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
module MaxmindTask
|
|
32
|
+
extend self
|
|
33
|
+
|
|
34
|
+
def check_for_package!
|
|
35
|
+
if %w[city country].include?(p = ENV['PACKAGE'])
|
|
36
|
+
return p
|
|
37
|
+
else
|
|
38
|
+
puts "Please specify PACKAGE=city or PACKAGE=country"
|
|
39
|
+
exit
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def download!(package, options = {})
|
|
44
|
+
p = "geolite_#{package}_csv".intern
|
|
45
|
+
Geocoder::MaxmindDatabase.download(p, options[:dir])
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def extract!(package, options = {})
|
|
49
|
+
begin
|
|
50
|
+
require 'zip'
|
|
51
|
+
rescue LoadError
|
|
52
|
+
puts "Please install gem: rubyzip (>= 1.0.0)"
|
|
53
|
+
exit
|
|
54
|
+
end
|
|
55
|
+
require 'fileutils'
|
|
56
|
+
p = "geolite_#{package}_csv".intern
|
|
57
|
+
archive_filename = Geocoder::MaxmindDatabase.archive_filename(p)
|
|
58
|
+
Zip::File.open(File.join(options[:dir], archive_filename)).each do |entry|
|
|
59
|
+
filepath = File.join(options[:dir], entry.name)
|
|
60
|
+
if File.exist? filepath
|
|
61
|
+
warn "File already exists (#{entry.name}), skipping"
|
|
62
|
+
else
|
|
63
|
+
FileUtils.mkdir_p(File.dirname(filepath))
|
|
64
|
+
entry.extract(filepath)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def insert!(package, options = {})
|
|
70
|
+
p = "geolite_#{package}_csv".intern
|
|
71
|
+
Geocoder::MaxmindDatabase.insert(p, options[:dir])
|
|
72
|
+
end
|
|
73
|
+
end
|