maxminddb 0.1.3

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 386ad7ac9e84aaf85698ce963e747afc0d45155f
4
+ data.tar.gz: becf26c375061d81703423c7019aa89591535fb8
5
+ SHA512:
6
+ metadata.gz: fc73fc187a98edcbbe6d7536bb3546372543a40e19728141cf200bccc15f752f8cdb596bdd89d707259dd894ec5d005faea5ba562e128435bd8bf34eb70c8036
7
+ data.tar.gz: 952270232f86b80cff33a226a27cfd100795d21f1f99bd060024b85d915b1b1ff5d7ceba9a83a3da9f233c949fb67bc6a130c9a5d097868cfa7660610fe0d378
@@ -0,0 +1,2 @@
1
+ spec/cache/*.mmdb
2
+ *.swp
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color --format documentation
@@ -0,0 +1,14 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.0
6
+ - 2.1.1
7
+ - jruby-19mode
8
+ - rbx-2
9
+
10
+ script: bundle exec rake
11
+
12
+ cache:
13
+ directories:
14
+ - spec/cache
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in maxminddb.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 yhirose
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,59 @@
1
+ # maxminddb
2
+
3
+ Pure Ruby [MaxMind DB](http://maxmind.github.io/MaxMind-DB/) binary file reader.
4
+
5
+ [![Gem Version](https://badge.fury.io/rb/maxminddb.svg)](http://badge.fury.io/rb/maxminddb)
6
+ [![Build Status](https://travis-ci.org/yhirose/maxminddb.svg?branch=master)](https://travis-ci.org/yhirose/maxminddb)
7
+ [![Code Climate](https://codeclimate.com/github/yhirose/maxminddb.png)](https://codeclimate.com/github/yhirose/maxminddb)
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'maxminddb'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ ```sh
20
+ $ bundle
21
+ ```
22
+
23
+ Or install it yourself as:
24
+
25
+ ```sh
26
+ $ gem install maxminddb
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ ```ruby
32
+ db = MaxMindDB.new('./GeoLite2-City.mmdb')
33
+ ret = db.lookup('74.125.225.224')
34
+
35
+ ret.found? # => true
36
+ ret.country.name # => 'United States'
37
+ ret.country.name('zh-CN') # => '美国'
38
+ ret.country.iso_code # => 'US'
39
+ ret.city.name(:fr) # => 'Mountain View'
40
+ ret.location.latitude # => -122.0574
41
+ ```
42
+
43
+ Even if no result could be found, you can ask for the attributes without guarding for nil:
44
+
45
+ ```ruby
46
+ db = MaxMindDB.new('./GeoLite2-City.mmdb')
47
+ ret = db.lookup('127.0.0.1')
48
+ ret.found? # => false
49
+ ret.country.name # => nil
50
+ ```
51
+
52
+
53
+ ## Contributing
54
+
55
+ 1. Fork it ( http://github.com/yhirose/maxminddb/fork )
56
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
57
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
58
+ 4. Push to the branch (`git push origin my-new-feature`)
59
+ 5. Create new Pull Request
@@ -0,0 +1,22 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ desc "Downloads maxmind free DBs if required"
7
+ task :ensure_maxmind_files do
8
+ unless File.exist?('spec/cache/GeoLite2-City.mmdb')
9
+ sh 'curl http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.mmdb.gz -o spec/cache/GeoLite2-City.mmdb.gz'
10
+ sh 'gunzip spec/cache/GeoLite2-City.mmdb.gz'
11
+ end
12
+
13
+ unless File.exist?('spec/cache/GeoLite2-Country.mmdb')
14
+ sh 'curl http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.mmdb.gz -o spec/cache/GeoLite2-Country.mmdb.gz'
15
+ sh 'gunzip spec/cache/GeoLite2-Country.mmdb.gz'
16
+ end
17
+ end
18
+
19
+ desc "Downloads maxmind free DBs if required and runs all specs"
20
+ task ensure_maxmind_files_and_spec: [:ensure_maxmind_files, :spec]
21
+
22
+ task default: :ensure_maxmind_files_and_spec
@@ -0,0 +1,166 @@
1
+ require "maxminddb/version"
2
+ require 'maxminddb/result'
3
+ require 'ipaddr'
4
+
5
+ module MaxMindDB
6
+
7
+ def self.new(path)
8
+ Client.new(path)
9
+ end
10
+
11
+ class Client
12
+ METADATA_BEGIN_MARKER = ([0xAB, 0xCD, 0xEF].pack('C*') + 'MaxMind.com').encode('ascii-8bit', 'ascii-8bit')
13
+ DATA_SECTION_SEPARATOR_SIZE = 16
14
+ SIZE_BASE_VALUES = [0, 29, 285, 65821]
15
+ POINTER_BASE_VALUES = [0, 0, 2048, 526336]
16
+
17
+ def initialize(path)
18
+ @path = path
19
+ @data = File.binread(path)
20
+
21
+ pos = @data.rindex(METADATA_BEGIN_MARKER)
22
+ raise 'invalid file format' unless pos
23
+ pos += METADATA_BEGIN_MARKER.size
24
+
25
+ @metadata = decode(pos, 0)[1]
26
+ @metadata['node_byte_size'] = @metadata['record_size'] * 2 / 8
27
+ @metadata['search_tree_size'] = @metadata['node_count'] * @metadata['node_byte_size']
28
+ end
29
+
30
+ def inspect
31
+ "#<MaxMindDB::Client: DBPath:'#{@path}'>"
32
+ end
33
+
34
+ def lookup(ip)
35
+ node_no = 0
36
+ addr = addr_from_ip(ip)
37
+ for i in 0 ... 128
38
+ flag = (addr >> (127 - i)) & 1
39
+ next_node_no = read_record(node_no, flag)
40
+ if next_node_no == 0
41
+ raise 'invalid file format'
42
+ elsif next_node_no >= @metadata['node_count']
43
+ data_section_start = @metadata['search_tree_size'] + DATA_SECTION_SEPARATOR_SIZE;
44
+ pos = (next_node_no - @metadata['node_count']) - DATA_SECTION_SEPARATOR_SIZE
45
+ return MaxMindDB::Result.new(decode(pos, data_section_start)[1])
46
+ else
47
+ node_no = next_node_no
48
+ end
49
+ end
50
+ raise 'invalid file format'
51
+ end
52
+
53
+ private
54
+
55
+ def read_record(node_no, flag)
56
+ node_byte_size = @metadata['node_byte_size']
57
+ rec_byte_size = node_byte_size / 2
58
+ pos = node_byte_size * node_no
59
+ middle = @data[pos + rec_byte_size, 1].unpack('C')[0] if node_byte_size.odd?
60
+ if flag == 0 # left
61
+ val = read_value(pos, 0, rec_byte_size)[1]
62
+ val += ((middle & 0xf0) << 20) if middle
63
+ else # right
64
+ val = read_value(pos + node_byte_size - rec_byte_size, 0, rec_byte_size)[1]
65
+ val += ((middle & 0xf) << 24) if middle
66
+ end
67
+ val
68
+ end
69
+
70
+ def decode(pos, base_pos)
71
+ ctrl = @data[pos + base_pos].unpack('C')[0]
72
+ pos += 1
73
+
74
+ type = ctrl >> 5
75
+
76
+ if type == 1 # pointer
77
+ size = ((ctrl >> 3) & 0x3) + 1
78
+ v1 = ctrl & 0x7
79
+ pos, v2 = read_value(pos, base_pos, size)
80
+
81
+ pointer = (v1 << (8 * size)) + v2 + POINTER_BASE_VALUES[size]
82
+ val = decode(pointer, base_pos)[1]
83
+ else
84
+ if type == 0 # extended type
85
+ type = 7 + @data[pos + base_pos].unpack('C')[0]
86
+ pos += 1
87
+ end
88
+
89
+ size = ctrl & 0x1f
90
+ if size >= 29
91
+ byte_size = size - 29 + 1
92
+ pos, val = read_value(pos, base_pos, byte_size)
93
+ size = val + SIZE_BASE_VALUES[byte_size]
94
+ end
95
+
96
+ case type
97
+ when 2 # utf8
98
+ val = @data[pos + base_pos, size].encode('utf-8', 'utf-8')
99
+ pos += size
100
+ when 3 # double
101
+ val = @data[pos + base_pos, size].unpack('G')[0]
102
+ pos += size
103
+ when 4 # bytes
104
+ val = @data[pos + base_pos, size]
105
+ pos += size
106
+ when 5 # unsigned 16-bit int
107
+ pos, val = read_value(pos, base_pos, size)
108
+ when 6 # unsigned 32-bit int
109
+ pos, val = read_value(pos, base_pos, size)
110
+ when 7 # map
111
+ val = {}
112
+ size.times do
113
+ pos, k = decode(pos, base_pos)
114
+ pos, v = decode(pos, base_pos)
115
+ val[k] = v
116
+ end
117
+ when 8 # signed 32-bit int
118
+ v1 = @data[pos + base_pos, size].unpack('N')[0]
119
+ bits = size * 8
120
+ val = (v1 & ~(1 << bits)) - (v1 & (1 << bits))
121
+ pos += size
122
+ when 9 # unsigned 64-bit int
123
+ pos, val = read_value(pos, base_pos, size)
124
+ when 10 # unsigned 128-bit int
125
+ pos, val = read_value(pos, base_pos, size)
126
+ when 11 # array
127
+ val = []
128
+ size.times do
129
+ pos, v = decode(pos, base_pos)
130
+ val.push(v)
131
+ end
132
+ when 12 # data cache container
133
+ raise 'TODO:'
134
+ when 13 # end marker
135
+ val = nil
136
+ when 14 # boolean
137
+ val = size ? true : false
138
+ when 15 # float
139
+ val = @data[pos + base_pos, size].unpack('g')[0]
140
+ pos += size
141
+ end
142
+ end
143
+
144
+ [pos, val]
145
+ end
146
+
147
+ def read_value(pos, base_pos, size)
148
+ bytes = @data[pos + base_pos, size].unpack('C*')
149
+ val = bytes.inject(0){|r, v| (r << 8) + v }
150
+ [pos + size, val]
151
+ end
152
+
153
+ def addr_from_ip(ip)
154
+ klass = ip.class
155
+ if klass == Fixnum || klass == Bignum
156
+ ip
157
+ else
158
+ addr = IPAddr.new(ip)
159
+ addr = addr.ipv4_compat if addr.ipv4?
160
+ addr.to_i
161
+ end
162
+ end
163
+ end
164
+ end
165
+
166
+ # vim: et ts=2 sw=2 ff=unix
@@ -0,0 +1,51 @@
1
+ require_relative 'result/location'
2
+ require_relative 'result/named_location'
3
+ require_relative 'result/postal'
4
+
5
+ module MaxMindDB
6
+ class Result
7
+ def initialize(raw)
8
+ @raw = raw || {}
9
+ end
10
+
11
+ def [](attr)
12
+ raw[attr]
13
+ end
14
+
15
+ def city
16
+ @_city ||= NamedLocation.new(raw['city'])
17
+ end
18
+
19
+ def continent
20
+ @_continent ||= NamedLocation.new(raw['continent'])
21
+ end
22
+
23
+ def country
24
+ @_country ||= NamedLocation.new(raw['country'])
25
+ end
26
+
27
+ def found?
28
+ !raw.empty?
29
+ end
30
+
31
+ def location
32
+ @_location ||= Location.new(raw['location'])
33
+ end
34
+
35
+ def postal
36
+ @_postal ||= Postal.new(raw['postal'])
37
+ end
38
+
39
+ def registered_country
40
+ @_registered_country ||= NamedLocation.new(raw['registered_country'])
41
+ end
42
+
43
+ def subdivisions
44
+ @_subdivisions ||= Array(raw['subdivisions']).map { |hash| NamedLocation.new(hash) }
45
+ end
46
+
47
+ private
48
+
49
+ attr_reader :raw
50
+ end
51
+ end
@@ -0,0 +1,29 @@
1
+ module MaxMindDB
2
+ class Result
3
+ class Location
4
+ def initialize(raw)
5
+ @raw = raw || {}
6
+ end
7
+
8
+ def latitude
9
+ raw['latitude']
10
+ end
11
+
12
+ def longitude
13
+ raw['longitude']
14
+ end
15
+
16
+ def metro_code
17
+ raw['metro_code']
18
+ end
19
+
20
+ def time_zone
21
+ raw['time_zone']
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :raw
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,25 @@
1
+ module MaxMindDB
2
+ class Result
3
+ class NamedLocation
4
+ def initialize(raw)
5
+ @raw = raw || {}
6
+ end
7
+
8
+ def geoname_id
9
+ raw['geoname_id']
10
+ end
11
+
12
+ def iso_code
13
+ raw['iso_code']
14
+ end
15
+
16
+ def name(locale = :en)
17
+ raw['names'] && raw['names'][locale.to_s]
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :raw
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,17 @@
1
+ module MaxMindDB
2
+ class Result
3
+ class Postal
4
+ def initialize(raw)
5
+ @raw = raw || {}
6
+ end
7
+
8
+ def code
9
+ raw['code']
10
+ end
11
+
12
+ private
13
+
14
+ attr_reader :raw
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module MaxMindDB
2
+ VERSION = "0.1.3"
3
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'maxminddb/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "maxminddb"
8
+ spec.version = MaxMindDB::VERSION
9
+ spec.authors = ["yhirose"]
10
+ spec.email = ["yuji.hirose.bug@gmail.com"]
11
+ spec.summary = %q{MaxMind DB binary file reader.}
12
+ spec.description = %q{Pure Ruby MaxMind DB binary file reader.}
13
+ spec.homepage = "https://github.com/yhirose/maxminddb"
14
+ spec.license = "MIT"
15
+
16
+ spec.post_install_message = "MIT"
17
+
18
+ spec.files = `git ls-files -z`.split("\x0")
19
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.5"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "rspec"
26
+ spec.add_development_dependency "rspec-its"
27
+ end
File without changes
@@ -0,0 +1,29 @@
1
+ require 'maxminddb'
2
+ require 'rspec/its'
3
+
4
+ describe MaxMindDB::Result::Location do
5
+ subject(:result) { described_class.new(raw_result) }
6
+
7
+ context "with a result" do
8
+ let(:raw_result) { {
9
+ "latitude"=>37.419200000000004,
10
+ "longitude"=>-122.0574,
11
+ "metro_code"=>"807",
12
+ "time_zone"=>"America/Los_Angeles"
13
+ } }
14
+
15
+ its(:latitude) { should eq(37.419200000000004) }
16
+ its(:longitude) { should eq(-122.0574) }
17
+ its(:metro_code) { should eq("807") }
18
+ its(:time_zone) { should eq("America/Los_Angeles") }
19
+ end
20
+
21
+ context "without a result" do
22
+ let(:raw_result) { nil }
23
+
24
+ its(:latitude) { should be_nil }
25
+ its(:longitude) { should be_nil }
26
+ its(:metro_code) { should be_nil }
27
+ its(:time_zone) { should be_nil }
28
+ end
29
+ end
@@ -0,0 +1,37 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'maxminddb'
3
+
4
+ describe MaxMindDB::Result::NamedLocation do
5
+ subject(:result) { described_class.new(raw_result) }
6
+
7
+ context "with a result" do
8
+ let(:raw_result) { {
9
+ "geoname_id"=>6252001,
10
+ "iso_code"=>"US",
11
+ "names"=>{"de"=>"USA", "en"=>"United States", "es"=>"Estados Unidos", "fr"=>"États-Unis", "ja"=>"アメリカ合衆国", "pt-BR"=>"Estados Unidos", "ru"=>"США", "zh-CN"=>"美国"}
12
+ } }
13
+
14
+ its(:geoname_id) { should eq(6252001) }
15
+ its(:iso_code) { should eq('US') }
16
+
17
+ describe "name" do
18
+ it 'should eq "United States"' do
19
+ expect(result.name).to eq('United States')
20
+ end
21
+
22
+ context "with locale :ja" do
23
+ it 'should eq "アメリカ合衆国"' do
24
+ expect(result.name(:ja)).to eq('アメリカ合衆国')
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ context "without a result" do
31
+ let(:raw_result) { nil }
32
+
33
+ its(:geoname_id) { should be_nil }
34
+ its(:iso_code) { should be_nil }
35
+ its(:name) { should be_nil }
36
+ end
37
+ end
@@ -0,0 +1,19 @@
1
+ require 'maxminddb'
2
+
3
+ describe MaxMindDB::Result::Postal do
4
+ subject(:result) { described_class.new(raw_result) }
5
+
6
+ context "with a result" do
7
+ let(:raw_result) { {
8
+ "code"=>"94043"
9
+ } }
10
+
11
+ its(:code) { should eq("94043") }
12
+ end
13
+
14
+ context "without a result" do
15
+ let(:raw_result) { nil }
16
+
17
+ its(:code) { should be_nil }
18
+ end
19
+ end
@@ -0,0 +1,223 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'maxminddb'
3
+
4
+ describe MaxMindDB::Result do
5
+ subject(:result) { described_class.new(raw_result) }
6
+ let(:raw_result) { {
7
+ "city"=>{
8
+ "geoname_id"=>5375480,
9
+ "names"=>{"de"=>"Mountain View", "en"=>"Mountain View", "fr"=>"Mountain View", "ru"=>"Маунтин-Вью", "zh-CN"=>"芒廷维尤"}
10
+ },
11
+ "continent"=>{
12
+ "code"=>"NA",
13
+ "geoname_id"=>6255149,
14
+ "names"=>{"de"=>"Nordamerika", "en"=>"North America", "es"=>"Norteamérica", "fr"=>"Amérique du Nord", "ja"=>"北アメリカ", "pt-BR"=>"América do Norte", "ru"=>"Северная Америка", "zh-CN"=>"北美洲"}
15
+ },
16
+ "country"=>{
17
+ "geoname_id"=>6252001,
18
+ "iso_code"=>"US",
19
+ "names"=>{"de"=>"USA", "en"=>"United States", "es"=>"Estados Unidos", "fr"=>"États-Unis", "ja"=>"アメリカ合衆国", "pt-BR"=>"Estados Unidos", "ru"=>"США", "zh-CN"=>"美国"}
20
+ },
21
+ "location"=>{
22
+ "latitude"=>37.419200000000004,
23
+ "longitude"=>-122.0574,
24
+ "metro_code"=>"807",
25
+ "time_zone"=>"America/Los_Angeles"
26
+ },
27
+ "postal"=>{
28
+ "code"=>"94043"
29
+ },
30
+ "registered_country"=>{
31
+ "geoname_id"=>6252001,
32
+ "iso_code"=>"US",
33
+ "names"=>{"de"=>"USA", "en"=>"United States", "es"=>"Estados Unidos", "fr"=>"États-Unis", "ja"=>"アメリカ合衆国", "pt-BR"=>"Estados Unidos", "ru"=>"США", "zh-CN"=>"美国"}
34
+ },
35
+ "subdivisions"=>[
36
+ {
37
+ "geoname_id"=>5332921,
38
+ "iso_code"=>"CA",
39
+ "names"=>{"de"=>"Kalifornien", "en"=>"California", "es"=>"California", "fr"=>"Californie", "ja"=>"カリフォルニア州", "pt-BR"=>"Califórnia", "ru"=>"Калифорния", "zh-CN"=>"加利福尼亚州"}
40
+ }
41
+ ]
42
+ } }
43
+
44
+ describe '#[]' do
45
+ it 'should return the given key on the raw result' do
46
+ expect(result['city']).to eq(raw_result['city'])
47
+ end
48
+ end
49
+
50
+ describe '#city' do
51
+ context 'with a result' do
52
+ it 'should be a kind of MaxMindDB::Result::NamedLocation' do
53
+ expect(result.city).to be_kind_of(MaxMindDB::Result::NamedLocation)
54
+ end
55
+
56
+ it 'should initialize the location with the city attributes' do
57
+ expect(MaxMindDB::Result::NamedLocation).to receive(:new).with(raw_result['city'])
58
+
59
+ result.city
60
+ end
61
+ end
62
+
63
+ context "without a result" do
64
+ let(:raw_result) { nil }
65
+
66
+ it 'should be a kind of MaxMindDB::Result::NamedLocation' do
67
+ expect(result.city).to be_kind_of(MaxMindDB::Result::NamedLocation)
68
+ end
69
+ end
70
+ end
71
+
72
+ describe '#continent' do
73
+ context 'with a result' do
74
+ it 'should be a kind of MaxMindDB::Result::NamedLocation' do
75
+ expect(result.continent).to be_kind_of(MaxMindDB::Result::NamedLocation)
76
+ end
77
+
78
+ it 'should initialize the location with the continent attributes' do
79
+ expect(MaxMindDB::Result::NamedLocation).to receive(:new).with(raw_result['continent'])
80
+
81
+ result.continent
82
+ end
83
+ end
84
+
85
+ context "without a result" do
86
+ let(:raw_result) { nil }
87
+
88
+ it 'should be a kind of MaxMindDB::Result::NamedLocation' do
89
+ expect(result.continent).to be_kind_of(MaxMindDB::Result::NamedLocation)
90
+ end
91
+ end
92
+ end
93
+
94
+ describe '#country' do
95
+ context 'with a result' do
96
+ it 'should be a kind of MaxMindDB::Result::NamedLocation' do
97
+ expect(result.country).to be_kind_of(MaxMindDB::Result::NamedLocation)
98
+ end
99
+
100
+ it 'should initialize the location with the country attributes' do
101
+ expect(MaxMindDB::Result::NamedLocation).to receive(:new).with(raw_result['country'])
102
+
103
+ result.country
104
+ end
105
+ end
106
+
107
+ context "without a result" do
108
+ let(:raw_result) { nil }
109
+
110
+ it 'should be a kind of MaxMindDB::Result::NamedLocation' do
111
+ expect(result.country).to be_kind_of(MaxMindDB::Result::NamedLocation)
112
+ end
113
+ end
114
+ end
115
+
116
+ describe '#found?' do
117
+ context 'with a result' do
118
+ it 'should return true' do
119
+ expect(result.found?).to be_truthy
120
+ end
121
+ end
122
+
123
+ context 'without a result' do
124
+ let(:raw_result) { nil }
125
+
126
+ it 'should return false' do
127
+ expect(result.found?).to be_falsy
128
+ end
129
+ end
130
+ end
131
+
132
+ describe '#location' do
133
+ context 'with a result' do
134
+ it 'should return a kind of MaxMindDB::Result::Location' do
135
+ expect(result.location).to be_kind_of(MaxMindDB::Result::Location)
136
+ end
137
+
138
+ it 'should initialize the object with the location attributes' do
139
+ expect(MaxMindDB::Result::Location).to receive(:new).with(raw_result['location'])
140
+
141
+ result.location
142
+ end
143
+ end
144
+
145
+ context "without a result" do
146
+ let(:raw_result) { nil }
147
+
148
+ it 'should be a kind of MaxMindDB::Result::Location' do
149
+ expect(result.location).to be_kind_of(MaxMindDB::Result::Location)
150
+ end
151
+ end
152
+ end
153
+
154
+ describe '#postal' do
155
+ context 'with a result' do
156
+ it 'should return a kind of MaxMindDB::Result::Postal' do
157
+ expect(result.postal).to be_kind_of(MaxMindDB::Result::Postal)
158
+ end
159
+
160
+ it 'should initialize the object with the postal attributes' do
161
+ expect(MaxMindDB::Result::Postal).to receive(:new).with(raw_result['postal'])
162
+
163
+ result.postal
164
+ end
165
+ end
166
+
167
+ context "without a result" do
168
+ let(:raw_result) { nil }
169
+
170
+ it 'should be a kind of MaxMindDB::Result::Postal' do
171
+ expect(result.postal).to be_kind_of(MaxMindDB::Result::Postal)
172
+ end
173
+ end
174
+ end
175
+
176
+ describe '#registered_country' do
177
+ context 'with a result' do
178
+ it 'should be a kind of MaxMindDB::Result::NamedLocation' do
179
+ expect(result.registered_country).to be_kind_of(MaxMindDB::Result::NamedLocation)
180
+ end
181
+
182
+ it 'should initialize the location with the registered_country attributes' do
183
+ expect(MaxMindDB::Result::NamedLocation).to receive(:new).with(raw_result['registered_country'])
184
+
185
+ result.registered_country
186
+ end
187
+ end
188
+ end
189
+
190
+ describe '#subdivisions' do
191
+ context 'with a result' do
192
+ it 'should be a kind of Array' do
193
+ expect(result.subdivisions).to be_kind_of(Array)
194
+ end
195
+
196
+ it 'should return as many results as there are subdivisions passed in' do
197
+ expect(result.subdivisions.length).to eq(raw_result['subdivisions'].length)
198
+ end
199
+
200
+ it 'should contain only MaxMindDB::Result::NamedLocation' do
201
+ expect(result.subdivisions.all? { |r| r.kind_of?(MaxMindDB::Result::NamedLocation)}).to be_truthy
202
+ end
203
+
204
+ it 'should initialize the location with the correct attributes' do
205
+ expect(MaxMindDB::Result::NamedLocation).to receive(:new).with(raw_result['subdivisions'].first)
206
+
207
+ result.subdivisions
208
+ end
209
+ end
210
+
211
+ context 'without a result' do
212
+ let(:raw_result) { nil }
213
+
214
+ it 'should be a kind of Array' do
215
+ expect(result.subdivisions).to be_kind_of(Array)
216
+ end
217
+
218
+ it 'should be empty' do
219
+ expect(result.subdivisions).to be_empty
220
+ end
221
+ end
222
+ end
223
+ end
@@ -0,0 +1,104 @@
1
+ require 'maxminddb'
2
+
3
+ describe MaxMindDB do
4
+ let(:city_db) { MaxMindDB.new('spec/cache/GeoLite2-City.mmdb') }
5
+ let(:country_db) { MaxMindDB.new('spec/cache/GeoLite2-Country.mmdb') }
6
+
7
+ context 'for the ip 74.125.225.224' do
8
+ let(:ip) { '74.125.225.224' }
9
+
10
+ it 'returns a MaxMindDB::Result' do
11
+ expect(city_db.lookup(ip)).to be_kind_of(MaxMindDB::Result)
12
+ end
13
+
14
+ it 'finds data' do
15
+ expect(city_db.lookup(ip)).to be_found
16
+ end
17
+
18
+ it 'returns Mountain View as the English name' do
19
+ expect(city_db.lookup(ip).city.name).to eq('Mountain View')
20
+ end
21
+
22
+ it 'returns -122.0574 as the longitude' do
23
+ expect(city_db.lookup(ip).location.longitude).to eq(-122.0574)
24
+ end
25
+
26
+ it 'returns United States as the English country name' do
27
+ expect(country_db.lookup(ip).country.name).to eq('United States')
28
+ end
29
+
30
+ it 'returns US as the country iso code' do
31
+ expect(country_db.lookup(ip).country.iso_code).to eq('US')
32
+ end
33
+
34
+ context 'as a Fixnum' do
35
+ let(:integer_ip) { IPAddr.new(ip).to_i }
36
+
37
+ it 'returns a MaxMindDB::Result' do
38
+ expect(city_db.lookup(integer_ip)).to be_kind_of(MaxMindDB::Result)
39
+ end
40
+
41
+ it 'returns Mountain View as the English name' do
42
+ expect(city_db.lookup(integer_ip).city.name).to eq('Mountain View')
43
+ end
44
+
45
+ it 'returns United States as the English country name' do
46
+ expect(country_db.lookup(integer_ip).country.name).to eq('United States')
47
+ end
48
+ end
49
+ end
50
+
51
+ context 'for the ip 2001:708:510:8:9a6:442c:f8e0:7133' do
52
+ let(:ip) { '2001:708:510:8:9a6:442c:f8e0:7133' }
53
+
54
+ it 'finds data' do
55
+ expect(city_db.lookup(ip)).to be_found
56
+ end
57
+
58
+ it 'returns FI as the country iso code' do
59
+ expect(country_db.lookup(ip).country.iso_code).to eq('FI')
60
+ end
61
+
62
+ context 'as an integer' do
63
+ let(:integer_ip) { IPAddr.new(ip).to_i }
64
+
65
+ it 'returns FI as the country iso code' do
66
+ expect(country_db.lookup(integer_ip).country.iso_code).to eq('FI')
67
+ end
68
+ end
69
+ end
70
+
71
+ context 'for the ip 127.0.0.1' do
72
+ let(:ip) { '127.0.0.1' }
73
+
74
+ it 'returns a MaxMindDB::Result' do
75
+ expect(city_db.lookup(ip)).to be_kind_of(MaxMindDB::Result)
76
+ end
77
+
78
+ it "doesn't find data" do
79
+ expect(city_db.lookup(ip)).to_not be_found
80
+ end
81
+ end
82
+
83
+ context 'test ips' do
84
+ [
85
+ ['185.23.124.1', 'SA'],
86
+ ['178.72.254.1', 'CZ'],
87
+ ['95.153.177.210', 'RU'],
88
+ ['200.148.105.119', 'BR'],
89
+ ['195.59.71.43', 'GB'],
90
+ ['179.175.47.87', 'BR'],
91
+ ['202.67.40.50', 'ID'],
92
+ ].each do |ip, iso|
93
+ it 'returns a MaxMindDB::Result' do
94
+ expect(city_db.lookup(ip)).to be_kind_of(MaxMindDB::Result)
95
+ end
96
+
97
+ it "returns #{iso} as the country iso code" do
98
+ expect(country_db.lookup(ip).country.iso_code).to eq(iso)
99
+ end
100
+ end
101
+ end
102
+ end
103
+
104
+ # vim: et ts=2 sw=2 ff=unix
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: maxminddb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.3
5
+ platform: ruby
6
+ authors:
7
+ - yhirose
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-10-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec-its
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Pure Ruby MaxMind DB binary file reader.
70
+ email:
71
+ - yuji.hirose.bug@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".rspec"
78
+ - ".travis.yml"
79
+ - Gemfile
80
+ - LICENSE.txt
81
+ - README.md
82
+ - Rakefile
83
+ - lib/maxminddb.rb
84
+ - lib/maxminddb/result.rb
85
+ - lib/maxminddb/result/location.rb
86
+ - lib/maxminddb/result/named_location.rb
87
+ - lib/maxminddb/result/postal.rb
88
+ - lib/maxminddb/version.rb
89
+ - maxminddb.gemspec
90
+ - spec/cache/.gitkeep
91
+ - spec/maxminddb/result/location_spec.rb
92
+ - spec/maxminddb/result/named_location_spec.rb
93
+ - spec/maxminddb/result/postal_spec.rb
94
+ - spec/maxminddb/result_spec.rb
95
+ - spec/maxminddb_spec.rb
96
+ homepage: https://github.com/yhirose/maxminddb
97
+ licenses:
98
+ - MIT
99
+ metadata: {}
100
+ post_install_message: MIT
101
+ rdoc_options: []
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ requirements: []
115
+ rubyforge_project:
116
+ rubygems_version: 2.0.14
117
+ signing_key:
118
+ specification_version: 4
119
+ summary: MaxMind DB binary file reader.
120
+ test_files:
121
+ - spec/cache/.gitkeep
122
+ - spec/maxminddb/result/location_spec.rb
123
+ - spec/maxminddb/result/named_location_spec.rb
124
+ - spec/maxminddb/result/postal_spec.rb
125
+ - spec/maxminddb/result_spec.rb
126
+ - spec/maxminddb_spec.rb