geocoder2 0.1.0

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 (146) hide show
  1. data/.gitignore +5 -0
  2. data/.travis.yml +27 -0
  3. data/CHANGELOG.md +329 -0
  4. data/LICENSE +20 -0
  5. data/README.md +796 -0
  6. data/Rakefile +25 -0
  7. data/bin/geocode2 +5 -0
  8. data/examples/autoexpire_cache_dalli.rb +62 -0
  9. data/examples/autoexpire_cache_redis.rb +28 -0
  10. data/examples/cache_bypass.rb +48 -0
  11. data/gemfiles/Gemfile.mongoid-2.4.x +15 -0
  12. data/json?address=26+leonard+street%2C+Belmont&key=AIzaSyDoltU6YL8XeIQrSLFGk6ZfpKaWkPukwYQ&language=en +68 -0
  13. data/lib/generators/geocoder2/config/config_generator.rb +14 -0
  14. data/lib/generators/geocoder2/config/templates/initializer.rb +21 -0
  15. data/lib/geocoder2/cache.rb +89 -0
  16. data/lib/geocoder2/calculations.rb +389 -0
  17. data/lib/geocoder2/cli.rb +121 -0
  18. data/lib/geocoder2/configuration.rb +130 -0
  19. data/lib/geocoder2/configuration_hash.rb +11 -0
  20. data/lib/geocoder2/exceptions.rb +21 -0
  21. data/lib/geocoder2/lookup.rb +86 -0
  22. data/lib/geocoder2/lookups/baidu.rb +54 -0
  23. data/lib/geocoder2/lookups/base.rb +266 -0
  24. data/lib/geocoder2/lookups/bing.rb +47 -0
  25. data/lib/geocoder2/lookups/dstk.rb +20 -0
  26. data/lib/geocoder2/lookups/esri.rb +48 -0
  27. data/lib/geocoder2/lookups/freegeoip.rb +43 -0
  28. data/lib/geocoder2/lookups/geocoder_ca.rb +54 -0
  29. data/lib/geocoder2/lookups/geocoder_us.rb +39 -0
  30. data/lib/geocoder2/lookups/google.rb +69 -0
  31. data/lib/geocoder2/lookups/google_premier.rb +47 -0
  32. data/lib/geocoder2/lookups/mapquest.rb +59 -0
  33. data/lib/geocoder2/lookups/maxmind.rb +88 -0
  34. data/lib/geocoder2/lookups/nominatim.rb +44 -0
  35. data/lib/geocoder2/lookups/ovi.rb +62 -0
  36. data/lib/geocoder2/lookups/test.rb +44 -0
  37. data/lib/geocoder2/lookups/yahoo.rb +86 -0
  38. data/lib/geocoder2/lookups/yandex.rb +54 -0
  39. data/lib/geocoder2/models/active_record.rb +46 -0
  40. data/lib/geocoder2/models/base.rb +42 -0
  41. data/lib/geocoder2/models/mongo_base.rb +60 -0
  42. data/lib/geocoder2/models/mongo_mapper.rb +26 -0
  43. data/lib/geocoder2/models/mongoid.rb +32 -0
  44. data/lib/geocoder2/query.rb +107 -0
  45. data/lib/geocoder2/railtie.rb +26 -0
  46. data/lib/geocoder2/request.rb +23 -0
  47. data/lib/geocoder2/results/baidu.rb +79 -0
  48. data/lib/geocoder2/results/base.rb +67 -0
  49. data/lib/geocoder2/results/bing.rb +48 -0
  50. data/lib/geocoder2/results/dstk.rb +6 -0
  51. data/lib/geocoder2/results/esri.rb +51 -0
  52. data/lib/geocoder2/results/freegeoip.rb +45 -0
  53. data/lib/geocoder2/results/geocoder_ca.rb +60 -0
  54. data/lib/geocoder2/results/geocoder_us.rb +39 -0
  55. data/lib/geocoder2/results/google.rb +124 -0
  56. data/lib/geocoder2/results/google_premier.rb +6 -0
  57. data/lib/geocoder2/results/mapquest.rb +51 -0
  58. data/lib/geocoder2/results/maxmind.rb +135 -0
  59. data/lib/geocoder2/results/nominatim.rb +94 -0
  60. data/lib/geocoder2/results/ovi.rb +62 -0
  61. data/lib/geocoder2/results/test.rb +16 -0
  62. data/lib/geocoder2/results/yahoo.rb +55 -0
  63. data/lib/geocoder2/results/yandex.rb +80 -0
  64. data/lib/geocoder2/sql.rb +106 -0
  65. data/lib/geocoder2/stores/active_record.rb +272 -0
  66. data/lib/geocoder2/stores/base.rb +120 -0
  67. data/lib/geocoder2/stores/mongo_base.rb +89 -0
  68. data/lib/geocoder2/stores/mongo_mapper.rb +13 -0
  69. data/lib/geocoder2/stores/mongoid.rb +13 -0
  70. data/lib/geocoder2/version.rb +3 -0
  71. data/lib/geocoder2.rb +55 -0
  72. data/lib/hash_recursive_merge.rb +74 -0
  73. data/lib/oauth_util.rb +112 -0
  74. data/lib/tasks/geocoder2.rake +27 -0
  75. data/test/active_record_test.rb +15 -0
  76. data/test/cache_test.rb +35 -0
  77. data/test/calculations_test.rb +211 -0
  78. data/test/configuration_test.rb +78 -0
  79. data/test/custom_block_test.rb +32 -0
  80. data/test/error_handling_test.rb +43 -0
  81. data/test/fixtures/baidu_invalid_key +1 -0
  82. data/test/fixtures/baidu_no_results +1 -0
  83. data/test/fixtures/baidu_reverse +1 -0
  84. data/test/fixtures/baidu_shanghai_pearl_tower +12 -0
  85. data/test/fixtures/bing_invalid_key +1 -0
  86. data/test/fixtures/bing_madison_square_garden +40 -0
  87. data/test/fixtures/bing_no_results +16 -0
  88. data/test/fixtures/bing_reverse +42 -0
  89. data/test/fixtures/esri_madison_square_garden +59 -0
  90. data/test/fixtures/esri_no_results +8 -0
  91. data/test/fixtures/esri_reverse +21 -0
  92. data/test/fixtures/freegeoip_74_200_247_59 +12 -0
  93. data/test/fixtures/freegeoip_no_results +1 -0
  94. data/test/fixtures/geocoder_ca_madison_square_garden +1 -0
  95. data/test/fixtures/geocoder_ca_no_results +1 -0
  96. data/test/fixtures/geocoder_ca_reverse +34 -0
  97. data/test/fixtures/geocoder_us_madison_square_garden +1 -0
  98. data/test/fixtures/geocoder_us_no_results +1 -0
  99. data/test/fixtures/google_garbage +456 -0
  100. data/test/fixtures/google_madison_square_garden +57 -0
  101. data/test/fixtures/google_no_city_data +44 -0
  102. data/test/fixtures/google_no_locality +51 -0
  103. data/test/fixtures/google_no_results +4 -0
  104. data/test/fixtures/google_over_limit +4 -0
  105. data/test/fixtures/mapquest_error +16 -0
  106. data/test/fixtures/mapquest_invalid_api_key +16 -0
  107. data/test/fixtures/mapquest_invalid_request +16 -0
  108. data/test/fixtures/mapquest_madison_square_garden +52 -0
  109. data/test/fixtures/mapquest_no_results +16 -0
  110. data/test/fixtures/maxmind_24_24_24_21 +1 -0
  111. data/test/fixtures/maxmind_24_24_24_22 +1 -0
  112. data/test/fixtures/maxmind_24_24_24_23 +1 -0
  113. data/test/fixtures/maxmind_24_24_24_24 +1 -0
  114. data/test/fixtures/maxmind_74_200_247_59 +1 -0
  115. data/test/fixtures/maxmind_invalid_key +1 -0
  116. data/test/fixtures/maxmind_no_results +1 -0
  117. data/test/fixtures/nominatim_madison_square_garden +150 -0
  118. data/test/fixtures/nominatim_no_results +1 -0
  119. data/test/fixtures/ovi_madison_square_garden +72 -0
  120. data/test/fixtures/ovi_no_results +8 -0
  121. data/test/fixtures/yahoo_error +1 -0
  122. data/test/fixtures/yahoo_invalid_key +2 -0
  123. data/test/fixtures/yahoo_madison_square_garden +52 -0
  124. data/test/fixtures/yahoo_no_results +10 -0
  125. data/test/fixtures/yahoo_over_limit +2 -0
  126. data/test/fixtures/yandex_invalid_key +1 -0
  127. data/test/fixtures/yandex_kremlin +48 -0
  128. data/test/fixtures/yandex_no_city_and_town +112 -0
  129. data/test/fixtures/yandex_no_results +16 -0
  130. data/test/geocoder_test.rb +59 -0
  131. data/test/https_test.rb +16 -0
  132. data/test/integration/smoke_test.rb +26 -0
  133. data/test/lookup_test.rb +117 -0
  134. data/test/method_aliases_test.rb +25 -0
  135. data/test/mongoid_test.rb +46 -0
  136. data/test/mongoid_test_helper.rb +43 -0
  137. data/test/near_test.rb +61 -0
  138. data/test/oauth_util_test.rb +30 -0
  139. data/test/proxy_test.rb +36 -0
  140. data/test/query_test.rb +52 -0
  141. data/test/request_test.rb +29 -0
  142. data/test/result_test.rb +42 -0
  143. data/test/services_test.rb +393 -0
  144. data/test/test_helper.rb +289 -0
  145. data/test/test_mode_test.rb +59 -0
  146. metadata +213 -0
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rake/testtask'
5
+ Rake::TestTask.new(:test) do |test|
6
+ test.libs << 'lib' << 'test'
7
+ test.pattern = 'test/*_test.rb'
8
+ test.verbose = true
9
+ end
10
+
11
+ Rake::TestTask.new(:integration) do |test|
12
+ test.libs << 'lib' << 'test'
13
+ test.pattern = 'test/integration/*_test.rb'
14
+ test.verbose = true
15
+ end
16
+
17
+ task :default => [:test]
18
+
19
+ require 'rdoc/task'
20
+ Rake::RDocTask.new do |rdoc|
21
+ rdoc.rdoc_dir = 'rdoc'
22
+ rdoc.title = "Geocoder2 #{Geocoder2::VERSION}"
23
+ rdoc.rdoc_files.include('*.rdoc')
24
+ rdoc.rdoc_files.include('lib/**/*.rb')
25
+ end
data/bin/geocode2 ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'geocoder2/cli'
4
+
5
+ Geocoder2::Cli.run ARGV
@@ -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 = 'Geocoder2DalliClientKeys'
10
+ @ttl = ttl
11
+ end
12
+
13
+ def [](url)
14
+ res = @store.get(url)
15
+ res = YAML::load(res) if res.present?
16
+ res
17
+ end
18
+
19
+ def []=(url, value)
20
+ if value.nil?
21
+ del(url)
22
+ else
23
+ key_cache_add(url) if @store.add(url, YAML::dump(value), @ttl)
24
+ end
25
+ value
26
+ end
27
+
28
+ def keys
29
+ key_cache
30
+ end
31
+
32
+ def del(url)
33
+ key_cache_delete(url) if @store.delete(url)
34
+ end
35
+
36
+ private
37
+
38
+ def key_cache
39
+ the_keys = @store.get(@keys)
40
+ if the_keys.nil?
41
+ @store.add(@keys, YAML::dump([]))
42
+ []
43
+ else
44
+ YAML::load(the_keys)
45
+ end
46
+ end
47
+
48
+ def key_cache_add(key)
49
+ @store.replace(@keys, YAML::dump(key_cache << key))
50
+ end
51
+
52
+ def key_cache_delete(key)
53
+ tmp = key_cache
54
+ tmp.delete(key)
55
+ @store.replace(@keys, YAML::dump(tmp))
56
+ end
57
+ end
58
+
59
+ # Here Dalli is set up as on Heroku using the Memcachier gem.
60
+ # https://devcenter.heroku.com/articles/memcachier#ruby
61
+ # On other setups you might have to specify your Memcached server in Dalli::Client.new
62
+ Geocoder2.configure(:cache => AutoexpireCacheDalli.new(Dalli::Client.new))
@@ -0,0 +1,28 @@
1
+ # This class implements a cache with simple delegation to the Redis store, but
2
+ # when it creates a key/value pair, it also sends an EXPIRE command with a TTL.
3
+ # It should be fairly simple to do the same thing with Memcached.
4
+ class AutoexpireCacheRedis
5
+ def initialize(store, ttl = 86400)
6
+ @store = store
7
+ @ttl = ttl
8
+ end
9
+
10
+ def [](url)
11
+ @store.[](url)
12
+ end
13
+
14
+ def []=(url, value)
15
+ @store.[]=(url, value)
16
+ @store.expire(url, @ttl)
17
+ end
18
+
19
+ def keys
20
+ @store.keys
21
+ end
22
+
23
+ def del(url)
24
+ @store.del(url)
25
+ end
26
+ end
27
+
28
+ Geocoder2.configure(:cache => AutoexpireCacheRedis.new(Redis.new))
@@ -0,0 +1,48 @@
1
+ # This class allows you to configure how Geocoder2 should treat errors that occur when
2
+ # the cache is not available.
3
+ # Configure it like this
4
+ # config/initializers/geocoder2.rb
5
+ # Geocoder2.configure(
6
+ # :cache => Geocoder2::CacheBypass.new(Redis.new)
7
+ # )
8
+ #
9
+ # Depending on the value of @bypass this will either
10
+ # raise the exception (true) or swallow it and pretend the cache did not return a hit (false)
11
+ #
12
+ class Geocoder2::CacheBypass
13
+ def initialize(target, bypass = true)
14
+ @target = target
15
+ @bypass = bypass
16
+ end
17
+
18
+
19
+ def [](key)
20
+ with_bypass { @target[key] }
21
+ end
22
+
23
+ def []=(key, value)
24
+ with_bypass(value) { @target[key] = value }
25
+ end
26
+
27
+ def keys
28
+ with_bypass([]) { @target.keys }
29
+ end
30
+
31
+ def del(key)
32
+ with_bypass { @target.del(key) }
33
+ end
34
+
35
+ private
36
+
37
+ def with_bypass(return_value_if_exception = nil, &block)
38
+ begin
39
+ yield
40
+ rescue
41
+ if @bypass
42
+ return_value_if_exception
43
+ else
44
+ raise # reraise original exception
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,15 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec :path => '..'
4
+
5
+ group :development, :test do
6
+ gem 'rake'
7
+ gem 'mongoid', '2.4.11'
8
+ gem 'bson_ext', :platforms => :ruby
9
+
10
+ gem 'rails'
11
+
12
+ platforms :jruby do
13
+ gem 'jruby-openssl'
14
+ end
15
+ end
@@ -0,0 +1,68 @@
1
+ {
2
+ "results" : [
3
+ {
4
+ "address_components" : [
5
+ {
6
+ "long_name" : "26",
7
+ "short_name" : "26",
8
+ "types" : [ "street_number" ]
9
+ },
10
+ {
11
+ "long_name" : "Leonard Street",
12
+ "short_name" : "Leonard St",
13
+ "types" : [ "route" ]
14
+ },
15
+ {
16
+ "long_name" : "Belmont",
17
+ "short_name" : "Belmont",
18
+ "types" : [ "locality", "political" ]
19
+ },
20
+ {
21
+ "long_name" : "Greater Geelong City",
22
+ "short_name" : "Greater Geelong",
23
+ "types" : [ "administrative_area_level_2", "political" ]
24
+ },
25
+ {
26
+ "long_name" : "Victoria",
27
+ "short_name" : "VIC",
28
+ "types" : [ "administrative_area_level_1", "political" ]
29
+ },
30
+ {
31
+ "long_name" : "Australia",
32
+ "short_name" : "AU",
33
+ "types" : [ "country", "political" ]
34
+ },
35
+ {
36
+ "long_name" : "3216",
37
+ "short_name" : "3216",
38
+ "types" : [ "postal_code" ]
39
+ }
40
+ ],
41
+ "formatted_address" : "26 Leonard St, Belmont VIC 3216, Australia",
42
+ "geometry" : {
43
+ "location" : {
44
+ "lat" : -38.1704428,
45
+ "lng" : 144.3313951
46
+ },
47
+ "location_type" : "ROOFTOP",
48
+ "viewport" : {
49
+ "northeast" : {
50
+ "lat" : -38.1690938197085,
51
+ "lng" : 144.3327440802915
52
+ },
53
+ "southwest" : {
54
+ "lat" : -38.1717917802915,
55
+ "lng" : 144.3300461197085
56
+ }
57
+ }
58
+ },
59
+ "place_id" : "ChIJzz9XwJ0T1GoRKPvo8BszH70",
60
+ "plus_code" : {
61
+ "compound_code" : "R8HJ+RH Geelong, Victoria, Australia",
62
+ "global_code" : "4RH6R8HJ+RH"
63
+ },
64
+ "types" : [ "street_address" ]
65
+ }
66
+ ],
67
+ "status" : "OK"
68
+ }
@@ -0,0 +1,14 @@
1
+ require 'rails/generators'
2
+
3
+ module Geocoder2
4
+ class ConfigGenerator < Rails::Generators::Base
5
+ source_root File.expand_path("../templates", __FILE__)
6
+
7
+ desc "This generator creates an initializer file at config/initializers, " +
8
+ "with the default configuration options for Geocoder2."
9
+ def add_initializer
10
+ template "initializer.rb", "config/initializers/geocoder2.rb"
11
+ end
12
+ end
13
+ end
14
+
@@ -0,0 +1,21 @@
1
+ Geocoder2.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 => "geocoder2:", # prefix (string) to use for all cache keys
12
+
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 => [],
17
+
18
+ # calculation options
19
+ # :units => :mi, # :km for kilometers or :mi for miles
20
+ # :distances => :linear # :spherical or :linear
21
+ )
@@ -0,0 +1,89 @@
1
+ module Geocoder2
2
+ class Cache
3
+
4
+ def initialize(store, prefix)
5
+ @store = store
6
+ @prefix = prefix
7
+ end
8
+
9
+ ##
10
+ # Read from the Cache.
11
+ #
12
+ def [](url)
13
+ interpret case
14
+ when store.respond_to?(:[])
15
+ store[key_for(url)]
16
+ when store.respond_to?(:get)
17
+ store.get key_for(url)
18
+ when store.respond_to?(:read)
19
+ store.read key_for(url)
20
+ end
21
+ end
22
+
23
+ ##
24
+ # Write to the Cache.
25
+ #
26
+ def []=(url, value)
27
+ case
28
+ when store.respond_to?(:[]=)
29
+ store[key_for(url)] = value
30
+ when store.respond_to?(:set)
31
+ store.set key_for(url), value
32
+ when store.respond_to?(:write)
33
+ store.write key_for(url), value
34
+ end
35
+ end
36
+
37
+ ##
38
+ # Delete cache entry for given URL,
39
+ # or pass <tt>:all</tt> to clear all URLs.
40
+ #
41
+ def expire(url)
42
+ if url == :all
43
+ urls.each{ |u| expire(u) }
44
+ else
45
+ expire_single_url(url)
46
+ end
47
+ end
48
+
49
+
50
+ private # ----------------------------------------------------------------
51
+
52
+ attr_reader :prefix, :store
53
+
54
+ ##
55
+ # Cache key for a given URL.
56
+ #
57
+ def key_for(url)
58
+ [prefix, url].join
59
+ end
60
+
61
+ ##
62
+ # Array of keys with the currently configured prefix
63
+ # that have non-nil values.
64
+ #
65
+ def keys
66
+ store.keys.select{ |k| k.match /^#{prefix}/ and interpret(store[k]) }
67
+ end
68
+
69
+ ##
70
+ # Array of cached URLs.
71
+ #
72
+ def urls
73
+ keys.map{ |k| k[/^#{prefix}(.*)/, 1] }
74
+ end
75
+
76
+ ##
77
+ # Clean up value before returning. Namely, convert empty string to nil.
78
+ # (Some key/value stores return empty string instead of nil.)
79
+ #
80
+ def interpret(value)
81
+ value == "" ? nil : value
82
+ end
83
+
84
+ def expire_single_url(url)
85
+ key = key_for(url)
86
+ store.respond_to?(:del) ? store.del(key) : store.delete(key)
87
+ end
88
+ end
89
+ end