geocoder-sgonyea 1.1.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. data/.gitignore +5 -0
  2. data/.travis.yml +23 -0
  3. data/CHANGELOG.md +298 -0
  4. data/LICENSE +20 -0
  5. data/README.md +656 -0
  6. data/Rakefile +25 -0
  7. data/bin/geocode +5 -0
  8. data/examples/autoexpire_cache.rb +28 -0
  9. data/gemfiles/Gemfile.mongoid-2.4.x +15 -0
  10. data/lib/generators/geocoder/config/config_generator.rb +14 -0
  11. data/lib/generators/geocoder/config/templates/initializer.rb +21 -0
  12. data/lib/geocoder.rb +55 -0
  13. data/lib/geocoder/cache.rb +85 -0
  14. data/lib/geocoder/calculations.rb +319 -0
  15. data/lib/geocoder/cli.rb +114 -0
  16. data/lib/geocoder/configuration.rb +130 -0
  17. data/lib/geocoder/configuration_hash.rb +11 -0
  18. data/lib/geocoder/exceptions.rb +21 -0
  19. data/lib/geocoder/lookup.rb +82 -0
  20. data/lib/geocoder/lookups/base.rb +250 -0
  21. data/lib/geocoder/lookups/bing.rb +47 -0
  22. data/lib/geocoder/lookups/freegeoip.rb +47 -0
  23. data/lib/geocoder/lookups/geocoder_ca.rb +54 -0
  24. data/lib/geocoder/lookups/google.rb +62 -0
  25. data/lib/geocoder/lookups/google_premier.rb +47 -0
  26. data/lib/geocoder/lookups/mapquest.rb +43 -0
  27. data/lib/geocoder/lookups/maxmind.rb +88 -0
  28. data/lib/geocoder/lookups/nominatim.rb +45 -0
  29. data/lib/geocoder/lookups/ovi.rb +52 -0
  30. data/lib/geocoder/lookups/test.rb +38 -0
  31. data/lib/geocoder/lookups/yahoo.rb +84 -0
  32. data/lib/geocoder/lookups/yandex.rb +54 -0
  33. data/lib/geocoder/models/active_record.rb +46 -0
  34. data/lib/geocoder/models/base.rb +42 -0
  35. data/lib/geocoder/models/mongo_base.rb +60 -0
  36. data/lib/geocoder/models/mongo_mapper.rb +26 -0
  37. data/lib/geocoder/models/mongoid.rb +32 -0
  38. data/lib/geocoder/query.rb +103 -0
  39. data/lib/geocoder/railtie.rb +26 -0
  40. data/lib/geocoder/request.rb +23 -0
  41. data/lib/geocoder/results/base.rb +67 -0
  42. data/lib/geocoder/results/bing.rb +48 -0
  43. data/lib/geocoder/results/freegeoip.rb +45 -0
  44. data/lib/geocoder/results/geocoder_ca.rb +60 -0
  45. data/lib/geocoder/results/google.rb +106 -0
  46. data/lib/geocoder/results/google_premier.rb +6 -0
  47. data/lib/geocoder/results/mapquest.rb +51 -0
  48. data/lib/geocoder/results/maxmind.rb +136 -0
  49. data/lib/geocoder/results/nominatim.rb +94 -0
  50. data/lib/geocoder/results/ovi.rb +62 -0
  51. data/lib/geocoder/results/test.rb +16 -0
  52. data/lib/geocoder/results/yahoo.rb +55 -0
  53. data/lib/geocoder/results/yandex.rb +80 -0
  54. data/lib/geocoder/sql.rb +106 -0
  55. data/lib/geocoder/stores/active_record.rb +259 -0
  56. data/lib/geocoder/stores/base.rb +120 -0
  57. data/lib/geocoder/stores/mongo_base.rb +85 -0
  58. data/lib/geocoder/stores/mongo_mapper.rb +13 -0
  59. data/lib/geocoder/stores/mongoid.rb +13 -0
  60. data/lib/geocoder/version.rb +3 -0
  61. data/lib/hash_recursive_merge.rb +74 -0
  62. data/lib/oauth_util.rb +112 -0
  63. data/lib/tasks/geocoder.rake +25 -0
  64. data/test/active_record_test.rb +15 -0
  65. data/test/cache_test.rb +19 -0
  66. data/test/calculations_test.rb +195 -0
  67. data/test/configuration_test.rb +78 -0
  68. data/test/custom_block_test.rb +32 -0
  69. data/test/error_handling_test.rb +43 -0
  70. data/test/fixtures/bing_invalid_key +1 -0
  71. data/test/fixtures/bing_madison_square_garden +40 -0
  72. data/test/fixtures/bing_no_results +16 -0
  73. data/test/fixtures/bing_reverse +42 -0
  74. data/test/fixtures/freegeoip_74_200_247_59 +12 -0
  75. data/test/fixtures/freegeoip_no_results +1 -0
  76. data/test/fixtures/geocoder_ca_madison_square_garden +1 -0
  77. data/test/fixtures/geocoder_ca_no_results +1 -0
  78. data/test/fixtures/geocoder_ca_reverse +34 -0
  79. data/test/fixtures/google_garbage +456 -0
  80. data/test/fixtures/google_madison_square_garden +57 -0
  81. data/test/fixtures/google_no_city_data +44 -0
  82. data/test/fixtures/google_no_locality +51 -0
  83. data/test/fixtures/google_no_results +4 -0
  84. data/test/fixtures/mapquest_madison_square_garden +52 -0
  85. data/test/fixtures/mapquest_no_results +7 -0
  86. data/test/fixtures/maxmind_24_24_24_21 +1 -0
  87. data/test/fixtures/maxmind_24_24_24_22 +1 -0
  88. data/test/fixtures/maxmind_24_24_24_23 +1 -0
  89. data/test/fixtures/maxmind_24_24_24_24 +1 -0
  90. data/test/fixtures/maxmind_74_200_247_59 +1 -0
  91. data/test/fixtures/maxmind_invalid_key +1 -0
  92. data/test/fixtures/maxmind_no_results +1 -0
  93. data/test/fixtures/nominatim_madison_square_garden +150 -0
  94. data/test/fixtures/nominatim_no_results +1 -0
  95. data/test/fixtures/ovi_madison_square_garden +72 -0
  96. data/test/fixtures/ovi_no_results +8 -0
  97. data/test/fixtures/yahoo_error +1 -0
  98. data/test/fixtures/yahoo_invalid_key +2 -0
  99. data/test/fixtures/yahoo_madison_square_garden +52 -0
  100. data/test/fixtures/yahoo_no_results +10 -0
  101. data/test/fixtures/yahoo_over_limit +2 -0
  102. data/test/fixtures/yandex_invalid_key +1 -0
  103. data/test/fixtures/yandex_kremlin +48 -0
  104. data/test/fixtures/yandex_no_city_and_town +112 -0
  105. data/test/fixtures/yandex_no_results +16 -0
  106. data/test/geocoder_test.rb +59 -0
  107. data/test/https_test.rb +16 -0
  108. data/test/integration/smoke_test.rb +26 -0
  109. data/test/lookup_test.rb +116 -0
  110. data/test/method_aliases_test.rb +25 -0
  111. data/test/mongoid_test.rb +39 -0
  112. data/test/mongoid_test_helper.rb +43 -0
  113. data/test/near_test.rb +43 -0
  114. data/test/oauth_util_test.rb +30 -0
  115. data/test/proxy_test.rb +23 -0
  116. data/test/query_test.rb +51 -0
  117. data/test/request_test.rb +29 -0
  118. data/test/result_test.rb +42 -0
  119. data/test/services_test.rb +277 -0
  120. data/test/test_helper.rb +279 -0
  121. data/test/test_mode_test.rb +50 -0
  122. metadata +170 -0
