geocoder 1.1.4 → 1.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. data/.travis.yml +4 -0
  2. data/{CHANGELOG.rdoc → CHANGELOG.md} +120 -39
  3. data/README.md +200 -54
  4. data/examples/autoexpire_cache_dalli.rb +62 -0
  5. data/examples/{autoexpire_cache.rb → autoexpire_cache_redis.rb} +4 -6
  6. data/lib/generators/geocoder/config/templates/initializer.rb +19 -23
  7. data/lib/geocoder/cache.rb +6 -1
  8. data/lib/geocoder/calculations.rb +45 -13
  9. data/lib/geocoder/cli.rb +10 -11
  10. data/lib/geocoder/configuration.rb +67 -43
  11. data/lib/geocoder/configuration_hash.rb +11 -0
  12. data/lib/geocoder/exceptions.rb +3 -0
  13. data/lib/geocoder/lookup.rb +3 -1
  14. data/lib/geocoder/lookups/base.rb +89 -27
  15. data/lib/geocoder/lookups/bing.rb +21 -11
  16. data/lib/geocoder/lookups/esri.rb +48 -0
  17. data/lib/geocoder/lookups/freegeoip.rb +8 -4
  18. data/lib/geocoder/lookups/geocoder_ca.rb +11 -7
  19. data/lib/geocoder/lookups/google.rb +18 -9
  20. data/lib/geocoder/lookups/google_premier.rb +16 -8
  21. data/lib/geocoder/lookups/mapquest.rb +33 -5
  22. data/lib/geocoder/lookups/maxmind.rb +88 -0
  23. data/lib/geocoder/lookups/nominatim.rb +13 -8
  24. data/lib/geocoder/lookups/ovi.rb +52 -0
  25. data/lib/geocoder/lookups/test.rb +10 -0
  26. data/lib/geocoder/lookups/yahoo.rb +51 -38
  27. data/lib/geocoder/lookups/yandex.rb +17 -9
  28. data/lib/geocoder/models/mongoid.rb +1 -1
  29. data/lib/geocoder/query.rb +20 -5
  30. data/lib/geocoder/request.rb +7 -1
  31. data/lib/geocoder/results/base.rb +8 -2
  32. data/lib/geocoder/results/esri.rb +51 -0
  33. data/lib/geocoder/results/google.rb +34 -0
  34. data/lib/geocoder/results/mapquest.rb +46 -2
  35. data/lib/geocoder/results/maxmind.rb +135 -0
  36. data/lib/geocoder/results/nominatim.rb +0 -10
  37. data/lib/geocoder/results/ovi.rb +62 -0
  38. data/lib/geocoder/results/test.rb +2 -1
  39. data/lib/geocoder/results/yahoo.rb +9 -2
  40. data/lib/geocoder/results/yandex.rb +12 -2
  41. data/lib/geocoder/sql.rb +6 -4
  42. data/lib/geocoder/stores/active_record.rb +57 -30
  43. data/lib/geocoder/stores/base.rb +3 -2
  44. data/lib/geocoder/stores/mongo_base.rb +2 -1
  45. data/lib/geocoder/version.rb +1 -1
  46. data/lib/geocoder.rb +2 -4
  47. data/lib/hash_recursive_merge.rb +74 -0
  48. data/lib/oauth_util.rb +112 -0
  49. data/test/cache_test.rb +19 -0
  50. data/test/calculations_test.rb +10 -4
  51. data/test/configuration_test.rb +26 -51
  52. data/test/error_handling_test.rb +8 -4
  53. data/test/fixtures/bing_invalid_key +1 -0
  54. data/test/fixtures/esri_madison_square_garden +59 -0
  55. data/test/fixtures/esri_no_results +8 -0
  56. data/test/fixtures/esri_reverse +21 -0
  57. data/test/fixtures/mapquest_madison_square_garden +52 -0
  58. data/test/fixtures/mapquest_no_results +7 -0
  59. data/test/fixtures/maxmind_24_24_24_21 +1 -0
  60. data/test/fixtures/maxmind_24_24_24_22 +1 -0
  61. data/test/fixtures/maxmind_24_24_24_23 +1 -0
  62. data/test/fixtures/maxmind_24_24_24_24 +1 -0
  63. data/test/fixtures/maxmind_74_200_247_59 +1 -0
  64. data/test/fixtures/maxmind_invalid_key +1 -0
  65. data/test/fixtures/maxmind_no_results +1 -0
  66. data/test/fixtures/ovi_madison_square_garden +72 -0
  67. data/test/fixtures/ovi_no_results +8 -0
  68. data/test/fixtures/yahoo_error +1 -0
  69. data/test/fixtures/yahoo_invalid_key +2 -0
  70. data/test/fixtures/yahoo_madison_square_garden +52 -0
  71. data/test/fixtures/yahoo_no_results +10 -0
  72. data/test/fixtures/yahoo_over_limit +2 -0
  73. data/test/fixtures/yandex_no_city_and_town +112 -0
  74. data/test/https_test.rb +3 -3
  75. data/test/integration/smoke_test.rb +2 -2
  76. data/test/lookup_test.rb +84 -11
  77. data/test/near_test.rb +43 -0
  78. data/test/oauth_util_test.rb +30 -0
  79. data/test/proxy_test.rb +2 -2
  80. data/test/query_test.rb +13 -0
  81. data/test/request_test.rb +29 -0
  82. data/test/result_test.rb +12 -2
  83. data/test/services_test.rb +208 -57
  84. data/test/test_helper.rb +66 -100
  85. data/test/test_mode_test.rb +35 -26
  86. metadata +58 -35
  87. data/test/fixtures/mapquest_madison_square_garden.json +0 -27
  88. data/test/fixtures/mapquest_no_results.json +0 -1
  89. data/test/fixtures/yahoo_madison_square_garden.json +0 -52
  90. data/test/fixtures/yahoo_no_results.json +0 -10
  91. data/test/fixtures/yahoo_v1_madison_square_garden.json +0 -46
  92. data/test/fixtures/yahoo_v1_no_results.json +0 -10
  93. /data/test/fixtures/{bing_madison_square_garden.json → bing_madison_square_garden} +0 -0
  94. /data/test/fixtures/{bing_no_results.json → bing_no_results} +0 -0
  95. /data/test/fixtures/{bing_reverse.json → bing_reverse} +0 -0
  96. /data/test/fixtures/{freegeoip_74_200_247_59.json → freegeoip_74_200_247_59} +0 -0
  97. /data/test/fixtures/{freegeoip_no_results.json → freegeoip_no_results} +0 -0
  98. /data/test/fixtures/{geocoder_ca_madison_square_garden.json → geocoder_ca_madison_square_garden} +0 -0
  99. /data/test/fixtures/{geocoder_ca_no_results.json → geocoder_ca_no_results} +0 -0
  100. /data/test/fixtures/{geocoder_ca_reverse.json → geocoder_ca_reverse} +0 -0
  101. /data/test/fixtures/{google_garbage.json → google_garbage} +0 -0
  102. /data/test/fixtures/{google_madison_square_garden.json → google_madison_square_garden} +0 -0
  103. /data/test/fixtures/{google_no_city_data.json → google_no_city_data} +0 -0
  104. /data/test/fixtures/{google_no_locality.json → google_no_locality} +0 -0
  105. /data/test/fixtures/{google_no_results.json → google_no_results} +0 -0
  106. /data/test/fixtures/{nominatim_madison_square_garden.json → nominatim_madison_square_garden} +0 -0
  107. /data/test/fixtures/{nominatim_no_results.json → nominatim_no_results} +0 -0
  108. /data/test/fixtures/{yandex_invalid_key.json → yandex_invalid_key} +0 -0
  109. /data/test/fixtures/{yandex_kremlin.json → yandex_kremlin} +0 -0
  110. /data/test/fixtures/{yandex_no_results.json → yandex_no_results} +0 -0
