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
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'geocoder/configuration_hash'
|
3
|
+
|
4
|
+
module Geocoder
|
5
|
+
|
6
|
+
##
|
7
|
+
# Configuration options should be set by passing a hash:
|
8
|
+
#
|
9
|
+
# Geocoder.configure(
|
10
|
+
# :timeout => 5,
|
11
|
+
# :lookup => :yandex,
|
12
|
+
# :api_key => "2a9fsa983jaslfj982fjasd",
|
13
|
+
# :units => :km
|
14
|
+
# )
|
15
|
+
#
|
16
|
+
def self.configure(options = nil, &block)
|
17
|
+
if !options.nil?
|
18
|
+
Configuration.instance.configure(options)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
##
|
23
|
+
# Read-only access to the singleton's config data.
|
24
|
+
#
|
25
|
+
def self.config
|
26
|
+
Configuration.instance.data
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
# Read-only access to lookup-specific config data.
|
31
|
+
#
|
32
|
+
def self.config_for_lookup(lookup_name)
|
33
|
+
data = config.clone
|
34
|
+
data.reject!{ |key,value| !Configuration::OPTIONS.include?(key) }
|
35
|
+
if config.has_key?(lookup_name)
|
36
|
+
data.merge!(config[lookup_name])
|
37
|
+
end
|
38
|
+
data
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# Merge the given hash into a lookup's existing configuration.
|
43
|
+
#
|
44
|
+
def self.merge_into_lookup_config(lookup_name, options)
|
45
|
+
base = Geocoder.config[lookup_name]
|
46
|
+
Geocoder.configure(lookup_name => base.merge(options))
|
47
|
+
end
|
48
|
+
|
49
|
+
class Configuration
|
50
|
+
include Singleton
|
51
|
+
|
52
|
+
OPTIONS = [
|
53
|
+
:timeout,
|
54
|
+
:lookup,
|
55
|
+
:ip_lookup,
|
56
|
+
:language,
|
57
|
+
:http_headers,
|
58
|
+
:use_https,
|
59
|
+
:http_proxy,
|
60
|
+
:https_proxy,
|
61
|
+
:api_key,
|
62
|
+
:cache,
|
63
|
+
:cache_prefix,
|
64
|
+
:always_raise,
|
65
|
+
:units,
|
66
|
+
:distances,
|
67
|
+
:basic_auth,
|
68
|
+
:logger,
|
69
|
+
:kernel_logger_level
|
70
|
+
]
|
71
|
+
|
72
|
+
attr_accessor :data
|
73
|
+
|
74
|
+
def self.set_defaults
|
75
|
+
instance.set_defaults
|
76
|
+
end
|
77
|
+
|
78
|
+
OPTIONS.each do |o|
|
79
|
+
define_method o do
|
80
|
+
@data[o]
|
81
|
+
end
|
82
|
+
define_method "#{o}=" do |value|
|
83
|
+
@data[o] = value
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def configure(options)
|
88
|
+
@data.rmerge!(options)
|
89
|
+
end
|
90
|
+
|
91
|
+
def initialize # :nodoc
|
92
|
+
@data = Geocoder::ConfigurationHash.new
|
93
|
+
set_defaults
|
94
|
+
end
|
95
|
+
|
96
|
+
def set_defaults
|
97
|
+
|
98
|
+
# geocoding options
|
99
|
+
@data[:timeout] = 3 # geocoding service timeout (secs)
|
100
|
+
@data[:lookup] = :nominatim # name of street address geocoding service (symbol)
|
101
|
+
@data[:ip_lookup] = :ipinfo_io # name of IP address geocoding service (symbol)
|
102
|
+
@data[:language] = :en # ISO-639 language code
|
103
|
+
@data[:http_headers] = {} # HTTP headers for lookup
|
104
|
+
@data[:use_https] = false # use HTTPS for lookup requests? (if supported)
|
105
|
+
@data[:http_proxy] = nil # HTTP proxy server (user:pass@host:port)
|
106
|
+
@data[:https_proxy] = nil # HTTPS proxy server (user:pass@host:port)
|
107
|
+
@data[:api_key] = nil # API key for geocoding service
|
108
|
+
@data[:cache] = nil # cache object (must respond to #[], #[]=, and #keys)
|
109
|
+
@data[:cache_prefix] = "geocoder:" # prefix (string) to use for all cache keys
|
110
|
+
@data[:basic_auth] = {} # user and password for basic auth ({:user => "user", :password => "password"})
|
111
|
+
@data[:logger] = :kernel # :kernel or Logger instance
|
112
|
+
@data[:kernel_logger_level] = ::Logger::WARN # log level, if kernel logger is used
|
113
|
+
|
114
|
+
# exceptions that should not be rescued by default
|
115
|
+
# (if you want to implement custom error handling);
|
116
|
+
# supports SocketError and Timeout::Error
|
117
|
+
@data[:always_raise] = []
|
118
|
+
|
119
|
+
# calculation options
|
120
|
+
@data[:units] = :mi # :mi or :km
|
121
|
+
@data[:distances] = :linear # :linear or :spherical
|
122
|
+
end
|
123
|
+
|
124
|
+
instance_eval(OPTIONS.map do |option|
|
125
|
+
o = option.to_s
|
126
|
+
<<-EOS
|
127
|
+
def #{o}
|
128
|
+
instance.data[:#{o}]
|
129
|
+
end
|
130
|
+
|
131
|
+
def #{o}=(value)
|
132
|
+
instance.data[:#{o}] = value
|
133
|
+
end
|
134
|
+
EOS
|
135
|
+
end.join("\n\n"))
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Geocoder
|
2
|
+
class EsriToken
|
3
|
+
attr_accessor :value, :expires_at
|
4
|
+
|
5
|
+
def initialize(value, expires_at)
|
6
|
+
@value = value
|
7
|
+
@expires_at = expires_at
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
@value
|
12
|
+
end
|
13
|
+
|
14
|
+
def active?
|
15
|
+
@expires_at > Time.now
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.generate_token(client_id, client_secret, expires=1440)
|
19
|
+
# creates a new token that will expire in 1 day by default
|
20
|
+
getToken = Net::HTTP.post_form URI('https://www.arcgis.com/sharing/rest/oauth2/token'),
|
21
|
+
f: 'json',
|
22
|
+
client_id: client_id,
|
23
|
+
client_secret: client_secret,
|
24
|
+
grant_type: 'client_credentials',
|
25
|
+
expiration: expires # (minutes) max: 20160, default: 1 day
|
26
|
+
|
27
|
+
response = JSON.parse(getToken.body)
|
28
|
+
|
29
|
+
if response['error']
|
30
|
+
Geocoder.log(:warn, response['error'])
|
31
|
+
else
|
32
|
+
token_value = response['access_token']
|
33
|
+
expires_at = Time.now + (expires * 60)
|
34
|
+
new(token_value, expires_at)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
|
3
|
+
module Geocoder
|
4
|
+
|
5
|
+
class Error < StandardError
|
6
|
+
end
|
7
|
+
|
8
|
+
class ConfigurationError < Error
|
9
|
+
end
|
10
|
+
|
11
|
+
class OverQueryLimitError < Error
|
12
|
+
end
|
13
|
+
|
14
|
+
class ResponseParseError < Error
|
15
|
+
attr_reader :response
|
16
|
+
|
17
|
+
def initialize(response)
|
18
|
+
@response = response
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class RequestDenied < Error
|
23
|
+
end
|
24
|
+
|
25
|
+
class InvalidRequest < Error
|
26
|
+
end
|
27
|
+
|
28
|
+
class InvalidApiKey < Error
|
29
|
+
end
|
30
|
+
|
31
|
+
class ServiceUnavailable < Error
|
32
|
+
end
|
33
|
+
|
34
|
+
class LookupTimeout < ::Timeout::Error
|
35
|
+
end
|
36
|
+
|
37
|
+
class NetworkError < Error
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'resolv'
|
2
|
+
module Geocoder
|
3
|
+
class IpAddress < String
|
4
|
+
PRIVATE_IPS = [
|
5
|
+
'10.0.0.0/8',
|
6
|
+
'172.16.0.0/12',
|
7
|
+
'192.168.0.0/16',
|
8
|
+
].map { |ip| IPAddr.new(ip) }.freeze
|
9
|
+
|
10
|
+
def internal?
|
11
|
+
loopback? || private?
|
12
|
+
end
|
13
|
+
|
14
|
+
def loopback?
|
15
|
+
valid? and !!(self == "0.0.0.0" or self.match(/\A127\./) or self == "::1")
|
16
|
+
end
|
17
|
+
|
18
|
+
def private?
|
19
|
+
valid? && PRIVATE_IPS.any? { |ip| ip.include?(self) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def valid?
|
23
|
+
!!((self =~ Resolv::IPv4::Regex) || (self =~ Resolv::IPv6::Regex))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Geocoder
|
2
|
+
class KernelLogger
|
3
|
+
include Singleton
|
4
|
+
|
5
|
+
def add(level, message)
|
6
|
+
return unless log_message_at_level?(level)
|
7
|
+
case level
|
8
|
+
when ::Logger::DEBUG, ::Logger::INFO
|
9
|
+
puts message
|
10
|
+
when ::Logger::WARN
|
11
|
+
warn message
|
12
|
+
when ::Logger::ERROR
|
13
|
+
raise message
|
14
|
+
when ::Logger::FATAL
|
15
|
+
fail message
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private # ----------------------------------------------------------------
|
20
|
+
|
21
|
+
def log_message_at_level?(level)
|
22
|
+
level >= Geocoder.config.kernel_logger_level
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Geocoder
|
4
|
+
|
5
|
+
def self.log(level, message)
|
6
|
+
Logger.instance.log(level, message)
|
7
|
+
end
|
8
|
+
|
9
|
+
class Logger
|
10
|
+
include Singleton
|
11
|
+
|
12
|
+
SEVERITY = {
|
13
|
+
debug: ::Logger::DEBUG,
|
14
|
+
info: ::Logger::INFO,
|
15
|
+
warn: ::Logger::WARN,
|
16
|
+
error: ::Logger::ERROR,
|
17
|
+
fatal: ::Logger::FATAL
|
18
|
+
}
|
19
|
+
|
20
|
+
def log(level, message)
|
21
|
+
unless valid_level?(level)
|
22
|
+
raise StandardError, "Geocoder tried to log a message with an invalid log level."
|
23
|
+
end
|
24
|
+
if current_logger.respond_to? :add
|
25
|
+
current_logger.add(SEVERITY[level], message)
|
26
|
+
else
|
27
|
+
raise Geocoder::ConfigurationError, "Please specify valid logger for Geocoder. " +
|
28
|
+
"Logger specified must be :kernel or must respond to `add(level, message)`."
|
29
|
+
end
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
|
33
|
+
private # ----------------------------------------------------------------
|
34
|
+
|
35
|
+
def current_logger
|
36
|
+
logger = Geocoder.config[:logger]
|
37
|
+
if logger == :kernel
|
38
|
+
logger = Geocoder::KernelLogger.instance
|
39
|
+
end
|
40
|
+
logger
|
41
|
+
end
|
42
|
+
|
43
|
+
def valid_level?(level)
|
44
|
+
SEVERITY.keys.include?(level)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require "geocoder/lookups/test"
|
2
|
+
|
3
|
+
module Geocoder
|
4
|
+
module Lookup
|
5
|
+
extend self
|
6
|
+
|
7
|
+
##
|
8
|
+
# Array of valid Lookup service names.
|
9
|
+
#
|
10
|
+
def all_services
|
11
|
+
street_services + ip_services
|
12
|
+
end
|
13
|
+
|
14
|
+
##
|
15
|
+
# Array of valid Lookup service names, excluding :test.
|
16
|
+
#
|
17
|
+
def all_services_except_test
|
18
|
+
all_services - [:test]
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# All street address lookup services, default first.
|
23
|
+
#
|
24
|
+
def street_services
|
25
|
+
@street_services ||= [
|
26
|
+
:location_iq,
|
27
|
+
:dstk,
|
28
|
+
:esri,
|
29
|
+
:google,
|
30
|
+
:google_premier,
|
31
|
+
:google_places_details,
|
32
|
+
:google_places_search,
|
33
|
+
:bing,
|
34
|
+
:geocoder_ca,
|
35
|
+
:geocoder_us,
|
36
|
+
:yandex,
|
37
|
+
:nominatim,
|
38
|
+
:mapbox,
|
39
|
+
:mapquest,
|
40
|
+
:opencagedata,
|
41
|
+
:pelias,
|
42
|
+
:pickpoint,
|
43
|
+
:here,
|
44
|
+
:baidu,
|
45
|
+
:tencent,
|
46
|
+
:geocodio,
|
47
|
+
:smarty_streets,
|
48
|
+
:postcode_anywhere_uk,
|
49
|
+
:postcodes_io,
|
50
|
+
:geoportail_lu,
|
51
|
+
:ban_data_gouv_fr,
|
52
|
+
:test,
|
53
|
+
:latlon,
|
54
|
+
:amap
|
55
|
+
]
|
56
|
+
end
|
57
|
+
|
58
|
+
##
|
59
|
+
# All IP address lookup services, default first.
|
60
|
+
#
|
61
|
+
def ip_services
|
62
|
+
@ip_services ||= [
|
63
|
+
:baidu_ip,
|
64
|
+
:freegeoip,
|
65
|
+
:geoip2,
|
66
|
+
:maxmind,
|
67
|
+
:maxmind_local,
|
68
|
+
:telize,
|
69
|
+
:pointpin,
|
70
|
+
:maxmind_geoip2,
|
71
|
+
:ipinfo_io,
|
72
|
+
:ipapi_com,
|
73
|
+
:ipdata_co,
|
74
|
+
:db_ip_com,
|
75
|
+
:ipstack,
|
76
|
+
:ip2location
|
77
|
+
]
|
78
|
+
end
|
79
|
+
|
80
|
+
attr_writer :street_services, :ip_services
|
81
|
+
|
82
|
+
##
|
83
|
+
# Retrieve a Lookup object from the store.
|
84
|
+
# Use this instead of Geocoder::Lookup::X.new to get an
|
85
|
+
# already-configured Lookup object.
|
86
|
+
#
|
87
|
+
def get(name)
|
88
|
+
@services = {} unless defined?(@services)
|
89
|
+
@services[name] = spawn(name) unless @services.include?(name)
|
90
|
+
@services[name]
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
private # -----------------------------------------------------------------
|
95
|
+
|
96
|
+
##
|
97
|
+
# Spawn a Lookup of the given name.
|
98
|
+
#
|
99
|
+
def spawn(name)
|
100
|
+
if all_services.include?(name)
|
101
|
+
name = name.to_s
|
102
|
+
require "geocoder/lookups/#{name}"
|
103
|
+
Geocoder::Lookup.const_get(classify_name(name)).new
|
104
|
+
else
|
105
|
+
valids = all_services.map(&:inspect).join(", ")
|
106
|
+
raise ConfigurationError, "Please specify a valid lookup for Geocoder " +
|
107
|
+
"(#{name.inspect} is not one of: #{valids})."
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
##
|
112
|
+
# Convert an "underscore" version of a name into a "class" version.
|
113
|
+
#
|
114
|
+
def classify_name(filename)
|
115
|
+
filename.to_s.split("_").map{ |i| i[0...1].upcase + i[1..-1] }.join
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|