@@ -0,0 +1,120 @@
1
+ module Geocoder
2
+ module Store
3
+ module Base
4
+
5
+ ##
6
+ # Is this object geocoded? (Does it have latitude and longitude?)
7
+ #
8
+ def geocoded?
9
+ to_coordinates.compact.size > 0
10
+ end
11
+
12
+ ##
13
+ # Coordinates [lat,lon] of the object.
14
+ #
15
+ def to_coordinates
16
+ [:latitude, :longitude].map{ |i| send self.class.geocoder_options[i] }
17
+ end
18
+
19
+ ##
20
+ # Calculate the distance from the object to an arbitrary point.
21
+ # See Geocoder::Calculations.distance_between for ways of specifying
22
+ # the point. Also takes a symbol specifying the units
23
+ # (:mi or :km; can be specified in Geocoder configuration).
24
+ #
25
+ def distance_to(point, units = nil)
26
+ units ||= self.class.geocoder_options[:units]
27
+ return nil unless geocoded?
28
+ Geocoder::Calculations.distance_between(
29
+ to_coordinates, point, :units => units)
30
+ end
31
+
32
+ alias_method :distance_from, :distance_to
33
+
34
+ ##
35
+ # Calculate the bearing from the object to another point.
36
+ # See Geocoder::Calculations.distance_between for
37
+ # ways of specifying the point.
38
+ #
39
+ def bearing_to(point, options = {})
40
+ options[:method] ||= self.class.geocoder_options[:method]
41
+ return nil unless geocoded?
42
+ Geocoder::Calculations.bearing_between(
43
+ to_coordinates, point, options)
44
+ end
45
+
46
+ ##
47
+ # Calculate the bearing from another point to the object.
48
+ # See Geocoder::Calculations.distance_between for
49
+ # ways of specifying the point.
50
+ #
51
+ def bearing_from(point, options = {})
52
+ options[:method] ||= self.class.geocoder_options[:method]
53
+ return nil unless geocoded?
54
+ Geocoder::Calculations.bearing_between(
55
+ point, to_coordinates, options)
56
+ end
57
+
58
+ ##
59
+ # Get nearby geocoded objects.
60
+ # Takes the same options hash as the near class method (scope).
61
+ # Returns nil if the object is not geocoded.
62
+ #
63
+ def nearbys(radius = 20, options = {})
64
+ return nil unless geocoded?
65
+ options.merge!(:exclude => self) unless send(self.class.primary_key).nil?
66
+ self.class.near(self, radius, options)
67
+ end
68
+
69
+ ##
70
+ # Look up coordinates and assign to +latitude+ and +longitude+ attributes
71
+ # (or other as specified in +geocoded_by+). Returns coordinates (array).
72
+ #
73
+ def geocode
74
+ fail
75
+ end
76
+
77
+ ##
78
+ # Look up address and assign to +address+ attribute (or other as specified
79
+ # in +reverse_geocoded_by+). Returns address (string).
80
+ #
81
+ def reverse_geocode
82
+ fail
83
+ end
84
+
85
+ private # --------------------------------------------------------------
86
+
87
+ ##
88
+ # Look up geographic data based on object attributes (configured in
89
+ # geocoded_by or reverse_geocoded_by) and handle the results with the
90
+ # block (given to geocoded_by or reverse_geocoded_by). The block is
91
+ # given two-arguments: the object being geocoded and an array of
92
+ # Geocoder::Result objects).
93
+ #
94
+ def do_lookup(reverse = false)
95
+ options = self.class.geocoder_options
96
+ if reverse and options[:reverse_geocode]
97
+ query = to_coordinates
98
+ elsif !reverse and options[:geocode]
99
+ query = send(options[:user_address])
100
+ else
101
+ return
102
+ end
103
+
104
+ results = Geocoder.search(query)
105
+
106
+ # execute custom block, if specified in configuration
107
+ block_key = reverse ? :reverse_block : :geocode_block
108
+ if custom_block = options[block_key]
109
+ custom_block.call(self, results)
110
+
111
+ # else execute block passed directly to this method,
112
+ # which generally performs the "auto-assigns"
113
+ elsif block_given?
114
+ yield(self, results)
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+
@@ -0,0 +1,85 @@
1
+ module Geocoder::Store
2
+ module MongoBase
3
+
4
+ def self.included_by_model(base)
5
+ base.class_eval do
6
+
7
+ scope :geocoded, lambda {
8
+ where(geocoder_options[:coordinates].ne => nil)
9
+ }
10
+
11
+ scope :not_geocoded, lambda {
12
+ where(geocoder_options[:coordinates] => nil)
13
+ }
14
+
15
+ scope :near, lambda{ |location, *args|
16
+ coords = Geocoder::Calculations.extract_coordinates(location)
17
+
18
+ # no results if no lat/lon given
19
+ return where(:id => false) unless coords.is_a?(Array)
20
+
21
+ radius = args.size > 0 ? args.shift : 20
22
+ options = args.size > 0 ? args.shift : {}
23
+ options[:units] ||= geocoder_options[:units]
24
+
25
+ # Use BSON::OrderedHash if Ruby's hashes are unordered.
26
+ # Conditions must be in order required by indexes (see mongo gem).
27
+ empty = RUBY_VERSION.split('.')[1].to_i < 9 ? BSON::OrderedHash.new : {}
28
+
29
+ conds = empty.clone
30
+ field = geocoder_options[:coordinates]
31
+ conds[field] = empty.clone
32
+ conds[field]["$nearSphere"] = coords.reverse
33
+ conds[field]["$maxDistance"] = \
34
+ Geocoder::Calculations.distance_to_radians(radius, options[:units])
35
+
36
+ if obj = options[:exclude]
37
+ conds[:_id.ne] = obj.id
38
+ end
39
+ where(conds)
40
+ }
41
+ end
42
+ end
43
+
44
+ ##
45
+ # Coordinates [lat,lon] of the object.
46
+ # This method always returns coordinates in lat,lon order,
47
+ # even though internally they are stored in the opposite order.
48
+ #
49
+ def to_coordinates
50
+ coords = send(self.class.geocoder_options[:coordinates])
51
+ coords.is_a?(Array) ? coords.reverse : []
52
+ end
53
+
54
+ ##
55
+ # Look up coordinates and assign to +latitude+ and +longitude+ attributes
56
+ # (or other as specified in +geocoded_by+). Returns coordinates (array).
57
+ #
58
+ def geocode
59
+ do_lookup(false) do |o,rs|
60
+ if r = rs.first
61
+ unless r.coordinates.nil?
62
+ o.__send__ "#{self.class.geocoder_options[:coordinates]}=", r.coordinates.reverse
63
+ end
64
+ r.coordinates
65
+ end
66
+ end
67
+ end
68
+
69
+ ##
70
+ # Look up address and assign to +address+ attribute (or other as specified
71
+ # in +reverse_geocoded_by+). Returns address (string).
72
+ #
73
+ def reverse_geocode
74
+ do_lookup(true) do |o,rs|
75
+ if r = rs.first
76
+ unless r.address.nil?
77
+ o.__send__ "#{self.class.geocoder_options[:fetched_address]}=", r.address
78
+ end
79
+ r.address
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+
@@ -0,0 +1,13 @@
1
+ require 'geocoder/stores/base'
2
+ require 'geocoder/stores/mongo_base'
3
+
4
+ module Geocoder::Store
5
+ module MongoMapper
6
+ include Base
7
+ include MongoBase
8
+
9
+ def self.included(base)
10
+ MongoBase.included_by_model(base)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'geocoder/stores/base'
2
+ require 'geocoder/stores/mongo_base'
3
+
4
+ module Geocoder::Store
5
+ module Mongoid
6
+ include Base
7
+ include MongoBase
8
+
9
+ def self.included(base)
10
+ MongoBase.included_by_model(base)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ module Geocoder
2
+ VERSION = "1.1.6.1"
3
+ end
@@ -0,0 +1,74 @@
1
+ #
2
+ # = Hash Recursive Merge
3
+ #
4
+ # Merges a Ruby Hash recursively, Also known as deep merge.
5
+ # Recursive version of Hash#merge and Hash#merge!.
6
+ #
7
+ # Category:: Ruby
8
+ # Package:: Hash
9
+ # Author:: Simone Carletti <weppos@weppos.net>
10
+ # Copyright:: 2007-2008 The Authors
11
+ # License:: MIT License
12
+ # Link:: http://www.simonecarletti.com/
13
+ # Source:: http://gist.github.com/gists/6391/
14
+ #
15
+ module HashRecursiveMerge
16
+
17
+ #
18
+ # Recursive version of Hash#merge!
19
+ #
20
+ # Adds the contents of +other_hash+ to +hsh+,
21
+ # merging entries in +hsh+ with duplicate keys with those from +other_hash+.
22
+ #
23
+ # Compared with Hash#merge!, this method supports nested hashes.
24
+ # When both +hsh+ and +other_hash+ contains an entry with the same key,
25
+ # it merges and returns the values from both arrays.
26
+ #
27
+ # h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}}
28
+ # h2 = {"b" => 254, "c" => {"c1" => 16, "c3" => 94}}
29
+ # h1.rmerge!(h2) #=> {"a" => 100, "b" => 254, "c" => {"c1" => 16, "c2" => 14, "c3" => 94}}
30
+ #
31
+ # Simply using Hash#merge! would return
32
+ #
33
+ # h1.merge!(h2) #=> {"a" => 100, "b" = >254, "c" => {"c1" => 16, "c3" => 94}}
34
+ #
35
+ def rmerge!(other_hash)
36
+ merge!(other_hash) do |key, oldval, newval|
37
+ oldval.class == self.class ? oldval.rmerge!(newval) : newval
38
+ end
39
+ end
40
+
41
+ #
42
+ # Recursive version of Hash#merge
43
+ #
44
+ # Compared with Hash#merge!, this method supports nested hashes.
45
+ # When both +hsh+ and +other_hash+ contains an entry with the same key,
46
+ # it merges and returns the values from both arrays.
47
+ #
48
+ # Compared with Hash#merge, this method provides a different approch
49
+ # for merging nasted hashes.
50
+ # If the value of a given key is an Hash and both +other_hash+ abd +hsh
51
+ # includes the same key, the value is merged instead replaced with
52
+ # +other_hash+ value.
53
+ #
54
+ # h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}}
55
+ # h2 = {"b" => 254, "c" => {"c1" => 16, "c3" => 94}}
56
+ # h1.rmerge(h2) #=> {"a" => 100, "b" => 254, "c" => {"c1" => 16, "c2" => 14, "c3" => 94}}
57
+ #
58
+ # Simply using Hash#merge would return
59
+ #
60
+ # h1.merge(h2) #=> {"a" => 100, "b" = >254, "c" => {"c1" => 16, "c3" => 94}}
61
+ #
62
+ def rmerge(other_hash)
63
+ r = {}
64
+ merge(other_hash) do |key, oldval, newval|
65
+ r[key] = oldval.class == self.class ? oldval.rmerge(newval) : newval
66
+ end
67
+ end
68
+
69
+ end
70
+
71
+
72
+ class Hash
73
+ include HashRecursiveMerge
74
+ end
data/lib/oauth_util.rb ADDED
@@ -0,0 +1,112 @@
1
+ # A utility for signing an url using OAuth in a way that's convenient for debugging
2
+ # Note: the standard Ruby OAuth lib is here http://github.com/mojodna/oauth
3
+ # Source: http://gist.github.com/383159
4
+ # License: http://gist.github.com/375593
5
+ # Usage: see example.rb below
6
+ #
7
+ # NOTE: This file has been modified from the original Gist:
8
+ #
9
+ # 1. Fix to prevent param-array conversion, as mentioned in Gist comment.
10
+ # 2. Query string escaping has been changed. See:
11
+ # https://github.com/alexreisner/geocoder/pull/360
12
+ #
13
+
14
+ require 'uri'
15
+ require 'cgi'
16
+ require 'openssl'
17
+ require 'base64'
18
+
19
+ class OauthUtil
20
+
21
+ attr_accessor :consumer_key, :consumer_secret, :token, :token_secret, :req_method,
22
+ :sig_method, :oauth_version, :callback_url, :params, :req_url, :base_str
23
+
24
+ def initialize
25
+ @consumer_key = ''
26
+ @consumer_secret = ''
27
+ @token = ''
28
+ @token_secret = ''
29
+ @req_method = 'GET'
30
+ @sig_method = 'HMAC-SHA1'
31
+ @oauth_version = '1.0'
32
+ @callback_url = ''
33
+ end
34
+
35
+ # openssl::random_bytes returns non-word chars, which need to be removed. using alt method to get length
36
+ # ref http://snippets.dzone.com/posts/show/491
37
+ def nonce
38
+ Array.new( 5 ) { rand(256) }.pack('C*').unpack('H*').first
39
+ end
40
+
41
+ def percent_encode( string )
42
+
43
+ # ref http://snippets.dzone.com/posts/show/1260
44
+ return URI.escape( string, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]") ).gsub('*', '%2A')
45
+ end
46
+
47
+ # @ref http://oauth.net/core/1.0/#rfc.section.9.2
48
+ def signature
49
+ key = percent_encode( @consumer_secret ) + '&' + percent_encode( @token_secret )
50
+
51
+ # ref: http://blog.nathanielbibler.com/post/63031273/openssl-hmac-vs-ruby-hmac-benchmarks
52
+ digest = OpenSSL::Digest::Digest.new( 'sha1' )
53
+ hmac = OpenSSL::HMAC.digest( digest, key, @base_str )
54
+
55
+ # ref http://groups.google.com/group/oauth-ruby/browse_thread/thread/9110ed8c8f3cae81
56
+ Base64.encode64( hmac ).chomp.gsub( /\n/, '' )
57
+ end
58
+
59
+ # sort (very important as it affects the signature), concat, and percent encode
60
+ # @ref http://oauth.net/core/1.0/#rfc.section.9.1.1
61
+ # @ref http://oauth.net/core/1.0/#9.2.1
62
+ # @ref http://oauth.net/core/1.0/#rfc.section.A.5.1
63
+ def query_string
64
+ pairs = []
65
+ @params.sort.each { | key, val |
66
+ pairs.push( "#{ CGI.escape(key.to_s).gsub(/%(5B|5D)/n) { [$1].pack('H*') } }=#{ CGI.escape(val.to_s) }" )
67
+ }
68
+ pairs.join '&'
69
+ end
70
+
71
+ # organize params & create signature
72
+ def sign( parsed_url )
73
+
74
+ @params = {
75
+ 'oauth_consumer_key' => @consumer_key,
76
+ 'oauth_nonce' => nonce,
77
+ 'oauth_signature_method' => @sig_method,
78
+ 'oauth_timestamp' => Time.now.to_i.to_s,
79
+ 'oauth_version' => @oauth_version
80
+ }
81
+
82
+ # if url has query, merge key/values into params obj overwriting defaults
83
+ if parsed_url.query
84
+ CGI.parse( parsed_url.query ).each do |k,v|
85
+ if v.is_a?(Array) && v.count == 1
86
+ @params[k] = v.first
87
+ else
88
+ @params[k] = v
89
+ end
90
+ end
91
+ end
92
+
93
+ # @ref http://oauth.net/core/1.0/#rfc.section.9.1.2
94
+ @req_url = parsed_url.scheme + '://' + parsed_url.host + parsed_url.path
95
+
96
+ # create base str. make it an object attr for ez debugging
97
+ # ref http://oauth.net/core/1.0/#anchor14
98
+ @base_str = [
99
+ @req_method,
100
+ percent_encode( req_url ),
101
+
102
+ # normalization is just x-www-form-urlencoded
103
+ percent_encode( query_string )
104
+
105
+ ].join( '&' )
106
+
107
+ # add signature
108
+ @params[ 'oauth_signature' ] = signature
109
+
110
+ return self
111
+ end
112
+ end
@@ -0,0 +1,25 @@
1
+ namespace :geocode do
2
+ desc "Geocode all objects without coordinates."
3
+ task :all => :environment do
4
+ class_name = ENV['CLASS'] || ENV['class']
5
+ raise "Please specify a CLASS (model)" unless class_name
6
+ klass = class_from_string(class_name)
7
+
8
+ klass.not_geocoded.each do |obj|
9
+ obj.geocode; obj.save
10
+ end
11
+ end
12
+ end
13
+
14
+ ##
15
+ # Get a class object from the string given in the shell environment.
16
+ # Similar to ActiveSupport's +constantize+ method.
17
+ #
18
+ def class_from_string(class_name)
19
+ parts = class_name.split("::")
20
+ constant = Object
21
+ parts.each do |part|
22
+ constant = constant.const_get(part)
23
+ end
24
+ constant
25
+ end