Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Change preferred config read/write syntax.

Make Geocoder.configure accept a hash (now preferred) and add
Geocoder.config method (for reading config). Deprecate old syntax.
commit a0448d4c7498dd76deb3b74494972481f5d1c4f9 1 parent 4c9d28a
Alex Reisner authored
32 README.md
View
@@ -266,29 +266,29 @@ Geocoding Services
By default Geocoder uses Google's geocoding API to fetch coordinates and street addresses (FreeGeoIP is used for IP address info). However there are several other APIs supported, as well as a variety of settings. Please see the listing and comparison below for details on specific geocoding services (not all settings are supported by all services). Some common configuration options are:
# config/initializers/geocoder.rb
- Geocoder.configure do |config|
+ Geocoder.configure(
# geocoding service (see below for supported options):
- config.lookup = :yandex
+ :lookup => :yandex,
# to use an API key:
- config.api_key = "..."
+ :api_key => "...",
# geocoding service request timeout, in seconds (default 3):
- config.timeout = 5
+ :timeout => 5,
# set default units to kilometers:
- config.units = :km
+ :units => :km,
# caching (see below for details):
- config.cache = Redis.new
- config.cache_prefix = "..."
+ :cache => Redis.new,
+ :cache_prefix => "..."
end
Please see lib/geocoder/configuration.rb for a complete list of configuration options. Additionally, some lookups have their own configuration options which are listed in the comparison chart below, and as of version 1.2.0 you can pass arbitrary parameters to any geocoding service. For example, to use Nominatim's `countrycodes` parameter:
- Geocoder::Configuration.lookup = :nominatim
+ Geocoder.configure(:lookup => :nominatim)
Geocoder.search("Paris", :params => {:countrycodes => "gb,de,fr,es,us"})
@@ -308,13 +308,13 @@ The following is a comparison of the supported geocoding APIs. The "Limitations"
* **Documentation**: http://code.google.com/apis/maps/documentation/geocoding/#JSON
* **Terms of Service**: http://code.google.com/apis/maps/terms.html#section_10_12
* **Limitations**: "You must not use or display the Content without a corresponding Google map, unless you are explicitly permitted to do so in the Maps APIs Documentation, or through written permission from Google." "You must not pre-fetch, cache, or store any Content, except that you may store: (i) limited amounts of Content for the purpose of improving the performance of your Maps API Implementation..."
-* **Notes**: To use Google Premier set `Geocoder::Configuration.lookup = :google_premier` and `Geocoder::Configuration.api_key = [key, client, channel]`.
+* **Notes**: To use Google Premier set `Geocoder.configure(:lookup => :google_premier, :api_key => [key, client, channel])`.
#### Yahoo BOSS (`:yahoo`)
Yahoo BOSS is **not a free service**. As of November 17, 2012 Yahoo no longer offers a free geocoding API.
-* **API key**: requires OAuth consumer key and secret (set `Geocoder::Configuration.api_key = [key, secret]`)
+* **API key**: requires OAuth consumer key and secret (set `Geocoder.configure(:api_key => [key, secret])`)
* **Key signup**: http://developer.yahoo.com/boss/geo/
* **Quota**: unlimited, but subject to usage fees
* **Region**: world
@@ -374,7 +374,7 @@ Yahoo BOSS is **not a free service**. As of November 17, 2012 Yahoo no longer of
* **API key**: required for the licensed API, do not use for open tier
* **Quota**: ?
* **HTTP Headers**: in order to use the licensed API you can configure the http_headers to include a referer as so:
- `Geocoder::Configuration.http_headers = { "Referer" => "http://foo.com" }`
+ `Geocoder.configure(:http_headers => { "Referer" => "http://foo.com" })`
You can also allow a blank referer from the API management console via mapquest but it is potentially a security risk that someone else could use your API key from another domain.
* **Region**: world
* **SSL support**: no
@@ -400,7 +400,7 @@ Caching
It's a good idea, when relying on any external service, to cache retrieved data. When implemented correctly it improves your app's response time and stability. It's easy to cache geocoding results with Geocoder, just configure a cache store:
- Geocoder::Configuration.cache = Redis.new
+ Geocoder.configure(:cache => Redis.new)
This example uses Redis, but the cache store can be any object that supports these methods:
@@ -413,7 +413,7 @@ Even a plain Ruby hash will work, though it's not a great choice (cleared out wh
You can also set a custom prefix to be used for cache keys:
- Geocoder::Configuration.cache_prefix = "..."
+ Geocoder.configure(:cache_prefix => "...")
By default the prefix is `geocoder:`
@@ -477,7 +477,7 @@ Testing Apps that Use Geocoder
When writing tests for an app that uses Geocoder it may be useful to avoid network calls and have Geocoder return consistent, configurable results. To do this, configure and use the `:test` lookup. For example:
- Geocoder::Configuration.lookup = :test
+ Geocoder.configure(:lookup => :test)
Geocoder::Lookup::Test.add_stub(
"New York, NY", [
@@ -583,11 +583,11 @@ Error Handling
By default Geocoder will rescue any exceptions raised by calls to the geocoding service and return an empty array (using warn() to inform you of the error). You can override this and implement custom error handling for certain exceptions by using the `:always_raise` option:
- Geocoder::Configuration.always_raise = [SocketError, TimeoutError]
+ Geocoder.configure(:always_raise => [SocketError, TimeoutError])
You can also do this to raise all exceptions:
- Geocoder::Configuration.always_raise = :all
+ Geocoder.configure(:always_raise => :all)
See `lib/geocoder/exceptions.rb` for a list of raise-able exceptions.
4 examples/autoexpire_cache.rb
View
@@ -25,6 +25,4 @@ def del(url)
end
end
-Geocoder.configure do |config|
- config.cache = AutoexpireCache.new(Redis.new)
-end
+Geocoder.configure(:cache => AutoexpireCache.new(Redis.new))
40 lib/generators/geocoder/config/templates/initializer.rb
View
@@ -1,25 +1,21 @@
-Geocoder.configure do |config|
- ## Configurable parameters: if you wish to change some configurable
- ## behaviour in Geocoder, feel free to uncomment the following lines
- ## and provide custom parameters.
+Geocoder.configure(
+ # geocoding options
+ # :timeout => 3, # geocoding service timeout (secs)
+ # :lookup => :google, # name of geocoding service (symbol)
+ # :language => :en, # ISO-639 language code
+ # :use_https => false, # use HTTPS for lookup requests? (if supported)
+ # :http_proxy => nil, # HTTP proxy server (user:pass@host:port)
+ # :https_proxy => nil, # HTTPS proxy server (user:pass@host:port)
+ # :api_key => nil, # API key for geocoding service
+ # :cache => nil, # cache object (must respond to #[], #[]=, and #keys)
+ # :cache_prefix => "geocoder:", # prefix (string) to use for all cache keys
- # config.timeout = 3 # geocoding service timeout (secs)
- # config.lookup = :google # name of geocoding service (symbol)
- # config.language = :en # ISO-639 language code
- # config.use_https = false # use HTTPS for lookup requests? (if supported)
- # config.http_proxy = nil # HTTP proxy server (user:pass@host:port)
- # config.https_proxy = nil # HTTPS proxy server (user:pass@host:port)
- # config.api_key = nil # API key for geocoding service
- # config.cache = nil # cache object (must respond to #[], #[]=, and #keys)
- # config.cache_prefix = "geocoder:" # prefix (string) to use for all cache keys
+ # exceptions that should not be rescued by default
+ # (if you want to implement custom error handling);
+ # supports SocketError and TimeoutError
+ # :always_raise => [],
- ## exceptions that should not be rescued by default
- ## (if you want to implement custom error handling);
- ## supports SocketError and TimeoutError
- # config.always_raise = []
-
- ## Calculation options
- # config.units = :mi # :km for kilometers or :mi for miles
- # config.distances = :linear # :spherical or :linear
+ # calculation options
+ # :units => :mi, # :km for kilometers or :mi for miles
+ # :distances => :linear # :spherical or :linear
end
-
24 lib/geocoder/calculations.rb
View
@@ -40,7 +40,7 @@ def coordinates_present?(*args)
# Distance spanned by one degree of latitude in the given units.
#
def latitude_degree_distance(units = nil)
- units ||= Geocoder::Configuration.units
+ units ||= Geocoder.config.units
2 * Math::PI * earth_radius(units) / 360
end
@@ -49,7 +49,7 @@ def latitude_degree_distance(units = nil)
# This ranges from around 69 miles at the equator to zero at the poles.
#
def longitude_degree_distance(latitude, units = nil)
- units ||= Geocoder::Configuration.units
+ units ||= Geocoder.config.units
latitude_degree_distance(units) * Math.cos(to_radians(latitude))
end
@@ -67,12 +67,12 @@ def longitude_degree_distance(latitude, units = nil)
# The options hash supports:
#
# * <tt>:units</tt> - <tt>:mi</tt> or <tt>:km</tt>
- # See Geocoder::Configuration to know how configure default units.
+ # Use Geocoder.configure(:units => ...) to configure default units.
#
def distance_between(point1, point2, options = {})
# set default options
- options[:units] ||= Geocoder::Configuration.units
+ options[:units] ||= Geocoder.config.units
# convert to coordinate arrays
point1 = extract_coordinates(point1)
@@ -103,14 +103,14 @@ def distance_between(point1, point2, options = {})
# the spherical method is "correct" in that it returns the shortest path
# (one along a great circle) but the linear method is less confusing
# (returns due east or west when given two points with the same latitude).
- # See Geocoder::Configuration to know how configure default method.
+ # Use Geocoder.configure(:distances => ...) to configure calculation method.
#
# Based on: http://www.movable-type.co.uk/scripts/latlong.html
#
def bearing_between(point1, point2, options = {})
# set default options
- options[:method] ||= Geocoder::Configuration.distances
+ options[:method] ||= Geocoder.config.distances
options[:method] = :linear unless options[:method] == :spherical
# convert to coordinate arrays
@@ -197,12 +197,12 @@ def geographic_center(points)
# ways of specifying the point. Also accepts an options hash:
#
# * <tt>:units</tt> - <tt>:mi</tt> or <tt>:km</tt>.
- # See Geocoder::Configuration to know how configure default units.
+ # Use Geocoder.configure(:units => ...) to configure default units.
#
def bounding_box(point, radius, options = {})
lat,lon = extract_coordinates(point)
radius = radius.to_f
- units = options[:units] || Geocoder::Configuration.units
+ units = options[:units] || Geocoder.config.units
[
lat - (radius / latitude_degree_distance(units)),
lon - (radius / longitude_degree_distance(lat, units)),
@@ -240,12 +240,12 @@ def to_degrees(*args)
end
def distance_to_radians(distance, units = nil)
- units ||= Geocoder::Configuration.units
+ units ||= Geocoder.config.units
distance.to_f / earth_radius(units)
end
def radians_to_distance(radians, units = nil)
- units ||= Geocoder::Configuration.units
+ units ||= Geocoder.config.units
radians * earth_radius(units)
end
@@ -265,10 +265,10 @@ def to_miles(km)
##
# Radius of the Earth in the given units (:mi or :km).
- # See Geocoder::Configuration to know how configure default units.
+ # Use Geocoder.configure(:units => ...) to configure default units.
#
def earth_radius(units = nil)
- units ||= Geocoder::Configuration.units
+ units ||= Geocoder.config.units
units == :km ? EARTH_RADIUS : to_miles(EARTH_RADIUS)
end
14 lib/geocoder/cli.rb
View
@@ -16,31 +16,31 @@ def self.run(args, out = STDOUT)
"Key for geocoding API (optional for most). For Google Premier use 'key client channel' separated by spaces") do |key|
premier_key = key.split(' ')
if premier_key.length == 3
- Geocoder::Configuration.api_key = premier_key
+ Geocoder.configure(:api_key => premier_key)
else
- Geocoder::Configuration.api_key = key
+ Geocoder.configure(:api_key => key)
end
end
opts.on("-l <language>", "--language <language>",
"Language of output (see API docs for valid choices)") do |language|
- Geocoder::Configuration.language = language
+ Geocoder.configure(:language => language)
end
opts.on("-p <proxy>", "--proxy <proxy>",
"HTTP proxy server to use (user:pass@host:port)") do |proxy|
- Geocoder::Configuration.http_proxy = proxy
+ Geocoder.configure(:http_proxy => proxy)
end
opts.on("-s <service>", Geocoder::Lookup.all_services_except_test, "--service <service>",
"Geocoding service: #{Geocoder::Lookup.all_services_except_test * ', '}") do |service|
- Geocoder::Configuration.lookup = service.to_sym
- Geocoder::Configuration.ip_lookup = service.to_sym
+ Geocoder.configure(:lookup => service.to_sym)
+ Geocoder.configure(:ip_lookup => service.to_sym)
end
opts.on("-t <seconds>", "--timeout <seconds>",
"Maximum number of seconds to wait for API response") do |timeout|
- Geocoder::Configuration.timeout = timeout.to_i
+ Geocoder.configure(:timeout => timeout.to_i)
end
opts.on("-j", "--json", "Print API's raw JSON response") do
111 lib/geocoder/configuration.rb
View
@@ -1,66 +1,52 @@
require 'singleton'
+require 'geocoder/configuration_hash'
module Geocoder
##
# Provides convenient access to the Configuration singleton.
#
- def self.configure(&block)
+ def self.configure(options = nil, &block)
if block_given?
+ warn "WARNING: Passing a block to Geocoder.configure is DEPRECATED. Please pass a hash instead (eg: Geocoder.configure(:units => ..., :api_key => ...))."
block.call(Configuration.instance)
+ elsif !options.nil?
+ Configuration.instance.configure(options)
else
+ warn "WARNING: Use of Geocoder.configure to read or write single config options is DEPRECATED. To write to the config please pass a hash (eg: Geocoder.configure(:units => ...)). To read config options please use the Geocoder.config object (eg: Geocoder.config.units)."
Configuration.instance
end
end
##
- # Direct read access to the singleton's config data.
+ # Read-only access to the singleton's config data.
#
def self.config
Configuration.instance.data
end
##
- # Direct write access to the singleton's config data.
+ # Read-only access to lookup-specific config data.
#
- def self.config=(value)
- Configuration.instance.data = value
- end
-
def self.config_for_lookup(lookup_name)
- data = config.select{ |key,value| Configuration::OPTIONS.include? key }
+ data = config.clone
+ data.select!{ |key,value| Configuration::OPTIONS.include? key }
if config.has_key?(lookup_name)
data.merge!(config[lookup_name])
end
- # allow method-like access
- data.instance_eval do
- def method_missing(meth, *args, &block)
- has_key?(meth) ? self[meth] : super
- end
- end
data
end
##
- # This class handles geocoder Geocoder configuration
- # (geocoding service provider, caching, units of measurement, etc).
- # Configuration can be done in two ways:
- #
- # 1) Using Geocoder.configure and passing a block
- # (useful for configuring multiple things at once):
- #
- # Geocoder.configure do |config|
- # config.timeout = 5
- # config.lookup = :yandex
- # config.api_key = "2a9fsa983jaslfj982fjasd"
- # config.units = :km
- # end
- #
- # 2) Using the Geocoder::Configuration singleton directly:
- #
- # Geocoder::Configuration.language = 'pt-BR'
+ # Configuration options should be set by passing a hash to
+ # the configure method:
#
- # Default values are defined in Configuration#set_defaults.
+ # Geocoder.configure(
+ # :timeout => 5,
+ # :lookup => :yandex,
+ # :api_key => "2a9fsa983jaslfj982fjasd",
+ # :units => :km
+ # )
#
class Configuration
include Singleton
@@ -84,6 +70,10 @@ class Configuration
attr_accessor :data
+ def self.set_defaults
+ instance.set_defaults
+ end
+
OPTIONS.each do |o|
define_method o do
@data[o]
@@ -93,35 +83,38 @@ class Configuration
end
end
+ def configure(options)
+ @data.rmerge!(options)
+ end
+
def initialize # :nodoc
- @data = {}
+ @data = Geocoder::ConfigurationHash.new
set_defaults
end
def set_defaults
- @data = {
- # geocoding options
- :timeout => 3, # geocoding service timeout (secs)
- :lookup => :google, # name of street address geocoding service (symbol)
- :ip_lookup => :freegeoip, # name of IP address geocoding service (symbol)
- :language => :en, # ISO-639 language code
- :http_headers => {}, # HTTP headers for lookup
- :use_https => false, # use HTTPS for lookup requests? (if supported)
- :http_proxy => nil, # HTTP proxy server (user:pass@host:port)
- :https_proxy => nil, # HTTPS proxy server (user:pass@host:port)
- :api_key => nil, # API key for geocoding service
- :cache => nil, # cache object (must respond to #[], #[]=, and #keys)
- :cache_prefix => "geocoder:", # prefix (string) to use for all cache keys
-
- # exceptions that should not be rescued by default
- # (if you want to implement custom error handling);
- # supports SocketError and TimeoutError
- :always_raise => [],
-
- # calculation options
- :units => :mi, # :mi or :km
- :distances => :linear # :linear or :spherical
- }
+
+ # geocoding options
+ @data[:timeout] = 3 # geocoding service timeout (secs)
+ @data[:lookup] = :google # name of street address geocoding service (symbol)
+ @data[:ip_lookup] = :freegeoip # name of IP address geocoding service (symbol)
+ @data[:language] = :en # ISO-639 language code
+ @data[:http_headers] = {} # HTTP headers for lookup
+ @data[:use_https] = false # use HTTPS for lookup requests? (if supported)
+ @data[:http_proxy] = nil # HTTP proxy server (user:pass@host:port)
+ @data[:https_proxy] = nil # HTTPS proxy server (user:pass@host:port)
+ @data[:api_key] = nil # API key for geocoding service
+ @data[:cache] = nil # cache object (must respond to #[], #[]=, and #keys)
+ @data[:cache_prefix] = "geocoder:" # prefix (string) to use for all cache keys
+
+ # exceptions that should not be rescued by default
+ # (if you want to implement custom error handling);
+ # supports SocketError and TimeoutError
+ @data[:always_raise] = []
+
+ # calculation options
+ @data[:units] = :mi # :mi or :km
+ @data[:distances] = :linear # :linear or :spherical
end
instance_eval(OPTIONS.map do |option|
@@ -137,11 +130,5 @@ def #{o}=(value)
EOS
end.join("\n\n"))
- class << self
- def set_defaults
- instance.set_defaults
- end
- end
-
end
end
11 lib/geocoder/configuration_hash.rb
View
@@ -0,0 +1,11 @@
+require 'hash_recursive_merge'
+
+module Geocoder
+ class ConfigurationHash < Hash
+ include HashRecursiveMerge
+
+ def method_missing(meth, *args, &block)
+ has_key?(meth) ? self[meth] : super
+ end
+ end
+end
2  lib/geocoder/lookups/base.rb
View
@@ -159,7 +159,7 @@ def fetch_data(query)
raise_error(err) or warn "Geocoding API connection cannot be established."
rescue TimeoutError => err
raise_error(err) or warn "Geocoding API not responding fast enough " +
- "(use Geocoder.config[:timeout] to set limit)."
+ "(use Geocoder.configure(:timeout => ...) to set limit)."
end
##
4 lib/geocoder/lookups/mapquest.rb
View
@@ -16,7 +16,7 @@ def required_api_key_parts
private # ---------------------------------------------------------------
def query_url(query)
- key = Geocoder::Configuration.api_key
+ key = configuration.api_key
domain = key ? "www" : "open"
url = "#{protocol}://#{domain}.mapquestapi.com/geocoding/v1/#{search_type(query)}?"
url + url_query_string(query)
@@ -27,7 +27,7 @@ def search_type(query)
end
def query_url_params(query)
- key = Geocoder::Configuration.api_key
+ key = configuration.api_key
params = { :location => query.sanitized_text }
if key
params[:key] = CGI.unescape(key)
4 lib/geocoder/lookups/yahoo.rb
View
@@ -59,8 +59,8 @@ def raw_url(query)
def query_url(query)
parsed_url = URI.parse(raw_url(query))
o = OauthUtil.new
- o.consumer_key = Geocoder::Configuration.api_key[0]
- o.consumer_secret = Geocoder::Configuration.api_key[1]
+ o.consumer_key = configuration.api_key[0]
+ o.consumer_secret = configuration.api_key[1]
base_url + o.sign(parsed_url).query_string
end
end
6 lib/geocoder/sql.rb
View
@@ -11,7 +11,7 @@ module Sql
# http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL
#
def full_distance(latitude, longitude, lat_attr, lon_attr, options = {})
- units = options[:units] || Geocoder::Configuration.units
+ units = options[:units] || Geocoder.config.units
earth = Geocoder::Calculations.earth_radius(units)
"#{earth} * 2 * ASIN(SQRT(" +
@@ -32,7 +32,7 @@ def full_distance(latitude, longitude, lat_attr, lon_attr, options = {})
# are not intended for use in production!
#
def approx_distance(latitude, longitude, lat_attr, lon_attr, options = {})
- units = options[:units] || Geocoder::Configuration.units
+ units = options[:units] || Geocoder.config.units
dx = Geocoder::Calculations.longitude_degree_distance(30, units)
dy = Geocoder::Calculations.latitude_degree_distance(units)
@@ -63,7 +63,7 @@ def within_bounding_box(sw_lat, sw_lng, ne_lat, ne_lng, lat_attr, lon_attr)
# http://www.beginningspatial.com/calculating_bearing_one_point_another
#
def full_bearing(latitude, longitude, lat_attr, lon_attr, options = {})
- case options[:bearing] || Geocoder::Configuration.distances
+ case options[:bearing] || Geocoder.config.distances
when :linear
"CAST(" +
"DEGREES(ATAN2( " +
10 lib/geocoder/stores/active_record.rb
View
@@ -87,19 +87,19 @@ def distance_from_sql(location, *args)
# * +:units+ - <tt>:mi</tt> or <tt>:km</tt>; to be used.
# for interpreting radius as well as the +distance+ attribute which
# is added to each found nearby object.
- # See Geocoder::Configuration to know how configure default units.
+ # Use Geocoder.configure[:units] to configure default units.
# * +:bearing+ - <tt>:linear</tt> or <tt>:spherical</tt>.
# the method to be used for calculating the bearing (direction)
# between the given point and each found nearby point;
- # set to false for no bearing calculation.
- # See Geocoder::Configuration to know how configure default method.
+ # set to false for no bearing calculation. Use
+ # Geocoder.configure[:distances] to configure default calculation method.
# * +:select+ - string with the SELECT SQL fragment (e.g. “id, name”)
# * +:order+ - column(s) for ORDER BY SQL clause; default is distance;
# set to false or nil to omit the ORDER BY clause
# * +:exclude+ - an object to exclude (used by the +nearbys+ method)
#
def near_scope_options(latitude, longitude, radius = 20, options = {})
- options[:units] ||= (geocoder_options[:units] || Geocoder::Configuration.units)
+ options[:units] ||= (geocoder_options[:units] || Geocoder.config.units)
bearing = bearing_sql(latitude, longitude, options)
distance = distance_sql(latitude, longitude, options)
@@ -143,7 +143,7 @@ def distance_sql(latitude, longitude, options = {})
#
def bearing_sql(latitude, longitude, options = {})
if !options.include?(:bearing)
- options[:bearing] = Geocoder::Configuration.distances
+ options[:bearing] = Geocoder.config.distances
end
if options[:bearing]
method_prefix = using_sqlite? ? "approx" : "full"
74 lib/hash_recursive_merge.rb
View
@@ -0,0 +1,74 @@
+#
+# = Hash Recursive Merge
+#
+# Merges a Ruby Hash recursively, Also known as deep merge.
+# Recursive version of Hash#merge and Hash#merge!.
+#
+# Category:: Ruby
+# Package:: Hash
+# Author:: Simone Carletti <weppos@weppos.net>
+# Copyright:: 2007-2008 The Authors
+# License:: MIT License
+# Link:: http://www.simonecarletti.com/
+# Source:: http://gist.github.com/gists/6391/
+#
+module HashRecursiveMerge
+
+ #
+ # Recursive version of Hash#merge!
+ #
+ # Adds the contents of +other_hash+ to +hsh+,
+ # merging entries in +hsh+ with duplicate keys with those from +other_hash+.
+ #
+ # Compared with Hash#merge!, this method supports nested hashes.
+ # When both +hsh+ and +other_hash+ contains an entry with the same key,
+ # it merges and returns the values from both arrays.
+ #
+ # h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}}
+ # h2 = {"b" => 254, "c" => {"c1" => 16, "c3" => 94}}
+ # h1.rmerge!(h2) #=> {"a" => 100, "b" => 254, "c" => {"c1" => 16, "c2" => 14, "c3" => 94}}
+ #
+ # Simply using Hash#merge! would return
+ #
+ # h1.merge!(h2) #=> {"a" => 100, "b" = >254, "c" => {"c1" => 16, "c3" => 94}}
+ #
+ def rmerge!(other_hash)
+ merge!(other_hash) do |key, oldval, newval|
+ oldval.class == self.class ? oldval.rmerge!(newval) : newval
+ end
+ end
+
+ #
+ # Recursive version of Hash#merge
+ #
+ # Compared with Hash#merge!, this method supports nested hashes.
+ # When both +hsh+ and +other_hash+ contains an entry with the same key,
+ # it merges and returns the values from both arrays.
+ #
+ # Compared with Hash#merge, this method provides a different approch
+ # for merging nasted hashes.
+ # If the value of a given key is an Hash and both +other_hash+ abd +hsh
+ # includes the same key, the value is merged instead replaced with
+ # +other_hash+ value.
+ #
+ # h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}}
+ # h2 = {"b" => 254, "c" => {"c1" => 16, "c3" => 94}}
+ # h1.rmerge(h2) #=> {"a" => 100, "b" => 254, "c" => {"c1" => 16, "c2" => 14, "c3" => 94}}
+ #
+ # Simply using Hash#merge would return
+ #
+ # h1.merge(h2) #=> {"a" => 100, "b" = >254, "c" => {"c1" => 16, "c3" => 94}}
+ #
+ def rmerge(other_hash)
+ r = {}
+ merge(other_hash) do |key, oldval, newval|
+ r[key] = oldval.class == self.class ? oldval.rmerge(newval) : newval
+ end
+ end
+
+end
+
+
+class Hash
+ include HashRecursiveMerge
+end
4 test/cache_test.rb
View
@@ -4,9 +4,9 @@
class CacheTest < Test::Unit::TestCase
def test_second_occurrence_of_request_is_cache_hit
- Geocoder::Configuration.cache = {}
+ Geocoder.configure(:cache => {})
Geocoder::Lookup.all_services_except_test.each do |l|
- Geocoder::Configuration.lookup = l
+ Geocoder.configure(:lookup => l)
set_api_key!(l)
results = Geocoder.search("Madison Square Garden")
assert !results.first.cache_hit,
8 test/calculations_test.rb
View
@@ -3,10 +3,10 @@
class CalculationsTest < Test::Unit::TestCase
def setup
- Geocoder.configure do |config|
- config.units = :mi
- config.distances = :linear
- end
+ Geocoder.configure(
+ :units => :mi,
+ :distances => :linear
+ )
end
# --- degree distance ---
27 test/configuration_test.rb
View
@@ -7,7 +7,7 @@ def setup
end
def test_exception_raised_on_bad_lookup_config
- Geocoder::Configuration.lookup = :stoopid
+ Geocoder.configure(:lookup => :stoopid)
assert_raises Geocoder::ConfigurationError do
Geocoder.search "something dumb"
end
@@ -15,35 +15,34 @@ def test_exception_raised_on_bad_lookup_config
def test_setting_with_class_method
Geocoder::Configuration.units = :test
- assert_equal :test, Geocoder.configure.units
- assert_equal :test, Geocoder.config[:units]
+ assert_equal :test, Geocoder.config.units
end
def test_setting_with_configure_method
- Geocoder.configure.units = :test
- assert_equal :test, Geocoder::Configuration.units
- assert_equal :test, Geocoder.config[:units]
+ Geocoder.configure(:units => :test)
+ assert_equal :test, Geocoder.config.units
end
def test_setting_with_block_syntax
+ orig = $VERBOSE; $VERBOSE = nil
Geocoder.configure do |config|
config.units = :test
end
- assert_equal :test, Geocoder::Configuration.units
- assert_equal :test, Geocoder.configure.units
- assert_equal :test, Geocoder.config[:units]
+ assert_equal :test, Geocoder.config.units
+ ensure
+ $VERBOSE = orig
end
def test_config_for_lookup
- Geocoder.config = {
+ Geocoder.configure(
:timeout => 5,
:api_key => "aaa",
:google => {
:timeout => 2
}
- }
- assert_equal 2, Geocoder.config_for_lookup(:google)[:timeout]
- assert_equal "aaa", Geocoder.config_for_lookup(:google)[:api_key]
+ )
+ assert_equal 2, Geocoder.config_for_lookup(:google).timeout
+ assert_equal "aaa", Geocoder.config_for_lookup(:google).api_key
end
def test_model_configuration
@@ -66,7 +65,7 @@ def test_configuration_chain
v.longitude = 0
# method option > global configuration
- Geocoder.configure.units = :km
+ Geocoder.configure(:units => :km)
assert_equal 69, v.distance_to([0,1], :mi).round
# per-model configuration > global configuration
8 test/error_handling_test.rb
View
@@ -4,14 +4,14 @@
class ErrorHandlingTest < Test::Unit::TestCase
def teardown
- Geocoder::Configuration.always_raise = []
+ Geocoder.configure(:always_raise => [])
end
def test_does_not_choke_on_timeout
# keep test output clean: suppress timeout warning
orig = $VERBOSE; $VERBOSE = nil
Geocoder::Lookup.all_services_except_test.each do |l|
- Geocoder::Configuration.lookup = l
+ Geocoder.configure(:lookup => l)
set_api_key!(l)
assert_nothing_raised { Geocoder.search("timeout") }
end
@@ -20,7 +20,7 @@ def test_does_not_choke_on_timeout
end
def test_always_raise_timeout_error
- Geocoder::Configuration.always_raise = [TimeoutError]
+ Geocoder.configure(:always_raise => [TimeoutError])
Geocoder::Lookup.all_services_except_test.each do |l|
lookup = Geocoder::Lookup.get(l)
set_api_key!(l)
@@ -31,7 +31,7 @@ def test_always_raise_timeout_error
end
def test_always_raise_socket_error
- Geocoder::Configuration.always_raise = [SocketError]
+ Geocoder.configure(:always_raise => [SocketError])
Geocoder::Lookup.all_services_except_test.each do |l|
lookup = Geocoder::Lookup.get(l)
set_api_key!(l)
2  test/https_test.rb
View
@@ -4,7 +4,7 @@
class HttpsTest < Test::Unit::TestCase
def test_uses_https_for_secure_query
- Geocoder::Configuration.use_https = true
+ Geocoder.configure(:use_https => true)
g = Geocoder::Lookup::Google.new
assert_match /^https:/, g.send(:query_url, Geocoder::Query.new("test"))
end
4 test/integration/smoke_test.rb
View
@@ -14,13 +14,13 @@ def test_simple_zip_code_search
end
def test_simple_zip_code_search_with_ssl
- Geocoder::Configuration.use_https = true
+ Geocoder.configure(:use_https => true)
result = Geocoder.search "27701"
assert_not_nil (r = result.first)
assert_equal "Durham", r.city
assert_equal "North Carolina", r.state
ensure
- Geocoder::Configuration.use_https = false
+ Geocoder.configure(:use_https => false)
end
end
9 test/lookup_test.rb
View
@@ -14,7 +14,7 @@ def test_search_returns_empty_array_when_no_results
def test_does_not_choke_on_nil_address
Geocoder::Lookup.all_services.each do |l|
- Geocoder::Configuration.lookup = l
+ Geocoder.configure(:lookup => l)
assert_nothing_raised { Venue.new("Venue", nil).geocode }
end
end
@@ -25,21 +25,20 @@ def test_hash_to_query
end
def test_google_api_key
- Geocoder::Configuration.api_key = "MY_KEY"
+ Geocoder.configure(:api_key => "MY_KEY")
g = Geocoder::Lookup::Google.new
assert_match "key=MY_KEY", g.send(:query_url, Geocoder::Query.new("Madison Square Garden, New York, NY 10001, United States"))
end
def test_geocoder_ca_showpostal
- Geocoder::Configuration.api_key = "MY_KEY"
+ Geocoder.configure(:api_key => "MY_KEY")
g = Geocoder::Lookup::GeocoderCa.new
assert_match "showpostal=1", g.send(:query_url, Geocoder::Query.new("Madison Square Garden, New York, NY 10001, United States"))
end
def test_raises_configuration_error_on_missing_key
assert_raises Geocoder::ConfigurationError do
- Geocoder::Configuration.lookup = :bing
- Geocoder::Configuration.api_key = nil
+ Geocoder.configure(:lookup => :bing, :api_key => nil)
Geocoder.search("Madison Square Garden, New York, NY 10001, United States")
end
end
4 test/proxy_test.rb
View
@@ -4,7 +4,7 @@
class ProxyTest < Test::Unit::TestCase
def test_uses_proxy_when_specified
- Geocoder::Configuration.http_proxy = 'localhost'
+ Geocoder.configure(:http_proxy => 'localhost')
lookup = Geocoder::Lookup::Google.new
assert lookup.send(:http_client).proxy_class?
end
@@ -15,7 +15,7 @@ def test_doesnt_use_proxy_when_not_specified
end
def test_exception_raised_on_bad_proxy_url
- Geocoder::Configuration.http_proxy = ' \\_O< Quack Quack'
+ Geocoder.configure(:http_proxy => ' \\_O< Quack Quack')
assert_raise Geocoder::ConfigurationError do
Geocoder::Lookup::Google.new.send(:http_client)
end
4 test/result_test.rb
View
@@ -5,7 +5,7 @@ class ResultTest < Test::Unit::TestCase
def test_result_has_required_attributes
Geocoder::Lookup.all_services_except_test.each do |l|
- Geocoder::Configuration.lookup = l
+ Geocoder.configure(:lookup => l)
set_api_key!(l)
result = Geocoder.search([45.423733, -75.676333]).first
assert_result_has_required_attributes(result)
@@ -16,7 +16,7 @@ def test_result_has_required_attributes
private # ------------------------------------------------------------------
def assert_result_has_required_attributes(result)
- m = "Lookup #{Geocoder::Configuration.lookup} does not support %s attribute."
+ m = "Lookup #{Geocoder.config.lookup} does not support %s attribute."
assert result.coordinates.is_a?(Array), m % "coordinates"
assert result.latitude.is_a?(Float), m % "latitude"
assert result.longitude.is_a?(Float), m % "longitude"
31 test/services_test.rb
View
@@ -59,7 +59,7 @@ def test_google_query_url_contains_bounds
# --- Google Premier ---
def test_google_premier_result_components
- Geocoder::Configuration.lookup = :google_premier
+ Geocoder.configure(:lookup => :google_premier)
set_api_key!(:google_premier)
result = Geocoder.search("Madison Square Garden, New York, NY").first
assert_equal "Manhattan",
@@ -67,7 +67,7 @@ def test_google_premier_result_components
end
def test_google_premier_query_url
- Geocoder::Configuration.api_key = ["deadbeef", "gme-test", "test-dev"]
+ Geocoder.configure(:api_key => ["deadbeef", "gme-test", "test-dev"])
assert_equal "http://maps.googleapis.com/maps/api/geocode/json?address=Madison+Square+Garden%2C+New+York%2C+NY&channel=test-dev&client=gme-test&language=en&sensor=false&signature=doJvJqX7YJzgV9rJ0DnVkTGZqTg=",
Geocoder::Lookup::GooglePremier.new.send(:query_url, Geocoder::Query.new("Madison Square Garden, New York, NY"))
end
@@ -76,13 +76,13 @@ def test_google_premier_query_url
# --- Yahoo ---
def test_yahoo_no_results
- Geocoder::Configuration.lookup = :yahoo
+ Geocoder.configure(:lookup => :yahoo)
set_api_key!(:yahoo)
assert_equal [], Geocoder.search("no results")
end
def test_yahoo_error
- Geocoder::Configuration.lookup = :yahoo
+ Geocoder.configure(:lookup => :yahoo)
set_api_key!(:yahoo)
# keep test output clean: suppress timeout warning
orig = $VERBOSE; $VERBOSE = nil
@@ -92,14 +92,14 @@ def test_yahoo_error
end
def test_yahoo_result_components
- Geocoder::Configuration.lookup = :yahoo
+ Geocoder.configure(:lookup => :yahoo)
set_api_key!(:yahoo)
result = Geocoder.search("madison square garden").first
assert_equal "10001", result.postal_code
end
def test_yahoo_address_formatting
- Geocoder::Configuration.lookup = :yahoo
+ Geocoder.configure(:lookup => :yahoo)
set_api_key!(:yahoo)
result = Geocoder.search("madison square garden").first
assert_equal "Madison Square Garden, New York, NY 10001, United States", result.address
@@ -111,7 +111,7 @@ def test_yahoo_address_formatting
def test_yandex_with_invalid_key
# keep test output clean: suppress timeout warning
orig = $VERBOSE; $VERBOSE = nil
- Geocoder::Configuration.lookup = :yandex
+ Geocoder.configure(:lookup => :yandex)
set_api_key!(:yandex)
assert_equal [], Geocoder.search("invalid key")
ensure
@@ -122,7 +122,7 @@ def test_yandex_with_invalid_key
# --- Geocoder.ca ---
def test_geocoder_ca_result_components
- Geocoder::Configuration.lookup = :geocoder_ca
+ Geocoder.configure(:lookup => :geocoder_ca)
set_api_key!(:geocoder_ca)
result = Geocoder.search([45.423733, -75.676333]).first
assert_equal "CA", result.country_code
@@ -146,7 +146,7 @@ def test_freegeoip_result_components
# --- Bing ---
def test_bing_result_components
- Geocoder::Configuration.lookup = :bing
+ Geocoder.configure(:lookup => :bing)
set_api_key!(:bing)
result = Geocoder.search("Madison Square Garden, New York, NY").first
assert_equal "Madison Square Garden, NY", result.address
@@ -155,7 +155,7 @@ def test_bing_result_components
end
def test_bing_no_results
- Geocoder::Configuration.lookup = :bing
+ Geocoder.configure(:lookup => :bing)
set_api_key!(:bing)
results = Geocoder.search("no results")
assert_equal 0, results.length
@@ -164,14 +164,14 @@ def test_bing_no_results
# --- Nominatim ---
def test_nominatim_result_components
- Geocoder::Configuration.lookup = :nominatim
+ Geocoder.configure(:lookup => :nominatim)
set_api_key!(:nominatim)
result = Geocoder.search("Madison Square Garden, New York, NY").first
assert_equal "10001", result.postal_code
end
def test_nominatim_address_formatting
- Geocoder::Configuration.lookup = :nominatim
+ Geocoder.configure(:lookup => :nominatim)
set_api_key!(:nominatim)
result = Geocoder.search("Madison Square Garden, New York, NY").first
assert_equal "Madison Square Garden, West 31st Street, Long Island City, New York City, New York, 10001, United States of America",
@@ -181,8 +181,7 @@ def test_nominatim_address_formatting
# --- MapQuest ---
def test_api_route
- Geocoder::Configuration.lookup = :mapquest
- Geocoder::Configuration.api_key = "abc123"
+ Geocoder.configure(:lookup => :mapquest, :api_key => "abc123")
lookup = Geocoder::Lookup::Mapquest.new
query = Geocoder::Query.new("Bluffton, SC")
res = lookup.send(:query_url, query)
@@ -191,14 +190,14 @@ def test_api_route
end
def test_mapquest_result_components
- Geocoder::Configuration.lookup = :mapquest
+ Geocoder.configure(:lookup => :mapquest)
set_api_key!(:mapquest)
result = Geocoder.search("Madison Square Garden, New York, NY").first
assert_equal "10001", result.postal_code
end
def test_mapquest_address_formatting
- Geocoder::Configuration.lookup = :mapquest
+ Geocoder.configure(:lookup => :mapquest)
set_api_key!(:mapquest)
result = Geocoder.search("Madison Square Garden, New York, NY").first
assert_equal "46 West 31st Street, New York, NY, 10001, US",
2  test/test_helper.rb
View
@@ -334,6 +334,6 @@ def set_api_key!(lookup_name)
else
key = nil
end
- Geocoder::Configuration.api_key = key
+ Geocoder.configure(:api_key => key)
end
end
6 test/test_mode_test.rb
View
@@ -3,13 +3,13 @@
class TestModeTest < Test::Unit::TestCase
def setup
- @_original_lookup = Geocoder::Configuration.lookup
- Geocoder::Configuration.lookup = :test
+ @_original_lookup = Geocoder.config.lookup
+ Geocoder.configure(:lookup => :test)
end
def teardown
Geocoder::Lookup::Test.reset
- Geocoder::Configuration.lookup = @_original_lookup
+ Geocoder.configure(:lookup => @_original_lookup)
end
def test_search_with_known_stub

3 comments on commit a0448d4

Douwe Maan

Our of curiosity, why was this change made? The block-based syntax seems to be the de-facto standard.

Alex Reisner

The hash makes it easier to use nesting (for API-specific config). Also, this way the configuration is more like data than code, which seems simpler and more appropriate. I think DSLs are a little overused for configuration. What's the advantage?

Douwe Maan

No big advantages, just the one: consistency among gems.

Please sign in to comment.
Something went wrong with that request. Please try again.