rack-geoipcity 0.0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 20d4c9f7f5da4c49e72e3109eb566a57fa37f53c
4
+ data.tar.gz: 9090d1130020227c502a0b9b7fb2abd6fadee717
5
+ SHA512:
6
+ metadata.gz: 1fba05767d17158dc0f3a2f8d662f6f813737397b08d315f3e55558a726389f51aa35a81152e41f36cf266cff76811fa24be03442d8b53ca0d813ad54bb51b95
7
+ data.tar.gz: a4697c3cfa17d1a78ab7bbfbd776ff051c9e5092e21ccc9aff7775ec1f5f3d6d591669f7073a711712321690f24bb0740c6c74428c2899d0b23d56d9fb1cbed0
data/.gitignore CHANGED
@@ -1,3 +1,51 @@
1
+ # See http://help.github.com/ignore-files/ for more about ignoring files.
2
+ #
3
+ # If you find yourself ignoring temporary files generated by your text editor
4
+ # or operating system, you probably want to add a global ignore instead:
5
+ # git config --global core.excludesfile ~/.gitignore_global
6
+ .bundle
7
+ db/*.sqlite3*
8
+ log/*.log
9
+ *.log
10
+ /tmp/
11
+ doc/
12
+ docs/
13
+ .yardoc/
14
+ *.swp
15
+ *~
16
+ .project
1
17
  .DS_Store
18
+ .rvm
19
+ .DS_Store
20
+ *.log
21
+ *.swp
22
+ *.db
23
+ *.old
24
+ photos/
25
+ attachments/
26
+ .sass-cache/
27
+ run/
28
+ spec/fixtures/images/
29
+ spec/html/
2
30
  .rvmrc
3
- *.tmproj
31
+ .bundle
32
+ db/*.sqlite3*
33
+ log/*.log
34
+ *.log
35
+ /tmp/
36
+ doc/
37
+ *.swp
38
+ *~
39
+ .project
40
+ .rspec
41
+ vendor/ruby/
42
+ vendor.noindex
43
+ bin/
44
+ db/backups/
45
+ *.rdb
46
+ config/redis.conf
47
+ Gemfile.lock
48
+ *.gem
49
+ examples/assets/GeoLiteCity.dat
50
+ spec/support/assets/GeoLiteCity.dat
51
+ coverage/
@@ -0,0 +1,11 @@
1
+ # CH CH CH CHANGES! #
2
+
3
+ ## v1.0.0 ##
4
+
5
+ * Updated RSpec and specs ever so slightly because of it.
6
+ * Updated for semver, it's been same API for aaaages.
7
+ * Little bit of love for the formatting.
8
+
9
+ ## v0.0.1 ##
10
+
11
+ * Initial release.
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ # A sample Gemfile
2
+ source "https://rubygems.org"
3
+
4
+ gemspec
5
+
6
+ group :development do
7
+ gem "haml"
8
+ gem "simplecov"
9
+ gem "yard"
10
+ gem "rake"
11
+ gem "redcarpet"
12
+ gem "rspec"
13
+ gem "rspec-its"
14
+ gem "rack-test"
15
+ gem "rack-fake_ip", :git => "git://github.com/yb66/rack-fake_ip.git", :branch => "master"
16
+ end
@@ -0,0 +1,56 @@
1
+ ## Rack GeoIPCity ##
2
+
3
+ Rack::GeoIPCity uses the geoip gem and the GeoIP database to lookup the geographical info of a request by its IP address.
4
+ The database can be downloaded from:
5
+
6
+ (Lite version) http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz, instructions are here http://www.maxmind.com/app/geolitecity.
7
+
8
+ http://www.maxmind.com/app/city for full version.
9
+
10
+ *NOTE!* If you're using the country database you'll get a different struct returned, so use the GeoIPCountry gem, rack-geoipcountry. I'd make this middleware do both but:
11
+
12
+ a) It would be slower
13
+ b) there's already a country gem and
14
+ c) you can do the branching yourself if you really want, but why would you?
15
+
16
+ ## Usage: ##
17
+
18
+ `use Rack::GeoIPCity, :db => "path/to/GeoIP.dat"`
19
+
20
+ By default all requests are looked up and the X_GEOIP_* headers are added to the request. The headers can then be read in the application.
21
+ The country name is added to the request header as `X_GEOIP_COUNTRY`, eg:
22
+ `X_GEOIP_COUNTRY: United Kingdom`
23
+
24
+ The full set of GEOIP request headers is below:
25
+ `X_GEOIP_COUNTRY_CODE` - The ISO3166-1 two-character country code, if not found set to --
26
+ `X_GEOIP_COUNTRY_CODE3` - The ISO3166-2 three-character country code, if not found set to --
27
+ `X_GEOIP_COUNTRY` - The ISO3166 English-language name of the country, if not found set to an empty string
28
+ `X_GEOIP_CONTINENT` if not found set to an empty string
29
+ `X_GEOIP_REGION_NAME` if not found set to an empty string
30
+ `X_GEOIP_CITY_NAME` if not found set to an empty string
31
+ `X_GEOIP_POSTAL_CODE` if not found set to an empty string
32
+ `X_GEOIP_LATITUDE` if not found won't be returned as I can't think of another good return value to signify "not found"
33
+ `X_GEOIP_LONGITUDE` if not found won't be returned as I can't think of another good return value to signify "not found"
34
+ `X_GEOIP_DMA_CODE` The metropolitan code (this is for the USA, see http://code.google.com/apis/adwords/docs/appendix/metrocodes.html if you're interested), default 0 for not found.
35
+ `X_GEOIP_AREA_CODE` if not found set to an empty string
36
+ `X_GEOIP_TIMEZONE` if not found set to an empty string
37
+ `X_GEOIP_CONTINENT` - The two-character continent code, if not found set to an empty string
38
+
39
+
40
+ You can use the included Mapping class to trigger lookup only for certain requests by specifying matching path prefix in options, eg:
41
+ `use Rack::GeoIPCity::Mapping, :prefix => '/video_tracking'`
42
+ The above will lookup IP addresses only for requests matching `/video_tracking` etc.
43
+
44
+ ## Running the specs and examples ##
45
+
46
+ You'll need a copy of the database, so download it and place it in:
47
+
48
+ * examples/assets for the examples
49
+ * spec/support/assets for the specs
50
+
51
+ ## License ##
52
+
53
+ MIT License -
54
+ Originally by Karol Hosiawa ( http://twitter.com/hosiawak )
55
+ Converted to a gem by Thomas Maurer
56
+ This one by Iain Barnett
@@ -0,0 +1,6 @@
1
+ require 'yard'
2
+
3
+ YARD::Rake::YardocTask.new do |t|
4
+ t.files = ['lib/**/*.rb', 'app/*.rb', 'models/*.rb', 'spec/**/*.rb']
5
+ t.options = ['-odocs/'] # optional
6
+ end
@@ -0,0 +1,41 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ Bundler.require
6
+
7
+
8
+ require 'rack/geoipcity'
9
+ require 'haml'
10
+
11
+ root = File.expand_path File.dirname(__FILE__)
12
+
13
+ # everything was moved into a separate module/file to make it easier to set up tests
14
+
15
+ class FakeIps
16
+ def ip
17
+ @ips.sample
18
+ end
19
+
20
+ def initialize
21
+ @ips = ["87.237.57.28","89.145.84.82","119.63.193.39","122.152.129.9","193.26.222.136","202.165.96.142","209.198.242.61","212.209.54.40","216.239.41.97"]
22
+ end
23
+ end
24
+
25
+ use Rack::GeoIPCity, :db => File.join(root, "assets/GeoLiteCity.dat"), :ips => FakeIps.new#, :path => %r{^/geo}#, :prefix => "/ge"
26
+
27
+
28
+ layout = ->(){
29
+ File.read File.join(root, "views/layout.haml" )
30
+ }
31
+
32
+ app = lambda do |env|
33
+ request = Rack::Request.new(env)
34
+ stuff_here = env.select{|x| x =~ /^GEOIP/ }.inspect
35
+ output = Haml::Engine.new(layout.call).render( Object.new,
36
+ :stuff_here => stuff_here
37
+ )
38
+ Rack::Response.new( output ).finish
39
+ end
40
+
41
+ run app
@@ -0,0 +1,15 @@
1
+ - stuff_here ||= "Nothing doing"
2
+ !!!
3
+ %head
4
+ %title
5
+ Rack GeoIPCity Example
6
+ %body
7
+ %h1
8
+ Rack GeoIPCity Example
9
+
10
+ %p= Time.now
11
+
12
+ %h2
13
+ Geo Info
14
+ %p
15
+ = stuff_here
@@ -2,68 +2,51 @@ require 'geoip'
2
2
 
