geocoder-kb 1.2.6

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 (248) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +6 -0
  3. data/.travis.yml +31 -0
  4. data/CHANGELOG.md +384 -0
  5. data/LICENSE +20 -0
  6. data/README.md +1085 -0
  7. data/Rakefile +25 -0
  8. data/bin/geocode +5 -0
  9. data/examples/autoexpire_cache_dalli.rb +62 -0
  10. data/examples/autoexpire_cache_redis.rb +28 -0
  11. data/examples/cache_bypass.rb +48 -0
  12. data/examples/sidekiq_worker.rb +16 -0
  13. data/gemfiles/Gemfile.mongoid-2.4.x +16 -0
  14. data/lib/generators/geocoder/config/config_generator.rb +14 -0
  15. data/lib/generators/geocoder/config/templates/initializer.rb +21 -0
  16. data/lib/generators/geocoder/maxmind/geolite_city_generator.rb +28 -0
  17. data/lib/generators/geocoder/maxmind/geolite_country_generator.rb +28 -0
  18. data/lib/generators/geocoder/maxmind/templates/migration/geolite_city.rb +30 -0
  19. data/lib/generators/geocoder/maxmind/templates/migration/geolite_country.rb +17 -0
  20. data/lib/geocoder.rb +47 -0
  21. data/lib/geocoder/cache.rb +90 -0
  22. data/lib/geocoder/calculations.rb +428 -0
  23. data/lib/geocoder/cli.rb +121 -0
  24. data/lib/geocoder/configuration.rb +124 -0
  25. data/lib/geocoder/configuration_hash.rb +11 -0
  26. data/lib/geocoder/exceptions.rb +21 -0
  27. data/lib/geocoder/ip_address.rb +21 -0
  28. data/lib/geocoder/lookup.rb +102 -0
  29. data/lib/geocoder/lookups/amap.rb +55 -0
  30. data/lib/geocoder/lookups/baidu.rb +55 -0
  31. data/lib/geocoder/lookups/baidu_ip.rb +54 -0
  32. data/lib/geocoder/lookups/base.rb +302 -0
  33. data/lib/geocoder/lookups/bing.rb +59 -0
  34. data/lib/geocoder/lookups/dstk.rb +20 -0
  35. data/lib/geocoder/lookups/esri.rb +48 -0
  36. data/lib/geocoder/lookups/freegeoip.rb +47 -0
  37. data/lib/geocoder/lookups/geocoder_ca.rb +54 -0
  38. data/lib/geocoder/lookups/geocoder_us.rb +39 -0
  39. data/lib/geocoder/lookups/geocodio.rb +42 -0
  40. data/lib/geocoder/lookups/geoip2.rb +40 -0
  41. data/lib/geocoder/lookups/google.rb +67 -0
  42. data/lib/geocoder/lookups/google_places_details.rb +50 -0
  43. data/lib/geocoder/lookups/google_premier.rb +47 -0
  44. data/lib/geocoder/lookups/here.rb +62 -0
  45. data/lib/geocoder/lookups/mapquest.rb +60 -0
  46. data/lib/geocoder/lookups/maxmind.rb +90 -0
  47. data/lib/geocoder/lookups/maxmind_local.rb +58 -0
  48. data/lib/geocoder/lookups/nominatim.rb +52 -0
  49. data/lib/geocoder/lookups/okf.rb +43 -0
  50. data/lib/geocoder/lookups/opencagedata.rb +58 -0
  51. data/lib/geocoder/lookups/ovi.rb +62 -0
  52. data/lib/geocoder/lookups/pointpin.rb +68 -0
  53. data/lib/geocoder/lookups/postcode_anywhere_uk.rb +51 -0
  54. data/lib/geocoder/lookups/smarty_streets.rb +45 -0
  55. data/lib/geocoder/lookups/telize.rb +40 -0
  56. data/lib/geocoder/lookups/test.rb +44 -0
  57. data/lib/geocoder/lookups/yahoo.rb +88 -0
  58. data/lib/geocoder/lookups/yandex.rb +54 -0
  59. data/lib/geocoder/models/active_record.rb +50 -0
  60. data/lib/geocoder/models/base.rb +39 -0
  61. data/lib/geocoder/models/mongo_base.rb +64 -0
  62. data/lib/geocoder/models/mongo_mapper.rb +26 -0
  63. data/lib/geocoder/models/mongoid.rb +32 -0
  64. data/lib/geocoder/query.rb +111 -0
  65. data/lib/geocoder/railtie.rb +26 -0
  66. data/lib/geocoder/request.rb +25 -0
  67. data/lib/geocoder/results/amap.rb +85 -0
  68. data/lib/geocoder/results/baidu.rb +79 -0
  69. data/lib/geocoder/results/baidu_ip.rb +62 -0
  70. data/lib/geocoder/results/base.rb +67 -0
  71. data/lib/geocoder/results/bing.rb +48 -0
  72. data/lib/geocoder/results/dstk.rb +6 -0
  73. data/lib/geocoder/results/esri.rb +51 -0
  74. data/lib/geocoder/results/freegeoip.rb +45 -0
  75. data/lib/geocoder/results/geocoder_ca.rb +60 -0
  76. data/lib/geocoder/results/geocoder_us.rb +39 -0
  77. data/lib/geocoder/results/geocodio.rb +66 -0
  78. data/lib/geocoder/results/geoip2.rb +64 -0
  79. data/lib/geocoder/results/google.rb +124 -0
  80. data/lib/geocoder/results/google_places_details.rb +35 -0
  81. data/lib/geocoder/results/google_premier.rb +6 -0
  82. data/lib/geocoder/results/here.rb +62 -0
  83. data/lib/geocoder/results/mapquest.rb +51 -0
  84. data/lib/geocoder/results/maxmind.rb +135 -0
  85. data/lib/geocoder/results/maxmind_local.rb +49 -0
  86. data/lib/geocoder/results/nominatim.rb +94 -0
  87. data/lib/geocoder/results/okf.rb +106 -0
  88. data/lib/geocoder/results/opencagedata.rb +82 -0
  89. data/lib/geocoder/results/ovi.rb +62 -0
  90. data/lib/geocoder/results/pointpin.rb +44 -0
  91. data/lib/geocoder/results/postcode_anywhere_uk.rb +42 -0
  92. data/lib/geocoder/results/smarty_streets.rb +106 -0
  93. data/lib/geocoder/results/telize.rb +45 -0
  94. data/lib/geocoder/results/test.rb +33 -0
  95. data/lib/geocoder/results/yahoo.rb +55 -0
  96. data/lib/geocoder/results/yandex.rb +84 -0
  97. data/lib/geocoder/sql.rb +107 -0
  98. data/lib/geocoder/stores/active_record.rb +289 -0
  99. data/lib/geocoder/stores/base.rb +127 -0
  100. data/lib/geocoder/stores/mongo_base.rb +89 -0
  101. data/lib/geocoder/stores/mongo_mapper.rb +13 -0
  102. data/lib/geocoder/stores/mongoid.rb +13 -0
  103. data/lib/geocoder/version.rb +3 -0
  104. data/lib/hash_recursive_merge.rb +74 -0
  105. data/lib/maxmind_database.rb +109 -0
  106. data/lib/oauth_util.rb +112 -0
  107. data/lib/tasks/geocoder.rake +29 -0
  108. data/lib/tasks/maxmind.rake +73 -0
  109. data/test/fixtures/baidu_invalid_key +1 -0
  110. data/test/fixtures/baidu_ip_202_198_16_3 +19 -0
  111. data/test/fixtures/baidu_ip_invalid_key +1 -0
  112. data/test/fixtures/baidu_ip_no_results +1 -0
  113. data/test/fixtures/baidu_no_results +1 -0
  114. data/test/fixtures/baidu_reverse +1 -0
  115. data/test/fixtures/baidu_shanghai_pearl_tower +12 -0
  116. data/test/fixtures/bing_invalid_key +1 -0
  117. data/test/fixtures/bing_madison_square_garden +40 -0
  118. data/test/fixtures/bing_no_results +16 -0
  119. data/test/fixtures/bing_reverse +42 -0
  120. data/test/fixtures/cloudmade_invalid_key +1 -0
  121. data/test/fixtures/cloudmade_madison_square_garden +1 -0
  122. data/test/fixtures/cloudmade_no_results +1 -0
  123. data/test/fixtures/esri_madison_square_garden +59 -0
  124. data/test/fixtures/esri_no_results +8 -0
  125. data/test/fixtures/esri_reverse +21 -0
  126. data/test/fixtures/freegeoip_74_200_247_59 +12 -0
  127. data/test/fixtures/freegeoip_no_results +1 -0
  128. data/test/fixtures/geocoder_ca_madison_square_garden +1 -0
  129. data/test/fixtures/geocoder_ca_no_results +1 -0
  130. data/test/fixtures/geocoder_ca_reverse +34 -0
  131. data/test/fixtures/geocoder_us_madison_square_garden +1 -0
  132. data/test/fixtures/geocoder_us_no_results +1 -0
  133. data/test/fixtures/geocodio_1101_pennsylvania_ave +1 -0
  134. data/test/fixtures/geocodio_bad_api_key +3 -0
  135. data/test/fixtures/geocodio_invalid +4 -0
  136. data/test/fixtures/geocodio_no_results +1 -0
  137. data/test/fixtures/geocodio_over_query_limit +4 -0
  138. data/test/fixtures/google_garbage +456 -0
  139. data/test/fixtures/google_madison_square_garden +57 -0
  140. data/test/fixtures/google_no_city_data +44 -0
  141. data/test/fixtures/google_no_locality +51 -0
  142. data/test/fixtures/google_no_results +4 -0
  143. data/test/fixtures/google_over_limit +4 -0
  144. data/test/fixtures/google_places_details_invalid_request +4 -0
  145. data/test/fixtures/google_places_details_madison_square_garden +120 -0
  146. data/test/fixtures/google_places_details_no_results +4 -0
  147. data/test/fixtures/google_places_details_no_reviews +60 -0
  148. data/test/fixtures/google_places_details_no_types +66 -0
  149. data/test/fixtures/here_madison_square_garden +72 -0
  150. data/test/fixtures/here_no_results +8 -0
  151. data/test/fixtures/mapquest_error +16 -0
  152. data/test/fixtures/mapquest_invalid_api_key +16 -0
  153. data/test/fixtures/mapquest_invalid_request +16 -0
  154. data/test/fixtures/mapquest_madison_square_garden +52 -0
  155. data/test/fixtures/mapquest_no_results +16 -0
  156. data/test/fixtures/maxmind_24_24_24_21 +1 -0
  157. data/test/fixtures/maxmind_24_24_24_22 +1 -0
  158. data/test/fixtures/maxmind_24_24_24_23 +1 -0
  159. data/test/fixtures/maxmind_24_24_24_24 +1 -0
  160. data/test/fixtures/maxmind_74_200_247_59 +1 -0
  161. data/test/fixtures/maxmind_invalid_key +1 -0
  162. data/test/fixtures/maxmind_no_results +1 -0
  163. data/test/fixtures/nominatim_madison_square_garden +150 -0
  164. data/test/fixtures/nominatim_no_results +1 -0
  165. data/test/fixtures/nominatim_over_limit +1 -0
  166. data/test/fixtures/okf_kirstinmaki +67 -0
  167. data/test/fixtures/okf_no_results +4 -0
  168. data/test/fixtures/opencagedata_invalid_api_key +25 -0
  169. data/test/fixtures/opencagedata_invalid_request +26 -0
  170. data/test/fixtures/opencagedata_madison_square_garden +73 -0
  171. data/test/fixtures/opencagedata_no_results +29 -0
  172. data/test/fixtures/opencagedata_over_limit +31 -0
  173. data/test/fixtures/ovi_madison_square_garden +72 -0
  174. data/test/fixtures/ovi_no_results +8 -0
  175. data/test/fixtures/pointpin_10_10_10_10 +1 -0
  176. data/test/fixtures/pointpin_555_555_555_555 +1 -0
  177. data/test/fixtures/pointpin_80_111_555_555 +1 -0
  178. data/test/fixtures/pointpin_no_results +1 -0
  179. data/test/fixtures/postcode_anywhere_uk_geocode_v2_00_WR26NJ +1 -0
  180. data/test/fixtures/postcode_anywhere_uk_geocode_v2_00_generic_error +1 -0
  181. data/test/fixtures/postcode_anywhere_uk_geocode_v2_00_hampshire +1 -0
  182. data/test/fixtures/postcode_anywhere_uk_geocode_v2_00_key_limit_exceeded +1 -0
  183. data/test/fixtures/postcode_anywhere_uk_geocode_v2_00_no_results +1 -0
  184. data/test/fixtures/postcode_anywhere_uk_geocode_v2_00_romsey +1 -0
  185. data/test/fixtures/postcode_anywhere_uk_geocode_v2_00_unknown_key +1 -0
  186. data/test/fixtures/smarty_streets_11211 +1 -0
  187. data/test/fixtures/smarty_streets_madison_square_garden +47 -0
  188. data/test/fixtures/smarty_streets_no_results +1 -0
  189. data/test/fixtures/telize_10_10_10_10 +1 -0
  190. data/test/fixtures/telize_555_555_555_555 +4 -0
  191. data/test/fixtures/telize_74_200_247_59 +1 -0
  192. data/test/fixtures/telize_no_results +1 -0
  193. data/test/fixtures/yahoo_error +1 -0
  194. data/test/fixtures/yahoo_invalid_key +2 -0
  195. data/test/fixtures/yahoo_madison_square_garden +52 -0
  196. data/test/fixtures/yahoo_no_results +10 -0
  197. data/test/fixtures/yahoo_over_limit +2 -0
  198. data/test/fixtures/yandex_canada_rue_dupuis_14 +446 -0
  199. data/test/fixtures/yandex_invalid_key +1 -0
  200. data/test/fixtures/yandex_kremlin +48 -0
  201. data/test/fixtures/yandex_new_york +1 -0
  202. data/test/fixtures/yandex_no_city_and_town +112 -0
  203. data/test/fixtures/yandex_no_results +16 -0
  204. data/test/integration/http_client_test.rb +31 -0
  205. data/test/mongoid_test_helper.rb +43 -0
  206. data/test/test_helper.rb +416 -0
  207. data/test/unit/active_record_test.rb +16 -0
  208. data/test/unit/cache_test.rb +37 -0
  209. data/test/unit/calculations_test.rb +220 -0
  210. data/test/unit/configuration_test.rb +55 -0
  211. data/test/unit/error_handling_test.rb +56 -0
  212. data/test/unit/geocoder_test.rb +78 -0
  213. data/test/unit/https_test.rb +17 -0
  214. data/test/unit/ip_address_test.rb +27 -0
  215. data/test/unit/lookup_test.rb +153 -0
  216. data/test/unit/lookups/bing_test.rb +68 -0
  217. data/test/unit/lookups/dstk_test.rb +26 -0
  218. data/test/unit/lookups/esri_test.rb +48 -0
  219. data/test/unit/lookups/freegeoip_test.rb +27 -0
  220. data/test/unit/lookups/geocoder_ca_test.rb +17 -0
  221. data/test/unit/lookups/geocodio_test.rb +55 -0
  222. data/test/unit/lookups/geoip2_test.rb +27 -0
  223. data/test/unit/lookups/google_places_details_test.rb +122 -0
  224. data/test/unit/lookups/google_premier_test.rb +22 -0
  225. data/test/unit/lookups/google_test.rb +84 -0
  226. data/test/unit/lookups/mapquest_test.rb +60 -0
  227. data/test/unit/lookups/maxmind_local_test.rb +28 -0
  228. data/test/unit/lookups/maxmind_test.rb +63 -0
  229. data/test/unit/lookups/nominatim_test.rb +31 -0
  230. data/test/unit/lookups/okf_test.rb +38 -0
  231. data/test/unit/lookups/opencagedata_test.rb +64 -0
  232. data/test/unit/lookups/pointpin_test.rb +30 -0
  233. data/test/unit/lookups/postcode_anywhere_uk_test.rb +70 -0
  234. data/test/unit/lookups/smarty_streets_test.rb +71 -0
  235. data/test/unit/lookups/telize_test.rb +36 -0
  236. data/test/unit/lookups/yahoo_test.rb +35 -0
  237. data/test/unit/method_aliases_test.rb +26 -0
  238. data/test/unit/model_test.rb +38 -0
  239. data/test/unit/mongoid_test.rb +47 -0
  240. data/test/unit/near_test.rb +87 -0
  241. data/test/unit/oauth_util_test.rb +31 -0
  242. data/test/unit/proxy_test.rb +37 -0
  243. data/test/unit/query_test.rb +52 -0
  244. data/test/unit/rake_task_test.rb +21 -0
  245. data/test/unit/request_test.rb +35 -0
  246. data/test/unit/result_test.rb +72 -0
  247. data/test/unit/test_mode_test.rb +70 -0
  248. metadata +294 -0