data/README.md CHANGED
@@ -7,18 +7,20 @@ Geocoder is a complete geocoding solution for Ruby. With Rails it adds geocoding
7
7
  Compatibility
8
8
  -------------
9
9
 
10
- * Supports multiple Ruby versions: Ruby 1.8.7, 1.9.2, and JRuby.
10
+ * Supports multiple Ruby versions: Ruby 1.8.7, 1.9.2, 1.9.3, and JRuby.
11
11
  * Supports multiple databases: MySQL, PostgreSQL, SQLite, and MongoDB (1.7.0 and higher).
12
- * Supports Rails 3. If you need to use it with Rails 2 please see the `rails2` branch (no longer maintained, limited feature set).
12
+ * Supports Rails 3.x. If you need to use it with Rails 2 please see the `rails2` branch (no longer maintained, limited feature set).
13
13
  * Works very well outside of Rails, you just need to install either the `json` (for MRI) or `json_pure` (for JRuby) gem.
14
14
 
15
15
 
16
- Install
17
- -------
16
+ Installation
17
+ ------------
18
+
19
+ Install Geocoder like any other Ruby gem:
18
20
 
19
- ### As a Gem
21
+ gem install geocoder
20
22
 
21
- Add to your Gemfile:
23
+ Or, if you're using Rails/Bundler, add this to your Gemfile:
22
24
 
