maxmind_geoip2 0.0.8
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 +7 -0
- data/.gitignore +7 -0
- data/Gemfile +3 -0
- data/README.md +64 -0
- data/Rakefile +37 -0
- data/ext/maxmind_geoip2/extconf.rb +14 -0
- data/ext/maxmind_geoip2/maxmind_geoip2.c +150 -0
- data/lib/maxmind_geoip2.rb +1 -0
- data/maxmind_geoip2.gemspec +20 -0
- data/spec/maxmind_geoip2_spec.rb +75 -0
- data/spec/spec_helper.rb +10 -0
- metadata +83 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d44d6918c53ce5896328cf616a9aa3a8ab27ab52
|
4
|
+
data.tar.gz: 8834d5eba9dcac03dd2fe670412da8d65e57d4ad
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c7f4cb6969a08e851c94c1112261bea19e8a81d306338fc6eb1e0172fd51237ab29f57974490dbf46a5e0400a080bbb291f28ccaed1d8c1081a755dbce3bf2f6
|
7
|
+
data.tar.gz: 20ae0471170fa6022f853257fcb93ff37ed9c3774b07e748db0aa562903e41c47cfdb14a1465f88e8618b1733ed3d20943a71751cc89411db0b616d35538d1ac
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# Ruby Maxmind GeoIP2 Bindings
|
2
|
+
|
3
|
+
## Description
|
4
|
+
|
5
|
+
Searches city by ip address in local database from [maxmind.com](http://dev.maxmind.com/geoip/geoip2/geolite2/).
|
6
|
+
|
7
|
+
Use it with rails. For now, I think.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
[libmaxminddb](https://github.com/maxmind/libmaxminddb) must be installed.
|
12
|
+
|
13
|
+
```
|
14
|
+
gem 'maxmind_geoip2'
|
15
|
+
```
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
### Configuration, can be moved to rails initializer
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
MaxmindGeoIP2.file '<local_db_file.mmdb>' # default: GeoLite2-City.mmdb
|
23
|
+
MaxmindGeoIP2.locale 'ru' # default: 'ru'
|
24
|
+
```
|
25
|
+
|
26
|
+
### Further usage:
|
27
|
+
|
28
|
+
Returns nil if nothing found and raises exception if file not opened or not found
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
city = MaxmindGeoIP2.locate(<ip address>, <optional lang>)
|
32
|
+
|
33
|
+
city = MaxmindGeoIP2.locate '77.93.127.33'
|
34
|
+
=> {"city"=>"Тамбов",
|
35
|
+
"city_geoname_id"=>484646,
|
36
|
+
"country"=>"Россия",
|
37
|
+
"country_geoname_id"=>2017370,
|
38
|
+
"country_code"=>"RU",
|
39
|
+
"continent"=>"Европа",
|
40
|
+
"continent_code"=>"EU",
|
41
|
+
"continent_geoname_id"=>6255148,
|
42
|
+
"subdivision"=>"Тамбовская область",
|
43
|
+
"subdivision_code"=>"TAM",
|
44
|
+
"postal_code"=>nil,
|
45
|
+
"latitude"=>52.731700000000004,
|
46
|
+
"longitude"=>41.4433,
|
47
|
+
"time_zone"=>"Europe/Moscow"}
|
48
|
+
|
49
|
+
city = MaxmindGeoIP2.locate '77.93.127.33', 'en'
|
50
|
+
=> {"city"=>"Tambov",
|
51
|
+
"city_geoname_id"=>484646,
|
52
|
+
"country"=>"Russia",
|
53
|
+
"country_geoname_id"=>2017370,
|
54
|
+
"country_code"=>"RU",
|
55
|
+
"continent"=>"Europe",
|
56
|
+
"continent_code"=>"EU",
|
57
|
+
"continent_geoname_id"=>6255148,
|
58
|
+
"subdivision"=>"Tambovskaya Oblast'",
|
59
|
+
"subdivision_code"=>"TAM",
|
60
|
+
"postal_code"=>nil,
|
61
|
+
"latitude"=>52.731700000000004,
|
62
|
+
"longitude"=>41.4433,
|
63
|
+
"time_zone"=>"Europe/Moscow"}
|
64
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require "rake/extensiontask"
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'bundler'
|
4
|
+
Bundler::GemHelper.install_tasks
|
5
|
+
|
6
|
+
Rake::ExtensionTask.new "maxmind_geoip2" do |ext|
|
7
|
+
ext.lib_dir = "lib/maxmind_geoip2"
|
8
|
+
end
|
9
|
+
|
10
|
+
Rake::TestTask.new do |t|
|
11
|
+
t.pattern = 'spec/**/*_spec.rb'
|
12
|
+
t.libs.push 'spec'
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "Download free IP to City database"
|
16
|
+
task :download_free_database do
|
17
|
+
file = 'GeoLite2-City.mmdb'
|
18
|
+
if File.exist?(file)
|
19
|
+
puts "File already exists"
|
20
|
+
else
|
21
|
+
puts "Downloading file"
|
22
|
+
`curl http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.mmdb.gz | gzip -d > #{file}`
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
desc "An IRB console with the project loaded"
|
27
|
+
task :console do
|
28
|
+
$: << 'lib'
|
29
|
+
require 'maxmind_geoip2'
|
30
|
+
MaxmindGeoIP2.file('GeoLite2-City.mmdb')
|
31
|
+
require 'irb'
|
32
|
+
require 'irb/completion'
|
33
|
+
ARGV.clear
|
34
|
+
IRB.start
|
35
|
+
end
|
36
|
+
|
37
|
+
task default: [:compile, :test]
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'mkmf'
|
2
|
+
extension_name = 'maxmind_geoip2'
|
3
|
+
|
4
|
+
$LDFLAGS << " #{ENV['LDFLAGS']}"
|
5
|
+
$CFLAGS << " #{ENV['CFLAGS']}"
|
6
|
+
|
7
|
+
RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC']
|
8
|
+
|
9
|
+
if have_header('maxminddb.h') and have_library('maxminddb')
|
10
|
+
create_makefile("#{extension_name}/#{extension_name}")
|
11
|
+
else
|
12
|
+
abort "you must have mmdb library"
|
13
|
+
end
|
14
|
+
|
@@ -0,0 +1,150 @@
|
|
1
|
+
#include "maxminddb.h"
|
2
|
+
|
3
|
+
#include <ruby.h>
|
4
|
+
#include <ruby/encoding.h>
|
5
|
+
|
6
|
+
#include <stdio.h>
|
7
|
+
#include <stdlib.h>
|
8
|
+
#include <string.h>
|
9
|
+
#include <errno.h>
|
10
|
+
|
11
|
+
VALUE mMaxmindGeoIP2 = Qnil;
|
12
|
+
|
13
|
+
const char **lookup_path_parse(char *lookup_path, char *lang)
|
14
|
+
{
|
15
|
+
|
16
|
+
const char **result;
|
17
|
+
if (NULL != lang)
|
18
|
+
result = malloc(sizeof(char *) * (strlen(lookup_path) + strlen(lang) + 1 ));
|
19
|
+
else
|
20
|
+
result = malloc(sizeof(char *) * (strlen(lookup_path) + 1));
|
21
|
+
|
22
|
+
char *token;
|
23
|
+
char *string = strdup(lookup_path);
|
24
|
+
|
25
|
+
token = strtok(string, " ");
|
26
|
+
int i = 0;
|
27
|
+
while (token != NULL) {
|
28
|
+
result[i++] = token;
|
29
|
+
token = strtok(NULL, " ");
|
30
|
+
}
|
31
|
+
if (NULL != lang) {
|
32
|
+
result[i++] = lang;
|
33
|
+
}
|
34
|
+
result[i] = NULL;
|
35
|
+
return result;
|
36
|
+
}
|
37
|
+
|
38
|
+
VALUE locate_by_path(MMDB_lookup_result_s *result, char *lookup_path, char *lang)
|
39
|
+
{
|
40
|
+
VALUE return_value = Qnil;
|
41
|
+
|
42
|
+
MMDB_entry_data_s entry_data;
|
43
|
+
const char **lp = lookup_path_parse(lookup_path, lang);
|
44
|
+
int status = MMDB_aget_value(&result->entry, &entry_data, lp);
|
45
|
+
if (MMDB_SUCCESS == status)
|
46
|
+
{
|
47
|
+
if (entry_data.offset)
|
48
|
+
{
|
49
|
+
if (entry_data.has_data) {
|
50
|
+
if (entry_data.type == MMDB_DATA_TYPE_UTF8_STRING)
|
51
|
+
return_value = rb_enc_str_new(strndup((char *)entry_data.utf8_string, entry_data.data_size), entry_data.data_size, rb_utf8_encoding());
|
52
|
+
if (entry_data.type == MMDB_DATA_TYPE_UINT32)
|
53
|
+
return_value = rb_int_new(entry_data.uint32);
|
54
|
+
if (entry_data.type == MMDB_DATA_TYPE_DOUBLE)
|
55
|
+
return_value = rb_float_new(entry_data.double_value);
|
56
|
+
}
|
57
|
+
}
|
58
|
+
}
|
59
|
+
free(lp);
|
60
|
+
return return_value;
|
61
|
+
}
|
62
|
+
|
63
|
+
VALUE mMaxmindGeoIP2_locate(int argc, VALUE *argv, VALUE self)
|
64
|
+
{
|
65
|
+
VALUE locate_result = Qnil;
|
66
|
+
|
67
|
+
if (argc > 3 || argc == 0) {
|
68
|
+
rb_raise(rb_eArgError, "wrong number of arguments");
|
69
|
+
}
|
70
|
+
|
71
|
+
|
72
|
+
char *lang;
|
73
|
+
if (argc == 2) {
|
74
|
+
Check_Type(argv[1], T_STRING);
|
75
|
+
lang = StringValuePtr(argv[1]);
|
76
|
+
} else {
|
77
|
+
VALUE loc = rb_iv_get(self, "@_locale");
|
78
|
+
Check_Type(loc, T_STRING);
|
79
|
+
lang = StringValuePtr(loc);
|
80
|
+
}
|
81
|
+
|
82
|
+
VALUE ipaddr = argv[0];
|
83
|
+
Check_Type(ipaddr, T_STRING);
|
84
|
+
char *ip_address = StringValuePtr(ipaddr);
|
85
|
+
|
86
|
+
char *filename;
|
87
|
+
VALUE file = rb_iv_get(self, "@_file");
|
88
|
+
Check_Type(file, T_STRING);
|
89
|
+
filename = StringValuePtr(file);
|
90
|
+
|
91
|
+
|
92
|
+
MMDB_s mmdb;
|
93
|
+
int status = MMDB_open(filename, MMDB_MODE_MMAP, &mmdb);
|
94
|
+
if (MMDB_SUCCESS == status)
|
95
|
+
{
|
96
|
+
int gai_error, mmdb_error;
|
97
|
+
MMDB_lookup_result_s result =
|
98
|
+
MMDB_lookup_string(&mmdb, ip_address, &gai_error, &mmdb_error);
|
99
|
+
|
100
|
+
if (result.found_entry)
|
101
|
+
{
|
102
|
+
locate_result = rb_hash_new();
|
103
|
+
rb_hash_aset(locate_result, rb_str_new2("city"), locate_by_path(&result, "city names", lang));
|
104
|
+
rb_hash_aset(locate_result, rb_str_new2("city_geoname_id"), locate_by_path(&result, "city geoname_id", NULL));
|
105
|
+
rb_hash_aset(locate_result, rb_str_new2("country"), locate_by_path(&result, "country names", lang));
|
106
|
+
rb_hash_aset(locate_result, rb_str_new2("country_geoname_id"), locate_by_path(&result, "country geoname_id", NULL));
|
107
|
+
rb_hash_aset(locate_result, rb_str_new2("country_code"), locate_by_path(&result, "country iso_code", NULL));
|
108
|
+
rb_hash_aset(locate_result, rb_str_new2("continent"), locate_by_path(&result, "continent names", lang));
|
109
|
+
rb_hash_aset(locate_result, rb_str_new2("continent_code"), locate_by_path(&result, "continent code", NULL));
|
110
|
+
rb_hash_aset(locate_result, rb_str_new2("continent_geoname_id"), locate_by_path(&result, "continent geoname_id", NULL));
|
111
|
+
rb_hash_aset(locate_result, rb_str_new2("subdivision"), locate_by_path(&result, "subdivisions 0 names", lang));
|
112
|
+
rb_hash_aset(locate_result, rb_str_new2("subdivision_code"), locate_by_path(&result, "subdivisions 0 iso_code", NULL));
|
113
|
+
rb_hash_aset(locate_result, rb_str_new2("subdivision_geoname_id"), locate_by_path(&result, "subdivisions 0 geoname_id", NULL));
|
114
|
+
rb_hash_aset(locate_result, rb_str_new2("postal_code"), locate_by_path(&result, "postal code", NULL));
|
115
|
+
rb_hash_aset(locate_result, rb_str_new2("latitude"), locate_by_path(&result, "location latitude", NULL));
|
116
|
+
rb_hash_aset(locate_result, rb_str_new2("longitude"), locate_by_path(&result, "location longitude", NULL));
|
117
|
+
rb_hash_aset(locate_result, rb_str_new2("time_zone"), locate_by_path(&result, "location time_zone", NULL));
|
118
|
+
}
|
119
|
+
MMDB_close(&mmdb);
|
120
|
+
} else {
|
121
|
+
rb_raise(rb_eIOError, "unable to open file %s", filename);
|
122
|
+
}
|
123
|
+
return locate_result;
|
124
|
+
}
|
125
|
+
|
126
|
+
VALUE mMaxmindGeoIP2_file(VALUE self, VALUE filepath)
|
127
|
+
{
|
128
|
+
rb_iv_set(self, "@_file", filepath);
|
129
|
+
return Qtrue;
|
130
|
+
}
|
131
|
+
|
132
|
+
VALUE mMaxmindGeoIP2_locale(VALUE self, VALUE language)
|
133
|
+
{
|
134
|
+
rb_iv_set(self, "@_locale", language);
|
135
|
+
return Qtrue;
|
136
|
+
}
|
137
|
+
|
138
|
+
void Init_maxmind_geoip2()
|
139
|
+
{
|
140
|
+
mMaxmindGeoIP2 = rb_define_module("MaxmindGeoIP2");
|
141
|
+
rb_define_module_function(mMaxmindGeoIP2, "locate", mMaxmindGeoIP2_locate, -1);
|
142
|
+
rb_define_module_function(mMaxmindGeoIP2, "file", mMaxmindGeoIP2_file, 1);
|
143
|
+
rb_define_module_function(mMaxmindGeoIP2, "locale", mMaxmindGeoIP2_locale, 1);
|
144
|
+
|
145
|
+
rb_define_attr(mMaxmindGeoIP2, "_file", 1, 1);
|
146
|
+
rb_define_attr(mMaxmindGeoIP2, "_locale", 1, 1);
|
147
|
+
|
148
|
+
rb_iv_set(mMaxmindGeoIP2, "@_file", rb_str_new2("GeoLite2-City.mmdb"));
|
149
|
+
rb_iv_set(mMaxmindGeoIP2, "@_locale", rb_str_new2("ru"));
|
150
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'maxmind_geoip2/maxmind_geoip2'
|
@@ -0,0 +1,20 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'maxmind_geoip2'
|
3
|
+
s.version = "0.0.8"
|
4
|
+
|
5
|
+
s.licenses = ['WTFPL']
|
6
|
+
|
7
|
+
s.authors = ['Maksim Stepanov']
|
8
|
+
s.email = ['stepanov.ms@mail.ru']
|
9
|
+
|
10
|
+
s.summary = "A Binding to the GeoIP2 C library"
|
11
|
+
s.description = 'Generic GeoIP2 lookup tool.'
|
12
|
+
s.homepage = "http://github.com"
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.extensions = ['ext/maxmind_geoip2/extconf.rb']
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
|
18
|
+
s.add_development_dependency 'rake', '~>10.0'
|
19
|
+
s.add_development_dependency 'rake-compiler'
|
20
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MaxmindGeoIP2 do
|
4
|
+
describe '.locate' do
|
5
|
+
let(:result) { MaxmindGeoIP2.locate('74.125.237.168') }
|
6
|
+
|
7
|
+
it 'sets string encodings to utf8' do
|
8
|
+
result['country'].encoding.must_equal(Encoding::UTF_8)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'has a continent' do
|
12
|
+
result['continent'].must_be_instance_of(String)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'has a continent_code' do
|
16
|
+
result['continent_code'].must_be_instance_of(String)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'has a continent_geoname_id' do
|
20
|
+
result['continent_geoname_id'].must_be_instance_of(Fixnum)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'has the correct continent_geoname_id' do
|
24
|
+
result['continent_geoname_id'].must_equal(6255149)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'has a country' do
|
28
|
+
result['country'].must_be_instance_of(String)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'has a country_code' do
|
32
|
+
result['country_code'].must_be_instance_of(String)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'has a country_geoname_id' do
|
36
|
+
result['country_geoname_id'].must_be_instance_of(Fixnum)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'has a city' do
|
40
|
+
result['city'].must_be_instance_of(String)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'has a city_geoname_id' do
|
44
|
+
result['city_geoname_id'].must_be_instance_of(Fixnum)
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'has a subdivision' do
|
48
|
+
result['subdivision'].must_be_instance_of(String)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'has a subdivision_code' do
|
52
|
+
result['subdivision_code'].must_be_instance_of(String)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'has a subdivision_geoname_id' do
|
56
|
+
result['subdivision_geoname_id'].must_be_instance_of(Fixnum)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'has a postal_code' do
|
60
|
+
result['postal_code'].must_be_instance_of(String)
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'has a latitude' do
|
64
|
+
result['latitude'].must_be_instance_of(Float)
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'has a longitude' do
|
68
|
+
result['longitude'].must_be_instance_of(Float)
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'has a time_zone' do
|
72
|
+
result['time_zone'].must_be_instance_of(String)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
|
4
|
+
require 'maxmind_geoip2'
|
5
|
+
|
6
|
+
unless File.exist?('GeoLite2-City.mmdb')
|
7
|
+
raise "GeoLite2-City.mmdb doesn't exist. Run rake download_free_database to get it"
|
8
|
+
end
|
9
|
+
|
10
|
+
MaxmindGeoIP2.file('GeoLite2-City.mmdb')
|
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: maxmind_geoip2
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.8
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Maksim Stepanov
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-04-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '10.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '10.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake-compiler
|
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
|
+
description: Generic GeoIP2 lookup tool.
|
42
|
+
email:
|
43
|
+
- stepanov.ms@mail.ru
|
44
|
+
executables: []
|
45
|
+
extensions:
|
46
|
+
- ext/maxmind_geoip2/extconf.rb
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- ".gitignore"
|
50
|
+
- Gemfile
|
51
|
+
- README.md
|
52
|
+
- Rakefile
|
53
|
+
- ext/maxmind_geoip2/extconf.rb
|
54
|
+
- ext/maxmind_geoip2/maxmind_geoip2.c
|
55
|
+
- lib/maxmind_geoip2.rb
|
56
|
+
- maxmind_geoip2.gemspec
|
57
|
+
- spec/maxmind_geoip2_spec.rb
|
58
|
+
- spec/spec_helper.rb
|
59
|
+
homepage: http://github.com
|
60
|
+
licenses:
|
61
|
+
- WTFPL
|
62
|
+
metadata: {}
|
63
|
+
post_install_message:
|
64
|
+
rdoc_options: []
|
65
|
+
require_paths:
|
66
|
+
- lib
|
67
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
requirements: []
|
78
|
+
rubyforge_project:
|
79
|
+
rubygems_version: 2.4.3
|
80
|
+
signing_key:
|
81
|
+
specification_version: 4
|
82
|
+
summary: A Binding to the GeoIP2 C library
|
83
|
+
test_files: []
|