3
3
  module Rack
4
4
 
5
- # See the README for more docs
5
+ # See the README for more docs.
6
+ # @example
7
+ # use Rack::GeoIPCity, :db => "/PATH/TO/GeoLiteCity.dat")
6
8
  class GeoIPCity
9
+
10
+ # Setting the db. Let the app do this, hands away!
11
+ # @param [GeoIP] :db
12
+ def self.db=( db )
13
+ @db = db
14
+ end
15
+
16
+
17
+ # Use this from another app, say a Sinatra or Rack app, when you want to use the GeoIP database for something other than the referrer's IP.
18
+ # @return [GeoIP]
19
+ def self.db
20
+ @db
21
+ end
22
+
23
+ # @param [Hash] options
24
+ # @option options [String] :db Path to the GeoIP database
25
+ # @option options [#ip] :ips ('Rack::Request.new(env)') An object that responds to `ip` and gives an IP address. You'll probably want to use this when you're developing/testing locally and want to pass in fake addresses to get the GeoIP to fire something other than blanks.
26
+ # @option options [Regexp] :path
27
+ # @option options [String] :prefix
7
28
  def initialize(app, options = {})
8
29
  options[:db] ||= 'GeoIP.dat'
9
- @db = GeoIP.new(options[:db])
10
- @app = app
30
+ @ips = options[:ips]
31
+ @path = ->(env){ env['PATH_INFO'].start_with? options[:prefix] } unless options[:prefix].nil?
32
+ @path = ->(env){ env['PATH_INFO'] =~ options[:path] } unless options[:path].nil?
33
+ @path ||= ->(_){ true }
34
+ self.class.db = GeoIP.new(options[:db])
35
+ @app = app
11
36
  end