23
25
  gem "geocoder"
24
26
 
@@ -26,21 +28,13 @@ and run at the command prompt:
26
28
 
27
29
  bundle install
28
30
 
29
- ### Or As a Plugin
30
-
31
- At the command prompt:
32
-
33
- rails plugin install git://github.com/alexreisner/geocoder.git
34
-
35
-
36
- Configure Object Geocoding
37
- --------------------------
38
31
 
39
- In the below, note that addresses may be street or IP addresses.
32
+ Object Geocoding
33
+ ----------------
40
34
 
41
35
  ### ActiveRecord
42
36
 
43
- Your model must have two attributes (database columns) for storing latitude and longitude coordinates. By default they should be called `latitude` and `longitude` but this can be changed (see "More on Configuration" below):
37
+ Your model must have two attributes (database columns) for storing latitude and longitude coordinates. By default they should be called `latitude` and `longitude` but this can be changed (see "Model Configuration" below):
44
38
 
45
39
  rails generate migration AddLatitudeAndLongitudeToModel latitude:float longitude:float
46
40
  rake db:migrate
@@ -81,6 +75,10 @@ Reverse geocoding is similar:
81
75
  reverse_geocoded_by :coordinates
82
76
  after_validation :reverse_geocode # auto-fetch address
83
77
 
78
+ Once you've set up your model you'll need to create the necessary spatial indices in your database:
79
+
80
+ rake db:mongoid:create_indexes
81
+
84
82
  Be sure to read _Latitude/Longitude Order_ in the _Notes on MongoDB_ section below on how to properly retrieve latitude/longitude coordinates from your objects.
85
83
 
86
84
  ### MongoMapper
@@ -111,6 +109,8 @@ Geocoder adds a `location` method to the standard `Rack::Request` object so you
111
109
  # returns Geocoder::Result object
112
110
  result = request.location
113
111
 
112
+ Note that this will usually return `nil` in your test and development environments because things like "localhost" and "0.0.0.0" are not an Internet IP addresses.
113
+
114
114
  See _Advanced Geocoding_ below for more information about `Geocoder::Result` objects.
115
115
 
116
116
 
@@ -126,9 +126,11 @@ To find objects by location, use the following scopes:
126
126
 
127
127
  With geocoded objects you can do things like this:
128
128
 
129
- obj.nearbys(30) # other objects within 30 miles
130
- obj.distance_from([40.714,-100.234]) # distance from arbitrary point to object
131
- obj.bearing_to("Paris, France") # direction from object to arbitrary point
129
+ if obj.geocoded?
130
+ obj.nearbys(30) # other objects within 30 miles
131
+ obj.distance_from([40.714,-100.234]) # distance from arbitrary point to object
132
+ obj.bearing_to("Paris, France") # direction from object to arbitrary point
133
+ end
132
134
 
133
135
  Some utility methods are also available:
134
136
 
@@ -178,11 +180,11 @@ To calculate accurate distance and bearing with SQLite or MongoDB:
178
180
  obj.bearing_to([43.9,-98.6]) # bearing from obj to point
179
181
  obj.bearing_from(obj2) # bearing from obj2 to obj
180
182
 
181
- The `bearing_from/to` methods take a single argument which can be: a `[lat,lon]` array, a geocoded object, or a geocodable address (string). The `distance_from/to` methods also take a units argument (`:mi` or `:km`).
183
+ The `bearing_from/to` methods take a single argument which can be: a `[lat,lon]` array, a geocoded object, or a geocodable address (string). The `distance_from/to` methods also take a units argument (`:mi`, `:km`, or `:nm` for nautical miles).
182
184
 
