bw-geocoder 1.2.5

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 (233) hide show
  1. data/.gitignore +6 -0
  2. data/.travis.yml +31 -0
  3. data/CHANGELOG.md +377 -0
  4. data/LICENSE +20 -0
  5. data/README.md +1041 -0
  6. data/Rakefile +25 -0
  7. data/bin/geocode +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 +16 -0
  12. data/lib/generators/geocoder/config/config_generator.rb +14 -0
  13. data/lib/generators/geocoder/config/templates/initializer.rb +21 -0
  14. data/lib/generators/geocoder/maxmind/geolite_city_generator.rb +28 -0
  15. data/lib/generators/geocoder/maxmind/geolite_country_generator.rb +28 -0
  16. data/lib/generators/geocoder/maxmind/templates/migration/geolite_city.rb +30 -0
  17. data/lib/generators/geocoder/maxmind/templates/migration/geolite_country.rb +17 -0
  18. data/lib/geocoder/cache.rb +90 -0
  19. data/lib/geocoder/calculations.rb +428 -0
  20. data/lib/geocoder/cli.rb +121 -0
  21. data/lib/geocoder/configuration.rb +124 -0
  22. data/lib/geocoder/configuration_hash.rb +11 -0
  23. data/lib/geocoder/exceptions.rb +21 -0
  24. data/lib/geocoder/ip_address.rb +21 -0
  25. data/lib/geocoder/lookup.rb +100 -0
  26. data/lib/geocoder/lookups/baidu.rb +55 -0
  27. data/lib/geocoder/lookups/baidu_ip.rb +54 -0
  28. data/lib/geocoder/lookups/base.rb +302 -0
  29. data/lib/geocoder/lookups/bing.rb +59 -0
  30. data/lib/geocoder/lookups/dstk.rb +20 -0
  31. data/lib/geocoder/lookups/esri.rb +48 -0
  32. data/lib/geocoder/lookups/freegeoip.rb +47 -0
  33. data/lib/geocoder/lookups/geocoder_ca.rb +54 -0
  34. data/lib/geocoder/lookups/geocoder_us.rb +39 -0
  35. data/lib/geocoder/lookups/geocodio.rb +42 -0
  36. data/lib/geocoder/lookups/google.rb +67 -0
  37. data/lib/geocoder/lookups/google_places_details.rb +50 -0
  38. data/lib/geocoder/lookups/google_premier.rb +47 -0
  39. data/lib/geocoder/lookups/here.rb +62 -0
  40. data/lib/geocoder/lookups/ip_address_labs.rb +43 -0
  41. data/lib/geocoder/lookups/mapquest.rb +60 -0
  42. data/lib/geocoder/lookups/maxmind.rb +90 -0
  43. data/lib/geocoder/lookups/maxmind_local.rb +58 -0
  44. data/lib/geocoder/lookups/nominatim.rb +52 -0
  45. data/lib/geocoder/lookups/okf.rb +43 -0
  46. data/lib/geocoder/lookups/opencagedata.rb +58 -0
  47. data/lib/geocoder/lookups/ovi.rb +62 -0
  48. data/lib/geocoder/lookups/pointpin.rb +68 -0
  49. data/lib/geocoder/lookups/smarty_streets.rb +45 -0
  50. data/lib/geocoder/lookups/telize.rb +40 -0
  51. data/lib/geocoder/lookups/test.rb +44 -0
  52. data/lib/geocoder/lookups/yahoo.rb +88 -0
  53. data/lib/geocoder/lookups/yandex.rb +54 -0
  54. data/lib/geocoder/models/active_record.rb +50 -0
  55. data/lib/geocoder/models/base.rb +39 -0
  56. data/lib/geocoder/models/mongo_base.rb +64 -0
  57. data/lib/geocoder/models/mongo_mapper.rb +26 -0
  58. data/lib/geocoder/models/mongoid.rb +32 -0
  59. data/lib/geocoder/query.rb +111 -0
  60. data/lib/geocoder/railtie.rb +26 -0
  61. data/lib/geocoder/request.rb +25 -0
  62. data/lib/geocoder/results/baidu.rb +79 -0
  63. data/lib/geocoder/results/baidu_ip.rb +62 -0
  64. data/lib/geocoder/results/base.rb +67 -0
  65. data/lib/geocoder/results/bing.rb +48 -0
  66. data/lib/geocoder/results/dstk.rb +6 -0
  67. data/lib/geocoder/results/esri.rb +51 -0
  68. data/lib/geocoder/results/freegeoip.rb +45 -0
  69. data/lib/geocoder/results/geocoder_ca.rb +60 -0
  70. data/lib/geocoder/results/geocoder_us.rb +39 -0
  71. data/lib/geocoder/results/geocodio.rb +66 -0
  72. data/lib/geocoder/results/google.rb +124 -0
  73. data/lib/geocoder/results/google_places_details.rb +35 -0
  74. data/lib/geocoder/results/google_premier.rb +6 -0
  75. data/lib/geocoder/results/here.rb +62 -0
  76. data/lib/geocoder/results/ip_address_labs.rb +78 -0
  77. data/lib/geocoder/results/mapquest.rb +51 -0
  78. data/lib/geocoder/results/maxmind.rb +135 -0
  79. data/lib/geocoder/results/maxmind_local.rb +49 -0
  80. data/lib/geocoder/results/nominatim.rb +94 -0
  81. data/lib/geocoder/results/okf.rb +106 -0
  82. data/lib/geocoder/results/opencagedata.rb +82 -0
  83. data/lib/geocoder/results/ovi.rb +62 -0
  84. data/lib/geocoder/results/pointpin.rb +44 -0
  85. data/lib/geocoder/results/smarty_streets.rb +106 -0
  86. data/lib/geocoder/results/telize.rb +45 -0
  87. data/lib/geocoder/results/test.rb +33 -0
  88. data/lib/geocoder/results/yahoo.rb +55 -0
  89. data/lib/geocoder/results/yandex.rb +84 -0
  90. data/lib/geocoder/sql.rb +107 -0
  91. data/lib/geocoder/stores/active_record.rb +278 -0
  92. data/lib/geocoder/stores/base.rb +127 -0
  93. data/lib/geocoder/stores/mongo_base.rb +89 -0
  94. data/lib/geocoder/stores/mongo_mapper.rb +13 -0
  95. data/lib/geocoder/stores/mongoid.rb +13 -0
  96. data/lib/geocoder/version.rb +3 -0
  97. data/lib/geocoder.rb +47 -0
  98. data/lib/hash_recursive_merge.rb +74 -0
  99. data/lib/maxmind_database.rb +109 -0
  100. data/lib/oauth_util.rb +112 -0
  101. data/lib/tasks/geocoder.rake +29 -0
  102. data/lib/tasks/maxmind.rake +73 -0
  103. data/test/fixtures/baidu_invalid_key +1 -0
  104. data/test/fixtures/baidu_ip_202_198_16_3 +19 -0
  105. data/test/fixtures/baidu_ip_invalid_key +1 -0
  106. data/test/fixtures/baidu_ip_no_results +1 -0
  107. data/test/fixtures/baidu_no_results +1 -0
  108. data/test/fixtures/baidu_reverse +1 -0
  109. data/test/fixtures/baidu_shanghai_pearl_tower +12 -0
  110. data/test/fixtures/bing_invalid_key +1 -0
  111. data/test/fixtures/bing_madison_square_garden +40 -0
  112. data/test/fixtures/bing_no_results +16 -0
  113. data/test/fixtures/bing_reverse +42 -0
  114. data/test/fixtures/cloudmade_invalid_key +1 -0
  115. data/test/fixtures/cloudmade_madison_square_garden +1 -0
  116. data/test/fixtures/cloudmade_no_results +1 -0
  117. data/test/fixtures/esri_madison_square_garden +59 -0
  118. data/test/fixtures/esri_no_results +8 -0
  119. data/test/fixtures/esri_reverse +21 -0
  120. data/test/fixtures/freegeoip_74_200_247_59 +12 -0
  121. data/test/fixtures/freegeoip_no_results +1 -0
  122. data/test/fixtures/geocoder_ca_madison_square_garden +1 -0
  123. data/test/fixtures/geocoder_ca_no_results +1 -0
  124. data/test/fixtures/geocoder_ca_reverse +34 -0
  125. data/test/fixtures/geocoder_us_madison_square_garden +1 -0
  126. data/test/fixtures/geocoder_us_no_results +1 -0
  127. data/test/fixtures/geocodio_1101_pennsylvania_ave +1 -0
  128. data/test/fixtures/geocodio_bad_api_key +3 -0
  129. data/test/fixtures/geocodio_invalid +4 -0
  130. data/test/fixtures/geocodio_no_results +1 -0
  131. data/test/fixtures/geocodio_over_query_limit +4 -0
  132. data/test/fixtures/google_garbage +456 -0
  133. data/test/fixtures/google_madison_square_garden +57 -0
  134. data/test/fixtures/google_no_city_data +44 -0
  135. data/test/fixtures/google_no_locality +51 -0
  136. data/test/fixtures/google_no_results +4 -0
  137. data/test/fixtures/google_over_limit +4 -0
  138. data/test/fixtures/google_places_details_invalid_request +4 -0
  139. data/test/fixtures/google_places_details_madison_square_garden +120 -0
  140. data/test/fixtures/google_places_details_no_results +4 -0
  141. data/test/fixtures/google_places_details_no_reviews +60 -0
  142. data/test/fixtures/google_places_details_no_types +66 -0
  143. data/test/fixtures/here_madison_square_garden +72 -0
  144. data/test/fixtures/here_no_results +8 -0
  145. data/test/fixtures/mapquest_error +16 -0
  146. data/test/fixtures/mapquest_invalid_api_key +16 -0
  147. data/test/fixtures/mapquest_invalid_request +16 -0
  148. data/test/fixtures/mapquest_madison_square_garden +52 -0
  149. data/test/fixtures/mapquest_no_results +16 -0
  150. data/test/fixtures/maxmind_24_24_24_21 +1 -0
  151. data/test/fixtures/maxmind_24_24_24_22 +1 -0
  152. data/test/fixtures/maxmind_24_24_24_23 +1 -0
  153. data/test/fixtures/maxmind_24_24_24_24 +1 -0
  154. data/test/fixtures/maxmind_74_200_247_59 +1 -0
  155. data/test/fixtures/maxmind_invalid_key +1 -0
  156. data/test/fixtures/maxmind_no_results +1 -0
  157. data/test/fixtures/nominatim_madison_square_garden +150 -0
  158. data/test/fixtures/nominatim_no_results +1 -0
  159. data/test/fixtures/nominatim_over_limit +1 -0
  160. data/test/fixtures/okf_kirstinmaki +67 -0
  161. data/test/fixtures/okf_no_results +4 -0
  162. data/test/fixtures/opencagedata_invalid_api_key +25 -0
  163. data/test/fixtures/opencagedata_invalid_request +26 -0
  164. data/test/fixtures/opencagedata_madison_square_garden +73 -0
  165. data/test/fixtures/opencagedata_no_results +29 -0
  166. data/test/fixtures/opencagedata_over_limit +31 -0
  167. data/test/fixtures/ovi_madison_square_garden +72 -0
  168. data/test/fixtures/ovi_no_results +8 -0
  169. data/test/fixtures/pointpin_10_10_10_10 +1 -0
  170. data/test/fixtures/pointpin_555_555_555_555 +1 -0
  171. data/test/fixtures/pointpin_80_111_555_555 +1 -0
  172. data/test/fixtures/pointpin_no_results +1 -0
  173. data/test/fixtures/smarty_streets_11211 +1 -0
  174. data/test/fixtures/smarty_streets_madison_square_garden +47 -0
  175. data/test/fixtures/smarty_streets_no_results +1 -0
  176. data/test/fixtures/telize_10_10_10_10 +1 -0
  177. data/test/fixtures/telize_555_555_555_555 +4 -0
  178. data/test/fixtures/telize_74_200_247_59 +1 -0
  179. data/test/fixtures/telize_no_results +1 -0
  180. data/test/fixtures/yahoo_error +1 -0
  181. data/test/fixtures/yahoo_invalid_key +2 -0
  182. data/test/fixtures/yahoo_madison_square_garden +52 -0
  183. data/test/fixtures/yahoo_no_results +10 -0
  184. data/test/fixtures/yahoo_over_limit +2 -0
  185. data/test/fixtures/yandex_canada_rue_dupuis_14 +446 -0
  186. data/test/fixtures/yandex_invalid_key +1 -0
  187. data/test/fixtures/yandex_kremlin +48 -0
  188. data/test/fixtures/yandex_new_york +1 -0
  189. data/test/fixtures/yandex_no_city_and_town +112 -0
  190. data/test/fixtures/yandex_no_results +16 -0
  191. data/test/integration/http_client_test.rb +31 -0
  192. data/test/mongoid_test_helper.rb +43 -0
  193. data/test/test_helper.rb +386 -0
  194. data/test/unit/active_record_test.rb +16 -0
  195. data/test/unit/cache_test.rb +37 -0
  196. data/test/unit/calculations_test.rb +220 -0
  197. data/test/unit/configuration_test.rb +55 -0
  198. data/test/unit/error_handling_test.rb +56 -0
  199. data/test/unit/geocoder_test.rb +78 -0
  200. data/test/unit/https_test.rb +17 -0
  201. data/test/unit/ip_address_test.rb +27 -0
  202. data/test/unit/lookup_test.rb +153 -0
  203. data/test/unit/lookups/bing_test.rb +68 -0
  204. data/test/unit/lookups/dstk_test.rb +26 -0
  205. data/test/unit/lookups/esri_test.rb +48 -0
  206. data/test/unit/lookups/freegeoip_test.rb +27 -0
  207. data/test/unit/lookups/geocoder_ca_test.rb +17 -0
  208. data/test/unit/lookups/geocodio_test.rb +55 -0
  209. data/test/unit/lookups/google_places_details_test.rb +122 -0
  210. data/test/unit/lookups/google_premier_test.rb +22 -0
  211. data/test/unit/lookups/google_test.rb +84 -0
  212. data/test/unit/lookups/mapquest_test.rb +60 -0
  213. data/test/unit/lookups/maxmind_local_test.rb +28 -0
  214. data/test/unit/lookups/maxmind_test.rb +63 -0
  215. data/test/unit/lookups/nominatim_test.rb +31 -0
  216. data/test/unit/lookups/okf_test.rb +38 -0
  217. data/test/unit/lookups/opencagedata_test.rb +64 -0
  218. data/test/unit/lookups/pointpin_test.rb +30 -0
  219. data/test/unit/lookups/smarty_streets_test.rb +71 -0
  220. data/test/unit/lookups/telize_test.rb +36 -0
  221. data/test/unit/lookups/yahoo_test.rb +35 -0
  222. data/test/unit/method_aliases_test.rb +26 -0
  223. data/test/unit/model_test.rb +38 -0
  224. data/test/unit/mongoid_test.rb +47 -0
  225. data/test/unit/near_test.rb +87 -0
  226. data/test/unit/oauth_util_test.rb +31 -0
  227. data/test/unit/proxy_test.rb +37 -0
  228. data/test/unit/query_test.rb +52 -0
  229. data/test/unit/rake_task_test.rb +21 -0
  230. data/test/unit/request_test.rb +35 -0
  231. data/test/unit/result_test.rb +72 -0
  232. data/test/unit/test_mode_test.rb +70 -0
  233. metadata +281 -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/unit/**/*_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 = "Geocoder #{Geocoder::VERSION}"