12
-
13
- DEFAULTS = {
14
- 'X_GEOIP_COUNTRY_CODE' => 0,
15
- 'X_GEOIP_COUNTRY_CODE3' => 0,
16
- 'X_GEOIP_COUNTRY' => '',
17
- 'X_GEOIP_CONTINENT' => '',
18
- 'X_GEOIP_REGION_NAME' => '',
19
- 'X_GEOIP_CITY_NAME' => '',
20
- 'X_GEOIP_POSTAL_CODE' => '' ,
21
- 'X_GEOIP_LATITUDE' => nil,
22
- 'X_GEOIP_LONGITUDE' => nil,
23
- 'X_GEOIP_DMA_CODE' => 0,
24
- 'X_GEOIP_AREA_CODE' => 0,
25
- 'X_GEOIP_TIMEZONE' => '',
26
- }
27
-
37
+
28
38
  def call(env)
29
- res = @db.city(env['REMOTE_ADDR'])
30
-
31
- unless res.nil? # won't bork on local or bad ip's
32
- hash = {}
33
- hash['X_GEOIP_COUNTRY_CODE'] = res.country_code2 unless res.country_code2.nil?
34
- hash['X_GEOIP_COUNTRY_CODE3'] = res.country_code3 unless res.country_code3.nil?
35
- hash['X_GEOIP_COUNTRY'] = res.country_name unless res.country_name.nil?
36
- hash['X_GEOIP_CONTINENT'] = res.continent_code unless res.continent_code.nil?
37
- hash['X_GEOIP_REGION_NAME'] = res.region_name unless res.region_name.nil?
38
- hash['X_GEOIP_CITY_NAME'] = res.city_name unless res.city_name.nil?
39
- hash['X_GEOIP_POSTAL_CODE'] = res.postal_code unless res.postal_code.nil?
40
- hash['X_GEOIP_LATITUDE'] = res.latitude
41
- hash['X_GEOIP_LONGITUDE'] = res.longitude
42
- hash['X_GEOIP_DMA_CODE'] = res.dma_code unless res.dma_code.nil?
43
- hash['X_GEOIP_AREA_CODE'] = res.area_code unless res.area_code.nil?
44
- hash['X_GEOIP_TIMEZONE'] = res.timezone unless res.timezone.nil?
45
-
46
- hash.delete_if{|k,v| v.nil? } # remove latitude and longitude and any other stragglers
47
- env.merge!( DEFAULTS.merge hash )
48
- end
49
-
39
+ if @path.call(env)
40
+ ips = @ips || Rack::Request.new(env)
41
+ res = self.class.db.city ips.ip
42
+ unless res.nil? # won't bork on local or bad ip's
43
+ h = Hash[ [:country_code2, :country_code3, :country_name, :continent_code, :region_name, :city_name, :postal_code, :latitude, :longitude, :dma_code, :area_code, :timezone, :ip,].map{|x| [ "GEOIP_#{x.upcase}", res.__send__(x) ] } ].delete_if{|k,v| v.nil? }
44
+
45
+ env.merge! h
46
+ end
47
+ end
50
48
  @app.call(env)