183
185
 
184
- More on Configuration
185
- ---------------------
186
+ Model Configuration
187
+ -------------------
186
188
 
187
189
  You are not stuck with using the `latitude` and `longitude` database column names (with ActiveRecord) or the `coordinates` array (Mongo) for storing coordinates. For example:
188
190
 
@@ -257,32 +259,37 @@ If you're familiar with the results returned by the geocoding service you're usi
257
259
  Geocoding Services
258
260
  ------------------
259
261
 
260
- 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:
262
+ By default Geocoder uses Google's geocoding API to fetch coordinates and street addresses (FreeGeoIP is the default 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:
261
263
 
262
264
  # config/initializers/geocoder.rb
263
- Geocoder.configure do |config|
265
+ Geocoder.configure(
264
266
 
265
267
  # geocoding service (see below for supported options):
266
- config.lookup = :yahoo
268
+ :lookup => :yandex,
267
269
 
268
270
  # to use an API key:
269
- config.api_key = "..."
271
+ :api_key => "...",
270
272
 
271
273
  # geocoding service request timeout, in seconds (default 3):
272
- config.timeout = 5
274
+ :timeout => 5,
273
275
 
274
276
  # set default units to kilometers:
275
- config.units = :km
277
+ :units => :km,
276
278
 
277
279
  # caching (see below for details):
278
- config.cache = Redis.new
279
- config.cache_prefix = "..."
280
+ :cache => Redis.new,
281
+ :cache_prefix => "..."
280
282
 
281
- end
283
+ )
284
+
285
+ Please see lib/geocoder/configuration.rb for a complete list of configuration options. Additionally, some lookups have their own configuration options, some of which are directly supported by Geocoder. For example, to specify a value for Google's `bounds` parameter:
286
+
287
+ # with Google:
288
+ Geocoder.search("Paris", :bounds => [[32.1,-95.9], [33.9,-94.3]])
282
289
 