@@ -0,0 +1,127 @@
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
+ query_options = [:lookup, :ip_lookup, :language].inject({}) do |hash, key|
105
+ if options.has_key?(key)
106
+ val = options[key]
107
+ hash[key] = val.respond_to?(:call) ? val.call(self) : val
108
+ end
109
+ hash
110
+ end
111
+ results = Geocoder.search(query, query_options)
112
+
113
+ # execute custom block, if specified in configuration
114
+ block_key = reverse ? :reverse_block : :geocode_block
115
+ if custom_block = options[block_key]
116
+ custom_block.call(self, results)
117
+
118
+ # else execute block passed directly to this method,
119
+ # which generally performs the "auto-assigns"
120
+ elsif block_given?
121
+ yield(self, results)
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
127
+
@@ -0,0 +1,89 @@
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
+ version = RUBY_VERSION.split('.').map { |i| i.to_i }
28
+ empty = version[0] < 2 && version[1] < 9 ? BSON::OrderedHash.new : {}
29
+
30
+ conds = empty.clone
31
+ field = geocoder_options[:coordinates]
32
+ conds[field] = empty.clone
33
+ conds[field]["$nearSphere"] = coords.reverse
34
+
35
+ if radius
36
+ conds[field]["$maxDistance"] = \
37
+ Geocoder::Calculations.distance_to_radians(radius, options[:units])
38
+ end
39
+
40
+ if obj = options[:exclude]
41
+ conds[:_id.ne] = obj.id
42
+ end
43
+ where(conds)
44
+ }
45
+ end
46
+ end
47
+
48
+ ##
49
+ # Coordinates [lat,lon] of the object.
50
+ # This method always returns coordinates in lat,lon order,
51
+ # even though internally they are stored in the opposite order.
52
+ #
53
+ def to_coordinates
54
+ coords = send(self.class.geocoder_options[:coordinates])
55
+ coords.is_a?(Array) ? coords.reverse : []
56
+ end
57
+
58
+ ##
59
+ # Look up coordinates and assign to +latitude+ and +longitude+ attributes
60
+ # (or other as specified in +geocoded_by+). Returns coordinates (array).
61
+ #
62
+ def geocode
63
+ do_lookup(false) do |o,rs|
64
+ if r = rs.first
65
+ unless r.coordinates.nil?
66
+ o.__send__ "#{self.class.geocoder_options[:coordinates]}=", r.coordinates.reverse
67
+ end
68
+ r.coordinates
69
+ end
70
+ end
71
+ end
72
+
73
+ ##
74
+ # Look up address and assign to +address+ attribute (or other as specified
75
+ # in +reverse_geocoded_by+). Returns address (string).
76
+ #
77
+ def reverse_geocode
78
+ do_lookup(true) do |o,rs|
79
+ if r = rs.first
80
+ unless r.address.nil?
81
+ o.__send__ "#{self.class.geocoder_options[:fetched_address]}=", r.address
82
+ end
83
+ r.address
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+
@@ -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.2.6"
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
@@ -0,0 +1,109 @@
1
+ require 'csv'
2
+ require 'net/http'
3
+
4
+ module Geocoder
5
+ module MaxmindDatabase
6
+ extend self
7
+
8
+ def download(package, dir = "tmp")
9
+ filepath = File.expand_path(File.join(dir, archive_filename(package)))
10
+ open(filepath, 'wb') do |file|
11
+ uri = URI.parse(archive_url(package))
12
+ Net::HTTP.start(uri.host, uri.port) do |http|
13
+ http.request_get(uri.path) do |resp|
14
+ # TODO: show progress
15
+ resp.read_body do |segment|
16
+ file.write(segment)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ def insert(package, dir = "tmp")
24
+ data_files(package).each do |filepath,table|
25
+ print "Resetting table #{table}..."
26
+ ActiveRecord::Base.connection.execute("DELETE FROM #{table}")
27
+ puts "done"
28
+ insert_into_table(table, filepath)
29
+ end
30
+ end
31
+
32
+ def archive_filename(package)
33
+ p = archive_url_path(package)
34
+ s = !(pos = p.rindex('/')).nil? && pos + 1 || 0
35
+ p[s..-1]
36
+ end
37
+
38
+ private # -------------------------------------------------------------
39
+
40
+ def table_columns(table_name)
41
+ {
42
+ maxmind_geolite_city_blocks: %w[start_ip_num end_ip_num loc_id],
43
+ maxmind_geolite_city_location: %w[loc_id country region city postal_code latitude longitude metro_code area_code],
44
+ maxmind_geolite_country: %w[start_ip end_ip start_ip_num end_ip_num country_code country]
45
+ }[table_name.to_sym]
46
+ end
47
+
48
+ def insert_into_table(table, filepath)
49
+ start_time = Time.now
50
+ print "Loading data for table #{table}"
51
+ rows = []
52
+ columns = table_columns(table)
53
+ CSV.foreach(filepath, encoding: "ISO-8859-1") do |line|
54
+ # Some files have header rows.
55
+ # skip if starts with "Copyright" or "locId" or "startIpNum"
56
+ next if line.first.match(/[A-z]/)
57
+ rows << line.to_a
58
+ if rows.size == 10000
59
+ insert_rows(table, columns, rows)
60
+ rows = []
61
+ print "."
62
+ end
63
+ end
64
+ insert_rows(table, columns, rows) if rows.size > 0
65
+ puts "done (#{Time.now - start_time} seconds)"
66
+ end
67
+
68
+ def insert_rows(table, headers, rows)
69
+ value_strings = rows.map do |row|
70
+ "(" + row.map{ |col| sql_escaped_value(col) }.join(',') + ")"
71
+ end
72
+ q = "INSERT INTO #{table} (#{headers.join(',')}) " +
73
+ "VALUES #{value_strings.join(',')}"
74
+ ActiveRecord::Base.connection.execute(q)
75
+ end
76
+
77
+ def sql_escaped_value(value)
78
+ value.to_i.to_s == value ? value :
79
+ ActiveRecord::Base.connection.quote(value)
80
+ end
81
+
82
+ def data_files(package, dir = "tmp")
83
+ case package
84
+ when :geolite_city_csv
85
+ # use the last two in case multiple versions exist
86
+ files = Dir.glob(File.join(dir, "GeoLiteCity_*/*.csv"))[-2..-1].sort
87
+ Hash[*files.zip(["maxmind_geolite_city_blocks", "maxmind_geolite_city_location"]).flatten]
88
+ when :geolite_country_csv
89
+ {File.join(dir, "GeoIPCountryWhois.csv") => "maxmind_geolite_country"}
90
+ end
91
+ end
92
+
93
+ def archive_url(package)
94
+ base_url + archive_url_path(package)
95
+ end
96
+
97
+ def archive_url_path(package)
98
+ {
99
+ geolite_country_csv: "GeoIPCountryCSV.zip",
100
+ geolite_city_csv: "GeoLiteCity_CSV/GeoLiteCity-latest.zip",
101
+ geolite_asn_csv: "asnum/GeoIPASNum2.zip"
102
+ }[package]
103
+ end
104
+
105
+ def base_url
106
+ "http://geolite.maxmind.com/download/geoip/database/"
107
+ end
108
+ end
109
+ end
@@ -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.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