51
49
  end
52
-
53
- class Mapping
54
- def initialize(app, options = {})
55
- @app, @prefix = app, /^#{options.delete(:prefix)}/
56
- @geoip = GeoIPCity.new(app, options)
57
- end
58
-
59
- def call(env)
60
- if env['PATH_INFO'] =~ @prefix
61
- @geoip.call(env)
62
- else
63
- @app.call(env)
64
- end
65
- end
66
- end
67
50
 
68
- end
69
- end
51
+ end # GeoIPCity
52
+ end # Rack
@@ -1,5 +1,5 @@
1
1
  module Rack
2
- module GeoIPCity
3
- VERSION = "0.0.1"
2
+ class GeoIPCity
3
+ VERSION = "1.0.0"
4
4
  end
5
5
  end
@@ -13,6 +13,7 @@ Gem::Specification.new do |s|
13
13
  s.description = %q{Rack::GeoIPCity uses the geoip gem and the GeoIP database to lookup the city of a request by its IP address}
14
14
  s.license = 'MIT'
15
15
 
16
+ s.add_dependency 'rack'
16
17
  s.add_dependency 'geoip'
17
18
 
18
19
  s.files = `git ls-files`.split("\n")
@@ -0,0 +1,41 @@
1
+
2
+ require 'spec_helper'
3
+
4
+
5
+ describe "A normal route" do
6
+ # set a known static ip to help with testing
7
+ # i.e. Wikipedia
8
+ include_context "All routes", :ip => "208.80.152.201"
9
+ before do
10
+ get "/"
11
+ end
12
+ it_should_behave_like "Any route"
13
+ subject { Marshal.load last_response.body }
14
+ it { should respond_to :keys }
15
+ its(:keys) { should include "country_code2", "country_code3", "country_name", "continent_code", "region_name", "city_name", "postal_code", "latitude", "longitude", "timezone", "ip" }
16
+ its(:values) { should include "US", "USA", "United States", "NA", "CA", "San Francisco", "94105", 37.789800000000014, -122.3942, 807, 415, "America/Los_Angeles", "208.80.152.201" }
17
+ end
18
+
19
+ describe "Using the database from inside another app." do
20
+ include_context "All routes"
21
+ subject { Rack::GeoIPCity.db }
22
+ it { should_not be_nil }
23
+ it { should respond_to :city }
24
+
25
+ context "Given an IP" do
26
+ subject { Rack::GeoIPCity.db.city "208.80.152.201" }
27
+ it { should respond_to :members }
28
+ its(:members) { should include :country_code2, :country_code3, :country_name, :continent_code, :region_name, :city_name, :postal_code, :latitude, :longitude, :timezone, :ip }
29
+ its(:values) { should include "US", "USA", "United States", "NA", "CA", "San Francisco", "94105", 37.789800000000014, -122.3942, 807, 415, "America/Los_Angeles", "208.80.152.201" }
30
+ end
31
+
32
+ context "Called from inside a route" do
33
+ before do
34
+ get "/ip/208.80.152.201"
35
+ end
36
+ subject { Marshal.load last_response.body }
37
+ it { should respond_to :keys }
38
+ its(:keys) { should include :country_code2, :country_code3, :country_name, :continent_code, :region_name, :city_name, :postal_code, :latitude, :longitude, :timezone, :ip }
39
+ its(:values) { should include "US", "USA", "United States", "NA", "CA", "San Francisco", "94105", 37.789800000000014, -122.3942, 807, 415, "America/Los_Angeles", "208.80.152.201" }
40
+ end
41
+ end
@@ -0,0 +1,45 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'rspec'
4
+ require 'rspec/its'
5
+ Spec_dir = File.expand_path( File.dirname __FILE__ )
6
+
7
+ unless Kernel.respond_to?(:require_relative)
8
+ module Kernel
9
+ def require_relative(path)
10
+ require File.join(File.dirname(caller[0]), path.to_str)
11
+ end
12
+ end
13
+ end
14
+
15
+ # code coverage
16
+ require 'simplecov'
17
+ SimpleCov.start do
18
+ add_filter "/bin/"
19
+ add_filter "/vendor/"
20
+ add_filter "/vendor.noindex/"
21
+ end
22
+
23
+ require "rack/test"
24
+ ENV['RACK_ENV'] ||= 'test'
25
+ ENV["EXPECT_WITH"] ||= "racktest"
26
+
27
+
28
+ require "logger"
29
+ logger = Logger.new STDOUT
30
+ logger.level = Logger::DEBUG
31
+ logger.datetime_format = '%a %d-%m-%Y %H%M '
32
+ LOgger = logger
33
+
34
+
35
+
36
+ Dir[ File.join( Spec_dir, "/support/**/*.rb")].each do |f|
37
+ logger.info "requiring #{f}"
38
+ require f
39
+ end
40
+
41
+
42
+ RSpec.configure do |config|
43
+ config.treat_symbols_as_metadata_keys_with_true_values = true
44
+ end
45
+
@@ -0,0 +1,58 @@
1
+ require 'rack/fake_ip'
2
+ require_relative "../../../lib/rack/geoipcity.rb"
3
+
4
+
5
+ module App
6
+
7
+ def self.app( options={} )
8
+ Rack::Builder.app do
9
+ ip_opts = {}
10
+ ip_opts.merge!( {ip: options[:ip] } ) if options[:ip]
11
+ use Rack::FakeIP, ip_opts
12
+ use Rack::GeoIPCity, :db => File.expand_path( File.join(File.dirname(__FILE__), "../assets/GeoLiteCity.dat"))
13
+
14
+ geo_info = ->(env) {
15
+ h = {}
16
+ env.select{|x| x =~ /^GEOIP/ }.each {|k,v|
17
+ h[k.split("GEOIP_").last.downcase] = v
18
+ }
19
+ h
20
+ }
21
+
22
+ routes = lambda { |e|
23
+ request = Rack::Request.new(e)
24
+ response = if request.path == "/"
25
+ Rack::Response.new(
26
+ [Marshal.dump(geo_info.(e))],
27
+ 200,
28
+ {"Content-Type" => "text/html"}
29
+ ).finish
30
+ elsif request.path.start_with? "/ip/"
31
+ ip_in_route = request.path.split("/ip/").last
32
+ ip = Rack::GeoIPCity.db.city ip_in_route
33
+ Rack::Response.new(
34
+ [Marshal.dump(Hash[ip.each_pair.to_a])],
35
+ 200,
36
+ {"Content-Type" => "text/html"}
37
+ ).finish
38
+ end
39
+ response
40
+ }
41
+ run routes
42
+ end
43
+ end
44
+ end
45
+
46
+
47
+ shared_context "All routes" do |options={}|
48
+ include Rack::Test::Methods
49
+ warn "options = #{options.inspect}"
50
+ let(:app){ App.app( options ) }
51
+ end
52
+
53
+ shared_examples_for "Any route" do
54
+ subject {
55
+ last_response
56
+ }
57
+ it { should be_ok }
58
+ end
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-geoipcity
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
5
- prerelease:
4
+ version: 1.0.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Karol Hosiawa
@@ -11,19 +10,36 @@ authors:
11
10
  autorequire:
12
11
  bindir: bin
13
12
  cert_chain: []
14
- date: 2011-06-24 00:00:00.000000000Z
13
+ date: 2016-04-10 00:00:00.000000000 Z
15
14
  dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rack
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - '>='
27
+ - !ruby/object:Gem::Version
28
+ version: '0'
16
29
  - !ruby/object:Gem::Dependency
17
30
  name: geoip
18
- requirement: &2152901760 !ruby/object:Gem::Requirement
19
- none: false
31
+ requirement: !ruby/object:Gem::Requirement
20
32
  requirements:
21
- - - ! '>='
33
+ - - '>='
22
34
  - !ruby/object:Gem::Version
23
35
  version: '0'
24
36
  type: :runtime
25
37
  prerelease: false
26
- version_requirements: *2152901760
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - '>='
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
27
43
  description: Rack::GeoIPCity uses the geoip gem and the GeoIP database to lookup the
28
44
  city of a request by its IP address
29
45
  email:
@@ -33,33 +49,43 @@ extensions: []
33
49
  extra_rdoc_files: []
34
50
  files:
35
51
  - .gitignore
36
- - README
52
+ - CHANGES.md
53
+ - Gemfile
54
+ - README.md
55
+ - Rakefile
56
+ - examples/config.ru
57
+ - examples/views/layout.haml
37
58
  - lib/rack/geoipcity.rb
38
59
  - lib/rack/geoipcity/version.rb