283
- 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:
290
+ Please see the [source code for each lookup](https://github.com/alexreisner/geocoder/tree/master/lib/geocoder/lookups) to learn about directly supported parameters. Parameters which are not directly supported can be specified using the `:params` option, by which you can pass arbitrary parameters to any geocoding service. For example, to use Nominatim's `countrycodes` parameter:
284
291
 
285
- Geocoder::Configuration.lookup = :nominatim
292
+ # with Nominatim:
286
293
  Geocoder.search("Paris", :params => {:countrycodes => "gb,de,fr,es,us"})
287
294
 
288
295
 
@@ -302,19 +309,21 @@ The following is a comparison of the supported geocoding APIs. The "Limitations"
302
309
  * **Documentation**: http://code.google.com/apis/maps/documentation/geocoding/#JSON
303
310
  * **Terms of Service**: http://code.google.com/apis/maps/terms.html#section_10_12
304
311
  * **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..."
305
- * **Notes**: To use Google Premier set `Geocoder::Configuration.lookup = :google_premier` and `Geocoder::Configuration.api_key = [key, client, channel]`.
312
+ * **Notes**: To use Google Premier set `Geocoder.configure(:lookup => :google_premier, :api_key => [key, client, channel])`.
306
313
 
307
- #### Yahoo (`:yahoo`)
314
+ #### Yahoo BOSS (`:yahoo`)
308
315
 
309
- * **API key**: optional in development (required for production apps)
310
- * **Key signup**: https://developer.apps.yahoo.com/wsregapp
311
- * **Quota**: 50,000 requests/day, more available by special arrangement
316
+ Yahoo BOSS is **not a free service**. As of November 17, 2012 Yahoo no longer offers a free geocoding API.
317
+
318
+ * **API key**: requires OAuth consumer key and secret (set `Geocoder.configure(:api_key => [key, secret])`)
319
+ * **Key signup**: http://developer.yahoo.com/boss/geo/
320
+ * **Quota**: unlimited, but subject to usage fees
312
321
  * **Region**: world
313
322
  * **SSL support**: no
314
- * **Languages**: ?
315
- * **Documentation**: http://developer.yahoo.com/geo/placefinder/guide/responses.html
316
- * **Terms of Service**: http://info.yahoo.com/legal/us/yahoo/maps/mapsapi/mapsapi-2141.html
317
- * **Limitations**: "YOU SHALL NOT... (viii) store or allow end users to store map imagery, map data or geocoded location information from the Yahoo! Maps APIs for any future use; (ix) use the stand-alone geocoder for any use other than displaying Yahoo! Maps or displaying points on Yahoo! Maps;"
323
+ * **Languages**: en, fr, de, it, es, pt, nl, zh, ja, ko
324
+ * **Documentation**: http://developer.yahoo.com/boss/geo/docs/index.html
325
+ * **Terms of Service**: http://info.yahoo.com/legal/us/yahoo/boss/tou/?pir=ucJPcJ1ibUn.h.d.lVmlcbcEkoHjwJ_PvxG9SLK9VIbIQAw1XFrnDqY-
326
+ * **Limitations**: No mass downloads, no commercial map production based on the data, no storage of data except for caching.
318
327
 
319
328
  #### Bing (`:bing`)
320
329
 
@@ -363,8 +372,11 @@ The following is a comparison of the supported geocoding APIs. The "Limitations"
363
372
 
364
373
  #### Mapquest (`:mapquest`)
365
374
 
366
- * **API key**: none
375
+ * **API key**: required for the licensed API, do not use for open tier
367
376
  * **Quota**: ?
377
+ * **HTTP Headers**: in order to use the licensed API you can configure the http_headers to include a referer as so:
378
+ `Geocoder.configure(:http_headers => { "Referer" => "http://foo.com" })`
379
+ 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.
368
380
  * **Region**: world
369
381
  * **SSL support**: no
370
382
  * **Languages**: English
@@ -372,6 +384,17 @@ The following is a comparison of the supported geocoding APIs. The "Limitations"
372
384
  * **Terms of Service**: http://info.mapquest.com/terms-of-use/
373
385
  * **Limitations**: ?
374
386
 
387
+ #### Ovi/Nokia (`:ovi`)
388
+
389
+ * **API key**: not required, but performance restricted without it
390
+ * **Quota**: ?
391
+ * **Region**: world
392
+ * **SSL support**: no
393
+ * **Languages**: English
394
+ * **Documentation**: http://api.maps.ovi.com/devguide/overview.html
395
+ * **Terms of Service**: http://www.developer.nokia.com/Develop/Maps/TC.html
396
+ * **Limitations**: ?
397
+
375
398
  #### FreeGeoIP (`:freegeoip`)
376
399
 
377
400
  * **API key**: none
@@ -383,13 +406,37 @@ The following is a comparison of the supported geocoding APIs. The "Limitations"
383
406
  * **Terms of Service**: ?
384
407
  * **Limitations**: ?
385
408
 
409
+ #### MaxMind Web Services (`:maxmind`)
410
+
411
+ * **API key**: required
412
+ * **Quota**: Request Packs can be purchased
413
+ * **Region**: world
414
+ * **SSL support**: yes
415
+ * **Languages**: English
416
+ * **Documentation**: http://www.maxmind.com/app/web_services
417
+ * **Terms of Service**: ?
418
+ * **Limitations**: ?
419
+ * **Notes**: You must specify which MaxMind service you are using in your configuration. For example: `Geocoder.configure(:maxmind => {:service => :omni})`.
420
+
421
+ #### ESRI (`:esri`)
422
+
423
+ * **API key**: none
424
+ * **Quota**: Required for some scenarios (see Terms of Service)
425
+ * **Region**: world
426
+ * **SSL support**: yes
427
+ * **Languages**: English
428
+ * **Documentation**: http://resources.arcgis.com/en/help/arcgis-online-geocoding-rest-api/
429
+ * **Terms of Service**: http://www.esri.com/software/arcgis/arcgisonline/services/geoservices
430
+ * **Limitations**: ?
431
+ * **Notes**: You can specify which projection you want to use by setting, for example: `Geocoder.configure(:esri => {:outSR => 102100})`.
432
+
386
433
 
387
434
  Caching
388
435
  -------
389
436
 
390
437
  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:
391
438
 
392
- Geocoder::Configuration.cache = Redis.new
439
+ Geocoder.configure(:cache => Redis.new)
393
440
 
394
441
  This example uses Redis, but the cache store can be any object that supports these methods:
395
442
 
@@ -402,7 +449,7 @@ Even a plain Ruby hash will work, though it's not a great choice (cleared out wh
402
449
 
403
450
  You can also set a custom prefix to be used for cache keys:
404
451
 
405
- Geocoder::Configuration.cache_prefix = "..."
452
+ Geocoder.configure(:cache_prefix => "...")
406
453
 
407
454
  By default the prefix is `geocoder:`
408
455
 
@@ -450,6 +497,30 @@ However, there can be only one set of latitude/longitude attributes, and whichev
450
497
 
451
498
  The reason for this is that we don't want ambiguity when doing distance calculations. We need a single, authoritative source for coordinates!
452
499
 
500
+ Once both forward and reverse geocoding has been applied, it is possible to call them sequentially.
501
+
502
+ For example:
503
+
504
+ class Venue
505
+
506
+ after_validation :geocode, :reverse_geocode
507
+
508
+ end
509
+
510
+ For certain geolocation services such as Google geolocation API this may cause issues during subsequent updates to database records if the longtitude and latitude coordinates cannot be associated known location address (on a large body of water for example). On subsequent callbacks the following call:
511
+
512
+ after_validation :geocode
513
+
514
+ will alter the longtitude and latitude attributes based on the location field, which would be the closest known location to the original coordinates. In this case it is better to add conditions to each call, as not to override coordinates that do not have known location addresses associated with them.
515
+
516
+ For example:
517
+
518
+ class Venue
519
+
520
+ after_validation :reverse_geocode, :if => :has_coordinates
521
+ after_validation :geocode, :if => :has_location, :unless => :has_coordinates
522
+
523
+ end
453
524
 
454
525
  Use Outside of Rails
455
526
  --------------------
@@ -458,7 +529,7 @@ You can use Geocoder outside of Rails by calling the `Geocoder.search` method:
458
529
 
459
530
  results = Geocoder.search("McCarren Park, Brooklyn, NY")
460
531
 
461
- This returns an array of `Geocoder::Result` objects with all information provided by the geocoding service. Please see above and in the code for details.
532
+ This returns an array of `Geocoder::Result` objects with all data provided by the geocoding service.
462
533
 
463
534
 
464
535
  Testing Apps that Use Geocoder
@@ -466,7 +537,7 @@ Testing Apps that Use Geocoder
466
537
 
467
538
  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:
468
539
 
469
- Geocoder::Configuration.lookup = :test
540
+ Geocoder.configure(:lookup => :test)
470
541
 
471
542
  Geocoder::Lookup::Test.add_stub(
472
543
  "New York, NY", [
@@ -482,8 +553,25 @@ When writing tests for an app that uses Geocoder it may be useful to avoid netwo
482
553
  ]
483
554
  )
484
555
 
485
- Now, any time Geocoder looks up "New York, NY" its results array will contain one result with the above attributes.
556
+ Now, any time Geocoder looks up "New York, NY" its results array will contain one result with the above attributes. You can also set a default stub:
486
557
 
558
+ Geocoder.configure(:lookup => :test)
559
+
560
+ Geocoder::Lookup::Test.set_default_stub(
561
+ [
562
+ {
563
+ 'latitude' => 40.7143528,
564
+ 'longitude' => -74.0059731,
565
+ 'address' => 'New York, NY, USA',
566
+ 'state' => 'New York',
567
+ 'state_code' => 'NY',
568
+ 'country' => 'United States',
569
+ 'country_code' => 'US'
570
+ }
571
+ ]
572
+ )
573
+
574
+ Any query that hasn't been explicitly stubbed will return that result.
487
575
 
488
576
  Command Line Interface
489
577
  ----------------------
@@ -502,7 +590,6 @@ When you install the Geocoder gem it adds a `geocode` command to your shell. You
502
590
 
503
591
  There are also a number of options for setting the geocoding API, key, and language, viewing the raw JSON reponse, and more. Please run `geocode -h` for details.
504
592
 
505
-
506
593
  Notes on MongoDB
507
594
  ----------------
508
595
 
@@ -524,6 +611,22 @@ Calling `obj.coordinates` directly returns the internal representation of the co
524
611
 
525
612
  For consistency with the rest of Geocoder, always use the `to_coordinates` method instead.
526
613
 
614
+ Notes on Non-Rails Frameworks
615
+ -----------------------------
616
+
617
+ If you are using Geocoder with ActiveRecord and a framework other than Rails (like Sinatra or Padrino) you will need to add this in your model before calling Geocoder methods:
618
+
619
+ extend Geocoder::Model::ActiveRecord
620
+
621
+ Optimisation of Distance Queries
622
+ --------------------------------
623
+
624
+ In MySQL and Postgres the finding of objects near a given point is speeded up by using a bounding box to limit the number of points over which a full distance calculation needs to be done.
625
+
626
+ To take advantage of this optimisation you need to add a composite index on latitude and longitude. In your Rails migration:
627
+
628
+ add_index :table, [:latitude, :longitude]
629
+
527
630
 
528
631
  Distance Queries in SQLite
529
632
  --------------------------
@@ -555,15 +658,58 @@ http://github.com/alexreisner/geocoder_test
555
658
  Error Handling
556
659
  --------------
557
660
 
558
- 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:
661
+ By default Geocoder will rescue any exceptions raised by calls to a geocoding service and return an empty array (using warn() to inform you of the error). You can override this on a per-exception basis, and also have Geocoder raise its own exceptions for certain events (eg: API quota exceeded) by using the `:always_raise` option:
559
662
 
560
- Geocoder::Configuration.always_raise = [SocketError, TimeoutError]
663
+ Geocoder.configure(:always_raise => [SocketError, TimeoutError])
561
664
 
562
665
  You can also do this to raise all exceptions:
563
666
 
564
- Geocoder::Configuration.always_raise = :all
667
+ Geocoder.configure(:always_raise => :all)
668
+
669
+ The raise-able exceptions are:
670
+
671
+ SocketError
672
+ TimeoutError
673
+ Geocoder::OverQueryLimitError
674
+ Geocoder::RequestDenied
675
+ Geocoder::InvalidRequest
676
+ Geocoder::InvalidApiKey
677
+
678
+ Note that not all lookups support all exceptions.
679
+
680
+
681
+ Troubleshooting
682
+ ---------------
683
+
684
+ ### Mongoid
685
+
686
+ If you get one of these errors:
687
+
688
+ uninitialized constant Geocoder::Model::Mongoid
689
+ uninitialized constant Geocoder::Model::Mongoid::Mongo
690
+
691
+ you should check your Gemfile to make sure the Mongoid gem is listed _before_ Geocoder. If Mongoid isn't loaded when Geocoder is initialized, Geocoder will not load support for Mongoid.
692
+
693
+ ### ActiveRecord
694
+
695
+ A lot of debugging time can be saved by understanding how Geocoder works with ActiveRecord. When you use the `near` scope or the `nearbys` method of a geocoded object, Geocoder creates an ActiveModel::Relation object which adds some attributes (eg: distance, bearing) to the SELECT clause. It also adds a condition to the WHERE clause to check that distance is within the given radius. Because the SELECT clause is modified, anything else that modifies the SELECT clause may produce strange results, for example:
696
+
697
+ * using the `pluck` method (selects only a single column)
698
+ * specifying another model through `includes` (selects columns from other tables)
699
+
700
+ ### Unexpected Responses from Geocoding Services
701
+
702
+ Take a look at the server's raw JSON response. You can do this by getting the request URL in an app console:
703
+
704
+ Geocoder::Lookup.get(:google).query_url(Geocoder::Query.new("..."))
705
+
706
+ Replace `:google` with the lookup you are using and replace `...` with the address you are trying to geocode. Then visit the returned URL in your web browser. Often the API will return an error message that helps you resolve the problem. If, after reading the raw response, you believe there is a problem with Geocoder, please post an issue and include both the URL and raw response body.
707
+
708
+
709
+ Reporting Issues
710
+ ----------------
565
711
 
566
- See `lib/geocoder/exceptions.rb` for a list of raise-able exceptions.
712
+ When reporting an issue, please list the version of Geocoder you are using and any relevant information about your application (Rails version, database type and version, etc). Also avoid vague language like "it doesn't work." Please describe as specifically as you can what behavior your are actually seeing (eg: an error message? a nil return value?).
567
713
 
568
714
 
569
715
  Known Issue
@@ -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(key, 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(key)
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))
@@ -1,10 +1,10 @@
1
1
  # This class implements a cache with simple delegation to the Redis store, but
