sypex_geo 0.1.0 → 0.2.0
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.
- checksums.yaml +4 -4
- data/.travis.yml +5 -3
- data/README.md +30 -49
- data/lib/sypex_geo/database.rb +73 -106
- data/lib/sypex_geo/pack.rb +96 -46
- data/lib/sypex_geo/result.rb +40 -0
- data/lib/sypex_geo/version.rb +1 -1
- data/lib/sypex_geo.rb +1 -21
- data/spec/sypex_geo_spec.rb +94 -74
- metadata +4 -4
- data/lib/sypex_geo/memory_database.rb +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 270f6a9db891c1f4f6235ca37888a22a1cb6aa18
|
4
|
+
data.tar.gz: 2882b224bdbc064f518858245d670df34b08225b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0cbf268f45fcf101a93e837a82567ce4b824be475e20c86c12e5b9d2211619c707b68d13ff1d209ce2d18b7f25966d26b0d3b2fe69f363004bb4a2e70551a67f
|
7
|
+
data.tar.gz: 189c75e6d57c803d169c74f36f711b02a38545a132f5e81512775e14545e7af5a4436861aafa5caad480203930619b62a95e022014419be00a1773979ab2dbfc
|
data/.travis.yml
CHANGED
@@ -6,11 +6,13 @@ rvm:
|
|
6
6
|
- 2.1.0
|
7
7
|
|
8
8
|
env:
|
9
|
-
-
|
9
|
+
- SXGEO_DB=SxGeo.dat
|
10
|
+
- SXGEO_CITY_DB=SxGeoCity.dat
|
10
11
|
|
11
12
|
before_script:
|
12
|
-
- '
|
13
|
+
- 'wget http://sypexgeo.net/files/SxGeoCountry.zip && unzip SxGeoCountry.zip'
|
14
|
+
- 'wget http://sypexgeo.net/files/SxGeoCity_utf8.zip && unzip SxGeoCity_utf8.zip'
|
13
15
|
|
14
16
|
addons:
|
15
17
|
code_climate:
|
16
|
-
repo_token: 15e49d47a87a130d4a3cde0bb2ce14fe9053f525e92495a215027aff3e552368
|
18
|
+
repo_token: 15e49d47a87a130d4a3cde0bb2ce14fe9053f525e92495a215027aff3e552368
|
data/README.md
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
[](http://badge.fury.io/rb/sypex_geo)
|
2
|
+
[](https://travis-ci.org/kolesnikovde/sypex_geo)
|
3
|
+
[](https://codeclimate.com/github/kolesnikovde/sypex_geo)
|
4
|
+
[](https://codeclimate.com/github/kolesnikovde/sypex_geo)
|
2
5
|
|
3
6
|
# SypexGeo
|
4
7
|
|
@@ -19,65 +22,43 @@ And then execute:
|
|
19
22
|
```ruby
|
20
23
|
require 'sypex_geo'
|
21
24
|
|
22
|
-
db = SypexGeo::Database.new('./
|
23
|
-
db.
|
25
|
+
db = SypexGeo::Database.new('./SxGeoCity.dat')
|
26
|
+
location = db.query('<IPv4 address>')
|
27
|
+
|
28
|
+
location.city
|
29
|
+
# => {
|
30
|
+
# id: 524901,
|
31
|
+
# lat: 55.75222,
|
32
|
+
# lon: 37.61556,
|
33
|
+
# name_ru: 'Москва',
|
34
|
+
# name_en: 'Moscow'
|
35
|
+
# }
|
36
|
+
|
37
|
+
location.region
|
24
38
|
# => {
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
# name_ru: 'Москва',
|
30
|
-
# name_en: 'Moscow',
|
31
|
-
# okato: '45'
|
32
|
-
# },
|
33
|
-
# country: {
|
34
|
-
# id: 185,
|
35
|
-
# iso: 'RU'
|
36
|
-
# },
|
37
|
-
# region: nil
|
39
|
+
# id: 524894,
|
40
|
+
# iso: 'RU-MOW',
|
41
|
+
# name_ru: 'Москва',
|
42
|
+
# name_en: 'Moskva'
|
38
43
|
# }
|
39
44
|
|
40
|
-
|
41
|
-
db.lookup(<IPv4 address>, true)
|
45
|
+
location.country
|
42
46
|
# => {
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
# okato: '45'
|
50
|
-
# },
|
51
|
-
# region: {
|
52
|
-
# id: 524894,
|
53
|
-
# name_ru: 'Москва',
|
54
|
-
# name_en: 'Moskva',
|
55
|
-
# lat: 55,
|
56
|
-
# lon: 37,
|
57
|
-
# iso: 'RU-MOW',
|
58
|
-
# timezone: 'Europe/Moscow',
|
59
|
-
# okato: '45'
|
60
|
-
# },
|
61
|
-
# country: {
|
62
|
-
# id: 185,
|
63
|
-
# iso: 'RU',
|
64
|
-
# continent: 'EU',
|
65
|
-
# lat: 60,
|
66
|
-
# lon: 100,
|
67
|
-
# name_ru: 'Россия',
|
68
|
-
# name_en: 'Russia',
|
69
|
-
# timezone: 'Europe/Moscow'
|
70
|
-
# }
|
47
|
+
# id: 185,
|
48
|
+
# iso: 'RU',
|
49
|
+
# lat: 60.0,
|
50
|
+
# lon: 100.0,
|
51
|
+
# name_ru: 'Россия',
|
52
|
+
# name_en: 'Russia'
|
71
53
|
# }
|
72
54
|
|
73
|
-
|
74
|
-
|
75
|
-
db.lookup(<IPv4 address>)
|
55
|
+
location.country_code
|
56
|
+
# => 'RU'
|
76
57
|
```
|
77
58
|
|
78
59
|
## Testing
|
79
60
|
|
80
|
-
$
|
61
|
+
$ SXGEO_DB=./SxGeo.dat SXGEO_CITY_DB=./SxGeoCity.dat rspec
|
81
62
|
|
82
63
|
## License
|
83
64
|
|
data/lib/sypex_geo/database.rb
CHANGED
@@ -5,159 +5,126 @@ module SypexGeo
|
|
5
5
|
end
|
6
6
|
|
7
7
|
class Database
|
8
|
-
|
8
|
+
TYPE_COUNTRY = 1
|
9
|
+
TYPE_CITY = 3
|
10
|
+
|
11
|
+
attr_reader :version, :time
|
9
12
|
|
10
13
|
def initialize(path)
|
11
14
|
@file = File.open(path, 'rb')
|
12
15
|
|
13
|
-
|
16
|
+
parse_header
|
17
|
+
setup
|
14
18
|
end
|
15
19
|
|
16
|
-
def
|
17
|
-
if
|
18
|
-
|
20
|
+
def query(ip)
|
21
|
+
if position = search(ip)
|
22
|
+
Result.new(position, self)
|
19
23
|
end
|
20
24
|
end
|
21
25
|
|
26
|
+
def read_country(position)
|
27
|
+
@country_parser ||= Pack.new(@country_pack)
|
28
|
+
@country_parser.parse(@cities_db[position, @country_size])
|
29
|
+
end
|
30
|
+
|
31
|
+
def read_region(position)
|
32
|
+
@region_parser ||= Pack.new(@region_pack)
|
33
|
+
@region_parser.parse(@regions_db[position, @region_size])
|
34
|
+
end
|
35
|
+
|
36
|
+
def read_city(position)
|
37
|
+
@city_parser ||= Pack.new(@city_pack)
|
38
|
+
@city_parser.parse(@cities_db[position, @city_size])
|
39
|
+
end
|
40
|
+
|
41
|
+
def country?
|
42
|
+
@type == TYPE_COUNTRY
|
43
|
+
end
|
44
|
+
|
45
|
+
def city?
|
46
|
+
@type == TYPE_CITY
|
47
|
+
end
|
48
|
+
|
22
49
|
def inspect
|
23
50
|
"#<#{self.class}:0x#{object_id} @version=#{@version}>"
|
24
51
|
end
|
25
52
|
|
26
53
|
protected
|
27
54
|
|
28
|
-
def
|
55
|
+
def parse_header
|
29
56
|
if header = @file.read(40)
|
30
57
|
id, @version, @time, @type, @charset,
|
31
|
-
@
|
32
|
-
@
|
33
|
-
@
|
58
|
+
@block_idx_size, @main_idx_size, @range, @db_records_count, @id_size,
|
59
|
+
@region_size, @city_size, @regions_db_size, @cities_db_size,
|
60
|
+
@country_size, @countries_db_size,
|
34
61
|
@pack_size = header.unpack('a3CNCCCnnNCnnNNnNn')
|
35
62
|
end
|
36
63
|
|
37
64
|
raise DatabaseError.new, 'Wrong file format' unless id == 'SxG'
|
65
|
+
end
|
38
66
|
|
67
|
+
def setup
|
39
68
|
@pack = @file.read(@pack_size).split("\0")
|
40
|
-
@
|
41
|
-
|
69
|
+
@country_pack, @region_pack, @city_pack = @pack
|
70
|
+
|
71
|
+
@block_idx = @file.read(@block_idx_size * 4).unpack('N*')
|
72
|
+
@main_idx = @file.read(@main_idx_size * 4).scan(/.{1,4}/m)
|
42
73
|
|
43
|
-
@
|
44
|
-
@
|
45
|
-
@
|
46
|
-
@
|
74
|
+
@db_record_size = 3 + @id_size
|
75
|
+
@db = @file.read(@db_records_count * @db_record_size)
|
76
|
+
@regions_db = @file.read(@regions_db_size) if @regions_db_size > 0
|
77
|
+
@cities_db = @file.read(@cities_db_size) if @cities_db_size > 0
|
47
78
|
end
|
48
79
|
|
49
80
|
def search(ip)
|
50
|
-
|
51
|
-
|
52
|
-
return if ip1n == 0 or ip1n == 127 or ip1n >= 224
|
81
|
+
octet = ip.to_i
|
82
|
+
return if octet == 0 or octet == 127 or octet >= @block_idx_size
|
53
83
|
|
84
|
+
min, max = @block_idx[octet - 1], @block_idx[octet]
|
85
|
+
range = @range
|
54
86
|
ipn = IPAddr.new(ip).hton
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
min = part > 0 ? part * @range : 0
|
60
|
-
max = part > @m_idx_len ? @db_items : (part + 1) * @range
|
61
|
-
min = blocks_min if min < blocks_min
|
62
|
-
max = blocks_max if max > blocks_max
|
63
|
-
else
|
64
|
-
min = blocks_min
|
65
|
-
max = blocks_max
|
87
|
+
|
88
|
+
if max - min > range
|
89
|
+
min = range * main_idx_search(ipn, min / range, (max / range) - 1)
|
90
|
+
max = min + range
|
66
91
|
end
|
67
92
|
|
68
|
-
|
93
|
+
db_search(ipn, min, max)
|
69
94
|
end
|
70
95
|
|
71
|
-
def
|
72
|
-
idx = @
|
96
|
+
def main_idx_search(ipn, min, max)
|
97
|
+
idx = @main_idx
|
73
98
|
|
74
|
-
while
|
75
|
-
|
99
|
+
while min < max
|
100
|
+
mid = (min + max) / 2
|
76
101
|
|
77
|
-
if ipn > idx[
|
78
|
-
min =
|
102
|
+
if ipn > idx[mid]
|
103
|
+
min = mid + 1
|
79
104
|
else
|
80
|
-
max =
|
105
|
+
max = mid
|
81
106
|
end
|
82
107
|
end
|
83
108
|
|
84
|
-
while ipn > idx[min]
|
85
|
-
break if min >= max
|
86
|
-
min += 1
|
87
|
-
end
|
88
|
-
|
89
109
|
min
|
90
110
|
end
|
91
111
|
|
92
|
-
def
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
end
|
97
|
-
|
98
|
-
def search_db_chunk(data, ipn, min, max)
|
99
|
-
block_len = @block_len
|
112
|
+
def db_search(ipn, min, max)
|
113
|
+
db = @db
|
114
|
+
db_record_size = @db_record_size
|
115
|
+
octets = ipn[1, 3]
|
100
116
|
|
101
|
-
|
102
|
-
|
117
|
+
while min < max
|
118
|
+
mid = (min + max) / 2
|
103
119
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
min = offset
|
109
|
-
else
|
110
|
-
max = offset
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
while ipn >= data[min * block_len, 3]
|
115
|
-
min += 1
|
116
|
-
break if min >= max
|
120
|
+
if octets > db[mid * db_record_size, 3]
|
121
|
+
min = mid + 1
|
122
|
+
else
|
123
|
+
max = mid
|
117
124
|
end
|
118
|
-
else
|
119
|
-
min += 1
|
120
|
-
end
|
121
|
-
|
122
|
-
data[min * block_len - @id_len, @id_len].unpack('H*').first.hex
|
123
|
-
end
|
124
|
-
|
125
|
-
def read_data(seek, limit, type)
|
126
|
-
@file.pos = (type == TYPE_REGION ? @regions_begin : @cities_begin) + seek
|
127
|
-
Pack.parse(@pack[type], @file.read(limit))
|
128
|
-
end
|
129
|
-
|
130
|
-
def read_country(seek)
|
131
|
-
read_data(seek, @max_country, TYPE_COUNTRY)
|
132
|
-
end
|
133
|
-
|
134
|
-
def read_region(seek)
|
135
|
-
read_data(seek, @max_region, TYPE_REGION)
|
136
|
-
end
|
137
|
-
|
138
|
-
def read_city(seek)
|
139
|
-
read_data(seek, @max_city, TYPE_CITY)
|
140
|
-
end
|
141
|
-
|
142
|
-
def read_location(seek, full = false)
|
143
|
-
region = nil
|
144
|
-
city = nil
|
145
|
-
country = nil
|
146
|
-
|
147
|
-
if seek < @country_size
|
148
|
-
country = read_country(seek)
|
149
|
-
elsif city = read_city(seek)
|
150
|
-
region_seek = city.delete(:region_seek)
|
151
|
-
country_id = city.delete(:country_id)
|
152
|
-
country = { id: country_id, iso: COUNTRY_CODES[country_id - 1] }
|
153
|
-
end
|
154
|
-
|
155
|
-
if full and region_seek
|
156
|
-
region = read_region(region_seek)
|
157
|
-
country = read_country(region.delete(:country_seek))
|
158
125
|
end
|
159
126
|
|
160
|
-
|
127
|
+
db[min * db_record_size - @id_size, @id_size].unpack('H*')[0].hex
|
161
128
|
end
|
162
129
|
end
|
163
130
|
end
|
data/lib/sypex_geo/pack.rb
CHANGED
@@ -1,28 +1,21 @@
|
|
1
1
|
module SypexGeo
|
2
|
-
|
2
|
+
class Pack
|
3
3
|
def self.parse(pack, data)
|
4
|
+
new(pack).parse(data)
|
5
|
+
end
|
6
|
+
|
7
|
+
def initialize(pack)
|
8
|
+
@pack = pack
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse(data)
|
12
|
+
@data = data
|
13
|
+
@pos = 0
|
4
14
|
result = {}
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
if data.nil? or data.empty?
|
11
|
-
val = type[0] =~ /b|c/ ? '' : 0
|
12
|
-
else
|
13
|
-
if type[0] == 'b'
|
14
|
-
len = data.index("\0", pos) - pos + 1
|
15
|
-
val = data[pos, len - 1].force_encoding('UTF-8')
|
16
|
-
else
|
17
|
-
len = type_length(type)
|
18
|
-
val = unpack(type, data[pos, len])
|
19
|
-
val = val[0] if val.is_a?(Array)
|
20
|
-
end
|
21
|
-
|
22
|
-
pos += len
|
23
|
-
end
|
24
|
-
|
25
|
-
result[name.to_sym] = val
|
15
|
+
|
16
|
+
@pack.split('/').each do |part|
|
17
|
+
chunk, name = part.split(':')
|
18
|
+
result[name.to_sym] = parse_chunk(chunk)
|
26
19
|
end
|
27
20
|
|
28
21
|
result
|
@@ -30,33 +23,90 @@ module SypexGeo
|
|
30
23
|
|
31
24
|
protected
|
32
25
|
|
33
|
-
def
|
34
|
-
case
|
35
|
-
when
|
36
|
-
when
|
37
|
-
when
|
38
|
-
when '
|
39
|
-
when '
|
40
|
-
|
26
|
+
def parse_chunk(chunk)
|
27
|
+
case chunk[0]
|
28
|
+
when 't' then read_int8(chunk)
|
29
|
+
when 'T' then read_uint8(chunk)
|
30
|
+
when 's' then read_int16(chunk)
|
31
|
+
when 'S' then read_uint16(chunk)
|
32
|
+
when 'm' then read_int24(chunk)
|
33
|
+
when 'M' then read_uint24(chunk)
|
34
|
+
when 'i' then read_int32(chunk)
|
35
|
+
when 'I' then read_uint32(chunk)
|
36
|
+
when 'f' then read_float(chunk)
|
37
|
+
when 'd' then read_double(chunk)
|
38
|
+
when 'n' then read_decimal16(chunk)
|
39
|
+
when 'N' then read_decimal32(chunk)
|
40
|
+
when 'c' then read_chars(chunk)
|
41
|
+
when 'b' then read_blob(chunk)
|
41
42
|
end
|
42
43
|
end
|
43
44
|
|
44
|
-
def
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
45
|
+
def read(len)
|
46
|
+
@pos += len
|
47
|
+
@data[@pos - len, len]
|
48
|
+
end
|
49
|
+
|
50
|
+
def read_string(len)
|
51
|
+
read(len).strip.force_encoding('UTF-8')
|
52
|
+
end
|
53
|
+
|
54
|
+
def read_int8(chunk)
|
55
|
+
read(1).unpack('c')[0]
|
56
|
+
end
|
57
|
+
|
58
|
+
def read_uint8(chunk)
|
59
|
+
read(1).unpack('C')[0]
|
60
|
+
end
|
61
|
+
|
62
|
+
def read_int16(chunk)
|
63
|
+
read(2).unpack('s')[0]
|
64
|
+
end
|
65
|
+
|
66
|
+
def read_uint16(chunk)
|
67
|
+
read(2).unpack('S')[0]
|
68
|
+
end
|
69
|
+
|
70
|
+
def read_int24(chunk)
|
71
|
+
raw = read(3)
|
72
|
+
raw = raw + (raw[2].ord >> 7) > 0 ? "\xFF" : "\0"
|
73
|
+
raw.unpack('l')[0]
|
74
|
+
end
|
75
|
+
|
76
|
+
def read_uint24(chunk)
|
77
|
+
(read(3) + "\0").unpack('L')[0]
|
78
|
+
end
|
79
|
+
|
80
|
+
def read_int32(chunk)
|
81
|
+
read(4).unpack('l')[0]
|
82
|
+
end
|
83
|
+
|
84
|
+
def read_uint32(chunk)
|
85
|
+
read(4).unpack('L')[0]
|
86
|
+
end
|
87
|
+
|
88
|
+
def read_float(chunk)
|
89
|
+
read(4).unpack('f')[0]
|
90
|
+
end
|
91
|
+
|
92
|
+
def read_double(chunk)
|
93
|
+
read(8).unpack('d')[0]
|
94
|
+
end
|
95
|
+
|
96
|
+
def read_decimal16(chunk)
|
97
|
+
read(2).unpack('s')[0].to_f / (10 ** chunk[1].to_i)
|
98
|
+
end
|
99
|
+
|
100
|
+
def read_decimal32(chunk)
|
101
|
+
read(4).unpack('l')[0].to_f / (10 ** chunk[1].to_i)
|
102
|
+
end
|
103
|
+
|
104
|
+
def read_chars(chunk)
|
105
|
+
read_string(chunk[1..-1].to_i)
|
106
|
+
end
|
107
|
+
|
108
|
+
def read_blob(chunk)
|
109
|
+
read_string(@data.index("\0", @pos) - @pos + 1)
|
60
110
|
end
|
61
111
|
end
|
62
112
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module SypexGeo
|
2
|
+
COUNTRY_CODES = %w[
|
3
|
+
AP EU AD AE AF AG AI AL AM AN AO AQ AR AS AT AU AW AZ BA BB BD BE BF BG
|
4
|
+
BH BI BJ BM BN BO BR BS BT BV BW BY BZ CA CC CD CF CG CH CI CK CL CM CN
|
5
|
+
CO CR CU CV CX CY CZ DE DJ DK DM DO DZ EC EE EG EH ER ES ET FI FJ FK FM
|
6
|
+
FO FR FX GA GB GD GE GF GH GI GL GM GN GP GQ GR GS GT GU GW GY HK HM HN
|
7
|
+
HR HT HU ID IE IL IN IO IQ IR IS IT JM JO JP KE KG KH KI KM KN KP KR KW
|
8
|
+
KY KZ LA LB LC LI LK LR LS LT LU LV LY MA MC MD MG MH MK ML MM MN MO MP
|
9
|
+
MQ MR MS MT MU MV MW MX MY MZ NA NC NE NF NG NI NL NO NP NR NU NZ OM PA
|
10
|
+
PE PF PG PH PK PL PM PN PR PS PT PW PY QA RE RO RU RW SA SB SC SD SE SG
|
11
|
+
SH SI SJ SK SL SM SN SO SR ST SV SY SZ TC TD TF TG TH TJ TK TM TN TO TL
|
12
|
+
TR TT TV TW TZ UA UG UM US UY UZ VA VC VE VG VI VN VU WF WS YE YT RS ZA
|
13
|
+
ZM ME ZW A1 A2 O1 AX GG IM JE BL MF
|
14
|
+
]
|
15
|
+
|
16
|
+
class Result
|
17
|
+
def initialize(position, database)
|
18
|
+
@position = position
|
19
|
+
@database = database
|
20
|
+
end
|
21
|
+
|
22
|
+
def city
|
23
|
+
@city ||= @database.read_city(@position)
|
24
|
+
end
|
25
|
+
|
26
|
+
def region
|
27
|
+
@region ||= @database.read_region(city[:region_seek])
|
28
|
+
end
|
29
|
+
|
30
|
+
def country
|
31
|
+
@country ||= @database.read_country(region[:country_seek])
|
32
|
+
end
|
33
|
+
|
34
|
+
def country_code
|
35
|
+
@country_code ||= begin
|
36
|
+
COUNTRY_CODES[(@database.country? ? @position : city[:country_id]) - 1]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/sypex_geo/version.rb
CHANGED
data/lib/sypex_geo.rb
CHANGED
@@ -1,24 +1,4 @@
|
|
1
1
|
require 'sypex_geo/database'
|
2
|
-
require 'sypex_geo/
|
2
|
+
require 'sypex_geo/result'
|
3
3
|
require 'sypex_geo/pack'
|
4
4
|
require 'sypex_geo/version'
|
5
|
-
|
6
|
-
module SypexGeo
|
7
|
-
TYPE_COUNTRY = 0
|
8
|
-
TYPE_REGION = 1
|
9
|
-
TYPE_CITY = 2
|
10
|
-
|
11
|
-
COUNTRY_CODES = %w[
|
12
|
-
AP EU AD AE AF AG AI AL AM AN AO AQ AR AS AT AU AW AZ BA BB BD BE BF BG
|
13
|
-
BH BI BJ BM BN BO BR BS BT BV BW BY BZ CA CC CD CF CG CH CI CK CL CM CN
|
14
|
-
CO CR CU CV CX CY CZ DE DJ DK DM DO DZ EC EE EG EH ER ES ET FI FJ FK FM
|
15
|
-
FO FR FX GA GB GD GE GF GH GI GL GM GN GP GQ GR GS GT GU GW GY HK HM HN
|
16
|
-
HR HT HU ID IE IL IN IO IQ IR IS IT JM JO JP KE KG KH KI KM KN KP KR KW
|
17
|
-
KY KZ LA LB LC LI LK LR LS LT LU LV LY MA MC MD MG MH MK ML MM MN MO MP
|
18
|
-
MQ MR MS MT MU MV MW MX MY MZ NA NC NE NF NG NI NL NO NP NR NU NZ OM PA
|
19
|
-
PE PF PG PH PK PL PM PN PR PS PT PW PY QA RE RO RU RW SA SB SC SD SE SG
|
20
|
-
SH SI SJ SK SL SM SN SO SR ST SV SY SZ TC TD TF TG TH TJ TK TM TN TO TL
|
21
|
-
TR TT TV TW TZ UA UG UM US UY UZ VA VC VE VG VI VN VU WF WS YE YT RS ZA
|
22
|
-
ZM ME ZW A1 A2 O1 AX GG IM JE BL MF
|
23
|
-
]
|
24
|
-
end
|
data/spec/sypex_geo_spec.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
1
3
|
require './spec/spec_helper'
|
2
4
|
require 'sypex_geo'
|
5
|
+
require 'ipaddr'
|
3
6
|
|
4
7
|
describe SypexGeo do
|
5
8
|
let(:demo_ip) do
|
@@ -7,111 +10,128 @@ describe SypexGeo do
|
|
7
10
|
'80.90.64.1'
|
8
11
|
end
|
9
12
|
|
10
|
-
let(:default_db_file) do
|
11
|
-
File.expand_path(__FILE__ + '/../support/sypexgeo_city_max.dat')
|
12
|
-
end
|
13
|
-
|
14
13
|
let(:invalid_db_file) do
|
15
14
|
File.expand_path(__FILE__ + '/../support/invalid.dat')
|
16
15
|
end
|
17
16
|
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
shared_examples 'geo db' do
|
18
|
+
describe '#initialize' do
|
19
|
+
it 'raises error if database is invalid' do
|
20
|
+
expect do
|
21
|
+
described_class.new(invalid_db_file)
|
22
|
+
end.to raise_error(SypexGeo::DatabaseError)
|
23
|
+
end
|
24
|
+
end
|
21
25
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
26
|
+
describe '#query' do
|
27
|
+
it 'returns nil if IP address is reserved' do
|
28
|
+
expect(subject.query('0.0.0.0')).to be_nil
|
29
|
+
expect(subject.query('127.0.0.0')).to be_nil
|
30
|
+
expect(subject.query('224.0.0.0')).to be_nil
|
31
|
+
expect(subject.query('255.0.0.0')).to be_nil
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'raises error if IP address is invalid' do
|
35
|
+
error = if IPAddr.const_defined?('InvalidAddressError')
|
36
|
+
IPAddr::InvalidAddressError
|
37
|
+
else
|
38
|
+
ArgumentError
|
39
|
+
end
|
40
|
+
|
41
|
+
expect do
|
42
|
+
subject.query('1.invalid')
|
43
|
+
end.to raise_error(error)
|
44
|
+
end
|
45
|
+
end
|
38
46
|
end
|
39
47
|
|
40
|
-
|
41
|
-
|
42
|
-
|
48
|
+
shared_examples 'city db' do
|
49
|
+
it_behaves_like 'geo db'
|
50
|
+
|
51
|
+
let(:city_info) do
|
52
|
+
{
|
43
53
|
id: 524901,
|
44
|
-
|
45
|
-
|
54
|
+
country_id: 185,
|
55
|
+
lat: 55.75222,
|
56
|
+
lon: 37.61556,
|
46
57
|
name_ru: 'Москва',
|
47
58
|
name_en: 'Moscow',
|
48
|
-
|
49
|
-
}
|
50
|
-
|
59
|
+
region_seek: 11795
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
let(:region_info) do
|
64
|
+
{
|
51
65
|
id: 524894,
|
66
|
+
iso: 'RU-MOW',
|
52
67
|
name_ru: 'Москва',
|
53
68
|
name_en: 'Moskva',
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
country: {
|
69
|
+
country_seek: 9128
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
let(:country_info) do
|
74
|
+
{
|
61
75
|
id: 185,
|
62
76
|
iso: 'RU',
|
63
|
-
|
64
|
-
|
65
|
-
lon: 100,
|
77
|
+
lat: 60.0,
|
78
|
+
lon: 100.0,
|
66
79
|
name_ru: 'Россия',
|
67
|
-
name_en: 'Russia'
|
68
|
-
timezone: 'Europe/Moscow'
|
80
|
+
name_en: 'Russia'
|
69
81
|
}
|
70
|
-
|
71
|
-
end
|
82
|
+
end
|
72
83
|
|
73
|
-
|
74
|
-
|
75
|
-
it 'raises error if database is invalid' do
|
76
|
-
expect do
|
77
|
-
subject.class.new(invalid_db_file)
|
78
|
-
end.to raise_error(SypexGeo::DatabaseError)
|
79
|
-
end
|
84
|
+
let(:country_code) do
|
85
|
+
'RU'
|
80
86
|
end
|
81
87
|
|
82
|
-
|
83
|
-
|
84
|
-
expect(subject.lookup('0.0.0.0')).to be_nil
|
85
|
-
expect(subject.lookup('127.0.0.0')).to be_nil
|
86
|
-
expect(subject.lookup('224.0.0.0')).to be_nil
|
87
|
-
expect(subject.lookup('255.0.0.0')).to be_nil
|
88
|
-
end
|
88
|
+
it { should be_city }
|
89
|
+
it { should_not be_country }
|
89
90
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
end.to raise_error(IPAddr::InvalidAddressError)
|
94
|
-
end
|
91
|
+
describe '#query' do
|
92
|
+
it 'returns location info' do
|
93
|
+
location = subject.query(demo_ip)
|
95
94
|
|
96
|
-
|
97
|
-
expect(
|
95
|
+
expect(location.city).to eq(city_info)
|
96
|
+
expect(location.region).to eq(region_info)
|
97
|
+
expect(location.country).to eq(country_info)
|
98
|
+
expect(location.country_code).to eq(country_code)
|
98
99
|
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
shared_examples 'country db' do
|
104
|
+
it_behaves_like 'geo db'
|
105
|
+
|
106
|
+
let(:country_code) do
|
107
|
+
'RU'
|
108
|
+
end
|
99
109
|
|
100
|
-
|
101
|
-
|
110
|
+
it { should be_country }
|
111
|
+
it { should_not be_city }
|
112
|
+
|
113
|
+
describe '#query' do
|
114
|
+
it 'returns country code' do
|
115
|
+
expect(subject.query(demo_ip).country_code).to eq(country_code)
|
102
116
|
end
|
103
117
|
end
|
104
118
|
end
|
105
119
|
|
106
120
|
describe SypexGeo::Database do
|
107
|
-
|
121
|
+
if ENV['SXGEO_CITY_DB']
|
122
|
+
context 'city db' do
|
123
|
+
subject { SypexGeo::Database.new(ENV['SXGEO_CITY_DB']) }
|
108
124
|
|
109
|
-
|
110
|
-
|
125
|
+
it_behaves_like 'city db'
|
126
|
+
end
|
127
|
+
end
|
111
128
|
|
112
|
-
|
113
|
-
|
129
|
+
if ENV['SXGEO_DB']
|
130
|
+
context 'country db' do
|
131
|
+
subject { SypexGeo::Database.new(ENV['SXGEO_DB']) }
|
114
132
|
|
115
|
-
|
133
|
+
it_behaves_like 'country db'
|
134
|
+
end
|
135
|
+
end
|
116
136
|
end
|
117
137
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sypex_geo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kolesnikov Danil
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-09-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -82,8 +82,8 @@ files:
|
|
82
82
|
- Rakefile
|
83
83
|
- lib/sypex_geo.rb
|
84
84
|
- lib/sypex_geo/database.rb
|
85
|
-
- lib/sypex_geo/memory_database.rb
|
86
85
|
- lib/sypex_geo/pack.rb
|
86
|
+
- lib/sypex_geo/result.rb
|
87
87
|
- lib/sypex_geo/version.rb
|
88
88
|
- spec/spec_helper.rb
|
89
89
|
- spec/support/invalid.dat
|
@@ -109,7 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
109
109
|
version: '0'
|
110
110
|
requirements: []
|
111
111
|
rubyforge_project:
|
112
|
-
rubygems_version: 2.2.
|
112
|
+
rubygems_version: 2.2.1
|
113
113
|
signing_key:
|
114
114
|
specification_version: 4
|
115
115
|
summary: Sypex Geo IP database adapter for Ruby.
|
@@ -1,20 +0,0 @@
|
|
1
|
-
module SypexGeo
|
2
|
-
class MemoryDatabase < Database
|
3
|
-
def setup!
|
4
|
-
super
|
5
|
-
|
6
|
-
@db = @file.read(@db_items * @block_len)
|
7
|
-
@regions_db = @file.read(@region_size) if @region_size > 0
|
8
|
-
@cities_db = @file.read(@city_size) if @city_size > 0
|
9
|
-
end
|
10
|
-
|
11
|
-
def search_db(ipn, min, max)
|
12
|
-
search_db_chunk(@db, ipn, min, max)
|
13
|
-
end
|
14
|
-
|
15
|
-
def read_data(seek, limit, type)
|
16
|
-
raw = (type == TYPE_REGION ? @regions_db : @cities_db)[seek, limit]
|
17
|
-
Pack.parse(@pack[type], raw)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|