39
60
  - rack-geoipcity.gemspec
61
+ - spec/geoipcity_spec.rb
62
+ - spec/spec_helper.rb
63
+ - spec/support/shared/app.rb
40
64
  homepage: https://github.com/yb66/Rack-GeoIPCity
41
65
  licenses:
42
66
  - MIT
67
+ metadata: {}
43
68
  post_install_message:
44
69
  rdoc_options: []
45
70
  require_paths:
46
71
  - lib
47
72
  required_ruby_version: !ruby/object:Gem::Requirement
48
- none: false
49
73
  requirements:
50
- - - ! '>='
74
+ - - '>='
51
75
  - !ruby/object:Gem::Version
52
76
  version: '0'
53
77
  required_rubygems_version: !ruby/object:Gem::Requirement
54
- none: false
55
78
  requirements:
56
- - - ! '>='
79
+ - - '>='
57
80
  - !ruby/object:Gem::Version
58
81
  version: '0'
59
82
  requirements: []
60
83
  rubyforge_project:
61
- rubygems_version: 1.8.5
84
+ rubygems_version: 2.0.14
62
85
  signing_key:
63
- specification_version: 3
86
+ specification_version: 4
64
87
  summary: Rack middleware for Geo IP city lookup
65
- test_files: []
88
+ test_files:
89
+ - spec/geoipcity_spec.rb
90
+ - spec/spec_helper.rb
91
+ - spec/support/shared/app.rb
data/README DELETED
@@ -1,47 +0,0 @@
1
- Rack::GeoIPCity uses the geoip gem and the GeoIP database to lookup the geographical info of a request by its IP address
2
- The database can be downloaded from:
3
-
4
- (Lite version) http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz, instructions are here http://www.maxmind.com/app/geolitecity.
5
-
6
- http://www.maxmind.com/app/city for full version.
7
-
8
- *NOTE!* If you're using the country database you'll get a different struct returned, so use the GeoIPCountry gem, rack-geoipcountry. I'd make this middleware do both but:
9
-
10
- a) It would be slower
11
- b) there's already a country gem and
12
- c) you can do the branching yourself if you really want, but why would you?
13
-
14
- Usage:
15
- ======
16
-
17
- use Rack::GeoIPCity, :db => "path/to/GeoIP.dat"
18
-
19
- By default all requests are looked up and the X\_GEOIP\_* headers are added to the request
20
- The headers can then be read in the application
21
- The country name is added to the request header as X\_GEOIP\_COUNTRY, eg:
22
- X\_GEOIP\_COUNTRY: United Kingdom
23
-
24
- The full set of GEOIP request headers is below:
25
- X\_GEOIP\_COUNTRY\_CODE - The ISO3166-1 two-character country code, if not found set to --
26
- X\_GEOIP\_COUNTRY\_CODE3 - The ISO3166-2 three-character country code, if not found set to --
27
- X\_GEOIP\_COUNTRY - The ISO3166 English-language name of the country, if not found set to an empty string
28
- X\_GEOIP\_CONTINENT if not found set to an empty string
29
- X\_GEOIP\_REGION\_NAME if not found set to an empty string
30
- X\_GEOIP\_CITY\_NAME if not found set to an empty string
31
- X\_GEOIP\_POSTAL\_CODE if not found set to an empty string
32
- X\_GEOIP\_LATITUDE if not found won't be returned as I can't think of another good return value to signify "not found"
33
- X\_GEOIP\_LONGITUDE if not found won't be returned as I can't think of another good return value to signify "not found"
34
- 'X\_GEOIP\_DMA\_CODE' The metropolitan code (this is for the USA, see http://code.google.com/apis/adwords/docs/appendix/metrocodes.html if you're interested), default 0 for not found.
35
- X\_GEOIP\_AREA\_CODE if not found set to an empty string
36
- X\_GEOIP\_TIMEZONE if not found set to an empty string
37
- X\_GEOIP\_CONTINENT - The two-character continent code, if not found set to an empty string
38
-
39
-
40
- You can use the included Mapping class to trigger lookup only for certain requests by specifying matching path prefix in options, eg:
41
- use Rack::GeoIPCity::Mapping, :prefix => '/video\_tracking'
42
- The above will lookup IP addresses only for requests matching /video\_tracking etc.
43
-
44
- MIT License -
45
- Originally by Karol Hosiawa ( http://twitter.com/hosiawak )
46
- Converted to a gem by Thomas Maurer
47
- This one by Iain Barnett