2
2
  # when it creates a key/value pair, it also sends an EXPIRE command with a TTL.
3
3
  # It should be fairly simple to do the same thing with Memcached.
4
- class AutoexpireCache
5
- def initialize(store)
4
+ class AutoexpireCacheRedis
5
+ def initialize(store, ttl = 86400)
6
6
  @store = store
7
- @ttl = 86400
7
+ @ttl = ttl
8
8
  end
9
9
 
10
10
  def [](url)
@@ -25,6 +25,4 @@ class AutoexpireCache
25
25
  end
26
26
  end
27
27
 
28
- Geocoder.configure do |config|
29
- config.cache = AutoexpireCache.new(Redis.new)
30
- end
28
+ Geocoder.configure(:cache => AutoexpireCacheRedis.new(Redis.new))
@@ -1,25 +1,21 @@
1
- Geocoder.configure do |config|
2
- ## Configurable parameters: if you wish to change some configurable
3
- ## behaviour in Geocoder, feel free to uncomment the following lines
4
- ## and provide custom parameters.
1
+ Geocoder.configure(
2
+ # geocoding options
3
+ # :timeout => 3, # geocoding service timeout (secs)
4
+ # :lookup => :google, # name of geocoding service (symbol)
5
+ # :language => :en, # ISO-639 language code
6
+ # :use_https => false, # use HTTPS for lookup requests? (if supported)
7
+ # :http_proxy => nil, # HTTP proxy server (user:pass@host:port)
8
+ # :https_proxy => nil, # HTTPS proxy server (user:pass@host:port)
9
+ # :api_key => nil, # API key for geocoding service
10
+ # :cache => nil, # cache object (must respond to #[], #[]=, and #keys)
11
+ # :cache_prefix => "geocoder:", # prefix (string) to use for all cache keys
5
12
 
6
- # config.timeout = 3 # geocoding service timeout (secs)
7
- # config.lookup = :google # name of geocoding service (symbol)
8
- # config.language = :en # ISO-639 language code
9
- # config.use_https = false # use HTTPS for lookup requests? (if supported)
10
- # config.http_proxy = nil # HTTP proxy server (user:pass@host:port)
11
- # config.https_proxy = nil # HTTPS proxy server (user:pass@host:port)
12
- # config.api_key = nil # API key for geocoding service
13
- # config.cache = nil # cache object (must respond to #[], #[]=, and #keys)
14
- # config.cache_prefix = "geocoder:" # prefix (string) to use for all cache keys
15
-
16
- ## exceptions that should not be rescued by default
17
- ## (if you want to implement custom error handling);
18
- ## supports SocketError and TimeoutError
19
- # config.always_raise = []
20
-
21
- ## Calculation options
22
- # config.units = :mi # :km for kilometers or :mi for miles
23
- # config.distances = :linear # :spherical or :linear
24
- end
13
+ # exceptions that should not be rescued by default
14
+ # (if you want to implement custom error handling);
15
+ # supports SocketError and TimeoutError
16
+ # :always_raise => [],
25
17
 
18
+ # calculation options
19
+ # :units => :mi, # :km for kilometers or :mi for miles
20
+ # :distances => :linear # :spherical or :linear
21
+ )
@@ -15,6 +15,8 @@ module Geocoder
15
15
  store[key_for(url)]
16
16
  when store.respond_to?(:get)
17
17
  store.get key_for(url)
18
+ when store.respond_to?(:read)
19
+ store.read key_for(url)
18
20
  end
19
21
  end
20
22
 
@@ -27,6 +29,8 @@ module Geocoder
27
29
  store[key_for(url)] = value
28
30
  when store.respond_to?(:set)
29
31
  store.set key_for(url), value
32
+ when store.respond_to?(:write)
33
+ store.write key_for(url), value
30
34
  end
31
35
  end
32
36
 
@@ -78,7 +82,8 @@ module Geocoder
78
82
  end
79
83
 
80
84
  def expire_single_url(url)
81
- store.del(key_for(url))
85
+ key = key_for(url)
86
+ store.respond_to?(:del) ? store.del(key) : store.delete(key)
82
87
  end
83
88
  end
84
89
  end