qqwry 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +39 -0
- data/Rakefile +10 -0
- data/bin/qqwry +58 -0
- data/lib/qqwry.rb +4 -0
- data/lib/qqwry/database.rb +76 -0
- data/lib/qqwry/record.rb +78 -0
- data/lib/qqwry/version.rb +4 -0
- data/qqwry.gemspec +23 -0
- data/test/tc_database.rb +82 -0
- data/test/tc_record.rb +59 -0
- metadata +134 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Changli Gao
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# QQWry
|
2
|
+
|
3
|
+
A Ruby interface for QQWry IP database.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'qqwry'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install qqwry
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
At First, you need to get the latest QQWry IP database from http://www.cz88.net
|
22
|
+
|
23
|
+
````ruby
|
24
|
+
require 'rubygems'
|
25
|
+
require 'bundler/setup'
|
26
|
+
require 'qqwry'
|
27
|
+
|
28
|
+
db = QQWry::Database.new('QQWry.Dat')
|
29
|
+
r = db.query('8.8.8.8')
|
30
|
+
puts "#{r.country} #{r.area}"
|
31
|
+
````
|
32
|
+
|
33
|
+
## Contributing
|
34
|
+
|
35
|
+
1. Fork it
|
36
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
37
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
38
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
39
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/qqwry
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'qqwry'
|
6
|
+
require 'optparse'
|
7
|
+
require 'csv'
|
8
|
+
|
9
|
+
db_path = 'QQWry.Dat'
|
10
|
+
|
11
|
+
opt_parser = OptionParser.new do |opts|
|
12
|
+
opts.banner = "Usage: #$0 [options] (dump FN.csv)|(query IP)|version"
|
13
|
+
|
14
|
+
opts.separator('Version: ' + QQWry::VERSION)
|
15
|
+
opts.separator('Options:')
|
16
|
+
|
17
|
+
opts.on('-d', '--database PATH',
|
18
|
+
'Use the database at PATH instead of QQWry.Dat') do |d|
|
19
|
+
db_path = d
|
20
|
+
end
|
21
|
+
|
22
|
+
opts.on('-h', '--help', 'Show this message') do |h|
|
23
|
+
puts opts
|
24
|
+
exit
|
25
|
+
end
|
26
|
+
end
|
27
|
+
opt_parser.parse!(ARGV)
|
28
|
+
|
29
|
+
db = QQWry::Database.new(db_path)
|
30
|
+
case ARGV[0]
|
31
|
+
when 'dump'
|
32
|
+
if ARGV.length != 2
|
33
|
+
puts opt_parser
|
34
|
+
exit(1)
|
35
|
+
end
|
36
|
+
CSV.open(ARGV[1], 'w') do |writer|
|
37
|
+
db.each do |r|
|
38
|
+
writer << [r.ip_str_start, r.ip_str_end, r.country, r.area]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
when 'query'
|
42
|
+
if ARGV.length != 2
|
43
|
+
puts opt_parser
|
44
|
+
exit(1)
|
45
|
+
end
|
46
|
+
r = db.query(ARGV[1])
|
47
|
+
puts "country: #{r.country}"
|
48
|
+
puts "area: #{r.area}"
|
49
|
+
when 'version'
|
50
|
+
if ARGV.length != 1
|
51
|
+
puts opt_parser
|
52
|
+
exit(1)
|
53
|
+
end
|
54
|
+
puts db.version
|
55
|
+
else
|
56
|
+
puts opt_parser
|
57
|
+
exit(1)
|
58
|
+
end
|
data/lib/qqwry.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
|
2
|
+
require 'qqwry/record'
|
3
|
+
|
4
|
+
module QQWry
|
5
|
+
class Header < BinData::Record
|
6
|
+
SIZE = 8
|
7
|
+
endian :little
|
8
|
+
uint32 :index_offset_start
|
9
|
+
uint32 :index_offset_end
|
10
|
+
end
|
11
|
+
|
12
|
+
class Index < BinData::Record
|
13
|
+
SIZE = 7
|
14
|
+
endian :little
|
15
|
+
uint32 :ip_start
|
16
|
+
uint24 :record_offset
|
17
|
+
end
|
18
|
+
|
19
|
+
class Database
|
20
|
+
def initialize(file = 'QQWrt.Dat')
|
21
|
+
if file.respond_to?(:to_str)
|
22
|
+
@file = File.new(file.to_str)
|
23
|
+
else
|
24
|
+
@file = file
|
25
|
+
@file.seek(0)
|
26
|
+
end
|
27
|
+
@header = QQWry::Header.read(@file)
|
28
|
+
end
|
29
|
+
|
30
|
+
def each
|
31
|
+
offset = @header.index_offset_start
|
32
|
+
while offset <= @header.index_offset_end
|
33
|
+
@file.seek(offset)
|
34
|
+
index = QQWry::Index.read(@file)
|
35
|
+
record = QQWry::Record.new(index.ip_start, @file, index.record_offset)
|
36
|
+
yield record
|
37
|
+
offset += QQWry::Index::SIZE
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def version
|
42
|
+
# The last record saves the version info
|
43
|
+
@file.seek(@header.index_offset_end)
|
44
|
+
index = QQWry::Index.read(@file)
|
45
|
+
r = QQWry::Record.new(0, @file, index.record_offset)
|
46
|
+
r.country + r.area
|
47
|
+
end
|
48
|
+
|
49
|
+
def query(ip)
|
50
|
+
if ip.respond_to?(:to_str)
|
51
|
+
ip = ip.to_str.split('.').collect{|i| i.to_i}.pack('C4').unpack('N')[0]
|
52
|
+
end
|
53
|
+
raise ArgumentError, "invalid IP" unless (0...(1 << 32)).include?(ip)
|
54
|
+
@file.seek(QQWry::Header::SIZE)
|
55
|
+
low = 0
|
56
|
+
# the end record is the version info
|
57
|
+
start = @header.index_offset_start
|
58
|
+
high = (@header.index_offset_end - start) / QQWry::Index::SIZE
|
59
|
+
while low <= high
|
60
|
+
middle = (low + high) / 2
|
61
|
+
@file.seek(start + middle * QQWry::Index::SIZE)
|
62
|
+
index = QQWry::Index.read(@file)
|
63
|
+
if ip > index.ip_start
|
64
|
+
low = middle + 1
|
65
|
+
elsif ip < index.ip_start
|
66
|
+
high = middle - 1
|
67
|
+
else
|
68
|
+
return QQWry::Record.new(index.ip_start, @file, index.record_offset)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
@file.seek(start + high * QQWry::Index::SIZE)
|
72
|
+
index = QQWry::Index.read(@file)
|
73
|
+
QQWry::Record.new(index.ip_start, @file, index.record_offset)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/lib/qqwry/record.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
|
2
|
+
require 'rubygems'
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'bindata'
|
5
|
+
require 'iconv'
|
6
|
+
|
7
|
+
module QQWry
|
8
|
+
class Record
|
9
|
+
attr_reader :ip_start, :ip_end, :country, :area
|
10
|
+
|
11
|
+
def initialize(ip_start, file, offset)
|
12
|
+
@ip_start = ip_start
|
13
|
+
file.seek(offset)
|
14
|
+
@ip_end = BinData::Uint32le.read(file).to_i
|
15
|
+
case BinData::Uint8.read(file).to_i
|
16
|
+
when 0x1
|
17
|
+
offset = BinData::Uint24le.read(file).to_i
|
18
|
+
file.seek(offset)
|
19
|
+
case BinData::Uint8.read(file).to_i
|
20
|
+
when 0x2
|
21
|
+
file.seek(BinData::Uint24le.read(file).to_i)
|
22
|
+
@country = BinData::Stringz.read(file).to_s
|
23
|
+
file.seek(offset + 4)
|
24
|
+
else
|
25
|
+
file.seek(-1, IO::SEEK_CUR)
|
26
|
+
@country = BinData::Stringz.read(file).to_s
|
27
|
+
end
|
28
|
+
parse_area(file)
|
29
|
+
when 0x2
|
30
|
+
offset = BinData::Uint24le.read(file).to_i
|
31
|
+
parse_area(file)
|
32
|
+
file.seek(offset)
|
33
|
+
@country = BinData::Stringz.read(file).to_s
|
34
|
+
else
|
35
|
+
file.seek(-1, IO::SEEK_CUR)
|
36
|
+
@country = BinData::Stringz.read(file).to_s
|
37
|
+
parse_area(file)
|
38
|
+
end
|
39
|
+
if @country
|
40
|
+
@country = Iconv.conv('UTF-8', 'GBK', @country)
|
41
|
+
@country = nil if @country=~ /\s*CZ88\.NET\s*/
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def ip_str_start
|
46
|
+
ip_str(@ip_start)
|
47
|
+
end
|
48
|
+
|
49
|
+
def ip_str_end
|
50
|
+
ip_str(@ip_end)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def ip_str(ip)
|
56
|
+
[ip].pack('N').unpack('C4').join('.')
|
57
|
+
end
|
58
|
+
|
59
|
+
def parse_area(file)
|
60
|
+
case BinData::Uint8.read(file).to_i
|
61
|
+
when 0x1, 0x2
|
62
|
+
offset = BinData::Uint24le.read(file).to_i
|
63
|
+
if offset > 0
|
64
|
+
file.seek(offset)
|
65
|
+
@area = BinData::Stringz.read(file).to_s
|
66
|
+
end
|
67
|
+
else
|
68
|
+
file.seek(-1, IO::SEEK_CUR)
|
69
|
+
@area = BinData::Stringz.read(file).to_s
|
70
|
+
end
|
71
|
+
if @area
|
72
|
+
@area = Iconv.conv('UTF-8', 'GBK', @area)
|
73
|
+
# CZ88.NET is an advertisement
|
74
|
+
@area = nil if @area =~ /\s*CZ88\.NET\s*/
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/qqwry.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'qqwry/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "qqwry"
|
8
|
+
gem.version = QQWry::VERSION
|
9
|
+
gem.authors = ["Changli Gao"]
|
10
|
+
gem.email = ["xiaosuo@gmail.com"]
|
11
|
+
gem.description = %q{A Ruby interface for QQWry IP database}
|
12
|
+
gem.summary = %q{A Ruby interface for QQWry IP database}
|
13
|
+
gem.homepage = 'https://github.com/xiaosuo/qqwry'
|
14
|
+
gem.files = `git ls-files`.split($/)
|
15
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
16
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
17
|
+
gem.require_paths = ["lib"]
|
18
|
+
gem.license = 'MIT'
|
19
|
+
gem.add_dependency 'bindata'
|
20
|
+
gem.add_development_dependency 'rake'
|
21
|
+
gem.add_development_dependency 'bundler'
|
22
|
+
gem.add_development_dependency 'bindata'
|
23
|
+
end
|
data/test/tc_database.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
|
2
|
+
require 'test/unit'
|
3
|
+
require 'qqwry'
|
4
|
+
|
5
|
+
class TC_DataBase < Test::Unit::TestCase
|
6
|
+
# one record
|
7
|
+
def test_one_record
|
8
|
+
s = "\x12\x00\x00\x00\x12\x00\x00\x00" + # header
|
9
|
+
"\xff\xff\xff\xffc1\x00a1\x00" + # record
|
10
|
+
"\x00\x00\x00\x00\x08\x00\x00" # index
|
11
|
+
db = QQWry::Database.new(StringIO.new(s))
|
12
|
+
assert_equal('c1a1', db.version)
|
13
|
+
records = []
|
14
|
+
db.each{|r| records << r}
|
15
|
+
assert_equal(1, records.length)
|
16
|
+
assert_equal('c1', records[0].country)
|
17
|
+
assert_equal('a1', records[0].area)
|
18
|
+
['0.0.0.0', 0x01020304, '255.255.255.255'].each do |ip|
|
19
|
+
r = db.query(ip)
|
20
|
+
assert_equal('c1', r.country)
|
21
|
+
assert_equal('a1', r.area)
|
22
|
+
end
|
23
|
+
assert_raise(ArgumentError){db.query(-1)}
|
24
|
+
assert_raise(ArgumentError){db.query(1 << 32)}
|
25
|
+
end
|
26
|
+
|
27
|
+
# two records
|
28
|
+
def test_two_record
|
29
|
+
s = "\x1c\x00\x00\x00\x23\x00\x00\x00" + # header
|
30
|
+
"\xff\xff\x00\x00c1\x00a1\x00" + # record 1
|
31
|
+
"\xff\xff\xff\xffc2\x00a2\x00" + # record 2
|
32
|
+
"\x00\x00\x00\x00\x08\x00\x00" + # index 1
|
33
|
+
"\x00\x00\x01\x00\x12\x00\x00" # index 2
|
34
|
+
db = QQWry::Database.new(StringIO.new(s))
|
35
|
+
assert_equal('c2a2', db.version)
|
36
|
+
records = []
|
37
|
+
db.each{|r| records << r}
|
38
|
+
assert_equal(2, records.length)
|
39
|
+
r = [{:c => 'c1', :a => 'a1'}, {:c => 'c2', :a => 'a2'}]
|
40
|
+
(0...2).each do |i|
|
41
|
+
assert_equal(r[i][:c], records[i].country)
|
42
|
+
assert_equal(r[i][:a], records[i].area)
|
43
|
+
end
|
44
|
+
s = {'0.0.0.0' => r[0], '0.0.254.254' => r[0], '0.0.255.255' => r[0],
|
45
|
+
'0.1.0.0' => r[1], '0.1.2.3' => r[1], '255.255.255.255' => r[1]}
|
46
|
+
s.each do |ip, r|
|
47
|
+
rec = db.query(ip)
|
48
|
+
assert_equal(r[:c], rec.country)
|
49
|
+
assert_equal(r[:a], rec.area)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# three records
|
54
|
+
def test_three_record
|
55
|
+
s = "\x26\x00\x00\x00\x34\x00\x00\x00" + # header
|
56
|
+
"\xff\xff\x00\x00c1\x00a1\x00" + # record 1
|
57
|
+
"\xff\xff\xff\x00c2\x00a2\x00" + # record 2
|
58
|
+
"\xff\xff\xff\xffc3\x00a3\x00" + # record 3
|
59
|
+
"\x00\x00\x00\x00\x08\x00\x00" + # index 1
|
60
|
+
"\x00\x00\x01\x00\x12\x00\x00" + # index 2
|
61
|
+
"\x00\x00\x00\x01\x1c\x00\x00" # index 3
|
62
|
+
db = QQWry::Database.new(StringIO.new(s))
|
63
|
+
assert_equal('c3a3', db.version)
|
64
|
+
records = []
|
65
|
+
db.each{|r| records << r}
|
66
|
+
assert_equal(3, records.length)
|
67
|
+
r = [{:c => 'c1', :a => 'a1'}, {:c => 'c2', :a => 'a2'},
|
68
|
+
{:c => 'c3', :a => 'a3'}]
|
69
|
+
(0...3).each do |i|
|
70
|
+
assert_equal(r[i][:c], records[i].country)
|
71
|
+
assert_equal(r[i][:a], records[i].area)
|
72
|
+
end
|
73
|
+
s = {'0.0.0.0' => r[0], '0.0.254.254' => r[0], '0.0.255.255' => r[0],
|
74
|
+
'0.1.0.0' => r[1], '0.1.8.254' => r[1], '0.255.255.255' => r[1],
|
75
|
+
'1.0.0.0' => r[2], '1.2.3.4' => r[2], '255.255.255.255' => r[2]}
|
76
|
+
s.each do |ip, r|
|
77
|
+
rec = db.query(ip)
|
78
|
+
assert_equal(r[:c], rec.country)
|
79
|
+
assert_equal(r[:a], rec.area)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/test/tc_record.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
|
2
|
+
require 'test/unit'
|
3
|
+
require 'qqwry'
|
4
|
+
|
5
|
+
class TC_Record < Test::Unit::TestCase
|
6
|
+
def teardown
|
7
|
+
io = StringIO.new(@s)
|
8
|
+
r = QQWry::Record.new(0x01020300, io, 3)
|
9
|
+
assert_equal(0x01020300, r.ip_start)
|
10
|
+
assert_equal(0x01020304, r.ip_end)
|
11
|
+
assert_equal('1.2.3.0', r.ip_str_start)
|
12
|
+
assert_equal('1.2.3.4', r.ip_str_end)
|
13
|
+
if @nil_country
|
14
|
+
assert_equal(nil, r.country)
|
15
|
+
else
|
16
|
+
assert_equal('country', r.country)
|
17
|
+
end
|
18
|
+
if @nil_area
|
19
|
+
assert_equal(nil, r.area)
|
20
|
+
else
|
21
|
+
assert_equal('area', r.area)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_simple
|
26
|
+
@s = "pad\x04\x03\x02\x01country\x00area\x00"
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_mode1
|
30
|
+
@s = "pad\x04\x03\x02\x01\x01\x0b\x00\x00country\x00area\x00"
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_mode2
|
34
|
+
@s = "pad\x04\x03\x02\x01\x02\x10\x00\x00area\x00country\x00"
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_mix1
|
38
|
+
@s = "pad\x04\x03\x02\x01\x01\x0b\x00\x00\x02\x14\x00\x00area\x00country\x00"
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_mix2
|
42
|
+
@s = "pad\x04\x03\x02\x01\x01\x0b\x00\x00\x02\x13\x00\x00\x01\x1b\x00\x00country\x00area\x00"
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_mix2_2
|
46
|
+
@s = "pad\x04\x03\x02\x01\x01\x0b\x00\x00\x02\x13\x00\x00\x02\x1b\x00\x00country\x00area\x00"
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_zero_offset
|
50
|
+
@s = "pad\x04\x03\x02\x01\x01\x0b\x00\x00\x02\x13\x00\x00\x02\x00\x00\x00country\x00area\x00"
|
51
|
+
@nil_area = true
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_adv
|
55
|
+
@s = "pad\x04\x03\x02\x01\x01\x0b\x00\x00\x02\x13\x00\x00\x02\x1c\x00\x00CZ88.NET\x00 CZ88.NET \x00"
|
56
|
+
@nil_country = true
|
57
|
+
@nil_area = true
|
58
|
+
end
|
59
|
+
end
|
metadata
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: qqwry
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Changli Gao
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-12-17 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: bindata
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
version: "0"
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: rake
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
hash: 3
|
43
|
+
segments:
|
44
|
+
- 0
|
45
|
+
version: "0"
|
46
|
+
type: :development
|
47
|
+
version_requirements: *id002
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: bundler
|
50
|
+
prerelease: false
|
51
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
hash: 3
|
57
|
+
segments:
|
58
|
+
- 0
|
59
|
+
version: "0"
|
60
|
+
type: :development
|
61
|
+
version_requirements: *id003
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: bindata
|
64
|
+
prerelease: false
|
65
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
hash: 3
|
71
|
+
segments:
|
72
|
+
- 0
|
73
|
+
version: "0"
|
74
|
+
type: :development
|
75
|
+
version_requirements: *id004
|
76
|
+
description: A Ruby interface for QQWry IP database
|
77
|
+
email:
|
78
|
+
- xiaosuo@gmail.com
|
79
|
+
executables:
|
80
|
+
- qqwry
|
81
|
+
extensions: []
|
82
|
+
|
83
|
+
extra_rdoc_files: []
|
84
|
+
|
85
|
+
files:
|
86
|
+
- .gitignore
|
87
|
+
- Gemfile
|
88
|
+
- LICENSE.txt
|
89
|
+
- README.md
|
90
|
+
- Rakefile
|
91
|
+
- bin/qqwry
|
92
|
+
- lib/qqwry.rb
|
93
|
+
- lib/qqwry/database.rb
|
94
|
+
- lib/qqwry/record.rb
|
95
|
+
- lib/qqwry/version.rb
|
96
|
+
- qqwry.gemspec
|
97
|
+
- test/tc_database.rb
|
98
|
+
- test/tc_record.rb
|
99
|
+
homepage: https://github.com/xiaosuo/qqwry
|
100
|
+
licenses:
|
101
|
+
- MIT
|
102
|
+
post_install_message:
|
103
|
+
rdoc_options: []
|
104
|
+
|
105
|
+
require_paths:
|
106
|
+
- lib
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
108
|
+
none: false
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
hash: 3
|
113
|
+
segments:
|
114
|
+
- 0
|
115
|
+
version: "0"
|
116
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
|
+
none: false
|
118
|
+
requirements:
|
119
|
+
- - ">="
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
hash: 3
|
122
|
+
segments:
|
123
|
+
- 0
|
124
|
+
version: "0"
|
125
|
+
requirements: []
|
126
|
+
|
127
|
+
rubyforge_project:
|
128
|
+
rubygems_version: 1.8.24
|
129
|
+
signing_key:
|
130
|
+
specification_version: 3
|
131
|
+
summary: A Ruby interface for QQWry IP database
|
132
|
+
test_files:
|
133
|
+
- test/tc_database.rb
|
134
|
+
- test/tc_record.rb
|