23
+ rdoc.rdoc_files.include('*.rdoc')
24
+ rdoc.rdoc_files.include('lib/**/*.rb')
25
+ end
data/bin/geocode ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'geocoder/cli'
4
+
5
+ Geocoder::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 = '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(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
+ Geocoder.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
+ Geocoder.configure(:cache => AutoexpireCacheRedis.new(Redis.new))
@@ -0,0 +1,48 @@
1
+ # This class allows you to configure how Geocoder should treat errors that occur when
2
+ # the cache is not available.
3
+ # Configure it like this
4
+ # config/initializers/geocoder.rb
5
+ # Geocoder.configure(
6
+ # :cache => Geocoder::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 Geocoder::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,16 @@
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
+ gem 'geoip'
10
+
11
+ gem 'rails'
12
+
13
+ platforms :jruby do
14
+ gem 'jruby-openssl'
15
+ end
16
+ end
@@ -0,0 +1,14 @@
1
+ require 'rails/generators'
2
+
3
+ module Geocoder
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 Geocoder."
9
+ def add_initializer
10
+ template "initializer.rb", "config/initializers/geocoder.rb"
11
+ end
12
+ end
13
+ end
14
+
@@ -0,0 +1,21 @@
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
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,28 @@
1
+ require 'rails/generators/migration'
2
+
3
+ module Geocoder
4
+ module Generators
5
+ module Maxmind
6
+ class GeoliteCityGenerator < Rails::Generators::Base
7
+ include Rails::Generators::Migration
8
+
9
+ source_root File.expand_path('../templates', __FILE__)
10
+
11
+ def copy_migration_files
12
+ migration_template "migration/geolite_city.rb", "db/migrate/geocoder_maxmind_geolite_city.rb"
13
+ end
14
+
15
+ # Define the next_migration_number method (necessary for the
16
+ # migration_template method to work)
17
+ def self.next_migration_number(dirname)
18
+ if ActiveRecord::Base.timestamped_migrations
19
+ sleep 1 # make sure each time we get a different timestamp
20
+ Time.new.utc.strftime("%Y%m%d%H%M%S")
21
+ else
22
+ "%.3d" % (current_migration_number(dirname) + 1)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ require 'rails/generators/migration'
2
+
3
+ module Geocoder
4
+ module Generators
5
+ module Maxmind
6
+ class GeoliteCountryGenerator < Rails::Generators::Base
7
+ include Rails::Generators::Migration
8
+
9
+ source_root File.expand_path('../templates', __FILE__)
10
+
11
+ def copy_migration_files
12
+ migration_template "migration/geolite_country.rb", "db/migrate/geocoder_maxmind_geolite_country.rb"
13
+ end
14
+
15
+ # Define the next_migration_number method (necessary for the
16
+ # migration_template method to work)
17
+ def self.next_migration_number(dirname)
18
+ if ActiveRecord::Base.timestamped_migrations
19
+ sleep 1 # make sure each time we get a different timestamp
20
+ Time.new.utc.strftime("%Y%m%d%H%M%S")
21
+ else
22
+ "%.3d" % (current_migration_number(dirname) + 1)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,30 @@
1
+ class GeocoderMaxmindGeoliteCity < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :maxmind_geolite_city_blocks, id: false do |t|
4
+ t.column :start_ip_num, :bigint, null: false
5
+ t.column :end_ip_num, :bigint, null: false
6
+ t.column :loc_id, :bigint, null: false
7
+ end
8
+ add_index :maxmind_geolite_city_blocks, :loc_id
9
+ add_index :maxmind_geolite_city_blocks, :start_ip_num, unique: true
10
+ add_index :maxmind_geolite_city_blocks, [:end_ip_num, :start_ip_num], unique: true, name: 'index_maxmind_geolite_city_blocks_on_end_ip_num_range'
11
+
12
+ create_table :maxmind_geolite_city_location, id: false do |t|
13
+ t.column :loc_id, :bigint, null: false
14
+ t.string :country, null: false
15
+ t.string :region, null: false
16
+ t.string :city
17
+ t.string :postal_code, null: false
18
+ t.float :latitude
19
+ t.float :longitude
20
+ t.integer :metro_code
21
+ t.integer :area_code
22
+ end
23
+ add_index :maxmind_geolite_city_location, :loc_id, unique: true
24
+ end
25
+
26
+ def self.down
27
+ drop_table :maxmind_geolite_city_location
28
+ drop_table :maxmind_geolite_city_blocks
29
+ end
30
+ end
@@ -0,0 +1,17 @@
1
+ class GeocoderMaxmindGeoliteCountry < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :maxmind_geolite_country, id: false do |t|
4
+ t.column :start_ip, :string
5
+ t.column :end_ip, :string
6
+ t.column :start_ip_num, :bigint, null: false
7
+ t.column :end_ip_num, :bigint, null: false
8
+ t.column :country_code, :string, null: false
9
+ t.column :country, :string, null: false
10
+ end
11
+ add_index :maxmind_geolite_country, :start_ip_num, unique: true
12
+ end
13
+
14
+ def self.down
15
+ drop_table :maxmind_geolite_country
16
+ end
17
+ end
@@ -0,0 +1,90 @@
1
+ module Geocoder
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
+ def prefix; @prefix; end
53
+ def store; @store; end
54
+
55
+ ##
56
+ # Cache key for a given URL.
57
+ #
58
+ def key_for(url)
59
+ [prefix, url].join
60
+ end
61
+
62
+ ##
63
+ # Array of keys with the currently configured prefix
64
+ # that have non-nil values.
65
+ #
66
+ def keys
67
+ store.keys.select{ |k| k.match(/^#{prefix}/) and interpret(store[k]) }
68
+ end
69
+
70
+ ##
71
+ # Array of cached URLs.
72
+ #
73
+ def urls
74
+ keys.map{ |k| k[/^#{prefix}(.*)/, 1] }
75
+ end
76
+
77
+ ##
78
+ # Clean up value before returning. Namely, convert empty string to nil.
79
+ # (Some key/value stores return empty string instead of nil.)
80
+ #
81
+ def interpret(value)
82
+ value == "" ? nil : value
83
+ end
84
+
85
+ def expire_single_url(url)
86
+ key = key_for(url)
87
+ store.respond_to?(:del) ? store.del(key) : store.delete(key)
88
+ end
89
+ end
90
+ end