geolocal 0.6.2 → 0.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +40 -18
- data/config/geolocal.rb +12 -0
- data/contrib/array-vs-range-benchmark.rb +23 -0
- data/contrib/continents.rb +20 -0
- data/lib/generators/geolocal/USAGE +8 -0
- data/lib/generators/geolocal/geolocal_generator.rb +7 -0
- data/lib/generators/geolocal/templates/geolocal.rb +17 -0
- data/lib/geolocal/provider/base.rb +53 -17
- data/lib/geolocal/provider/db_ip.rb +9 -0
- data/lib/geolocal/version.rb +1 -1
- data/lib/tasks/geolocal.rake +11 -1
- data/spec/{configuration_spec.rb → geolocal/configuration_spec.rb} +0 -0
- data/spec/geolocal/provider/base_spec.rb +181 -0
- data/spec/{provider → geolocal/provider}/db_ip_spec.rb +29 -29
- metadata +13 -8
- data/spec/provider/base_spec.rb +0 -86
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9967a2d045173bb94fd0a2099f9751d6c485d5e2
|
4
|
+
data.tar.gz: 68ccf53dc8987083ea502b65d27742c6f23e29c2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 95eb28b17e6676f41ea1684eb156ae562bd56a8d91dca3368c9a43f21f5c039e4969722a5f5830439406436de5ba32ada049f91b79f284645f1810c82a013dad
|
7
|
+
data.tar.gz: 218051f783714e1cdfdf988f3e6e76da636fcc3d4c0b71f4016ed5932e409ea1b246698e0baf13bab07ba5b7235c0333540bece81d149aa4515b3fdf20780626
|
data/README.md
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# Geolocal
|
2
2
|
|
3
3
|
Allows IP addresses to geocoded with a single Ruby if statement.
|
4
|
-
No network access, no context switches, no delay. Just one low-calorie
|
5
|
-
|
4
|
+
No network access, no context switches, no delay. Just one low-calorie lookup:
|
5
|
+
`Geolocal.in_spain?(request.remote_ip)`
|
6
6
|
|
7
7
|
## Installation
|
8
8
|
|
9
|
-
|
9
|
+
Add this line to your Gemfile:
|
10
10
|
|
11
11
|
```ruby
|
12
12
|
gem 'geolocal'
|
@@ -15,38 +15,47 @@ gem 'geolocal'
|
|
15
15
|
|
16
16
|
## Usage
|
17
17
|
|
18
|
-
|
18
|
+
If you're using Rails, run `rails generate geolocal` to create the configuration file.
|
19
|
+
Otherwise, crib from [config/geolocal.rb](https://github.com/bronson/geolocal/tree/master/config/geolocal.rb).
|
20
|
+
|
21
|
+
The config file describes the ranges you're interested in.
|
19
22
|
Here's an example:
|
20
23
|
|
21
24
|
```ruby
|
22
25
|
Geolocal.configure do
|
23
26
|
config.countries = {
|
24
27
|
us: 'US',
|
28
|
+
spain: 'ES',
|
25
29
|
central_america: %w[ BZ CR SV GT HN NI PA ]
|
26
30
|
}
|
27
31
|
end
|
28
32
|
```
|
29
33
|
|
30
|
-
Now run `rake geolocal:update`.
|
34
|
+
Now run `rake geolocal:update`. Geolocal downloads the geocoding data
|
31
35
|
from the default provider (see the [providers](#providers) section) and
|
32
|
-
|
36
|
+
creates the desired methods:
|
33
37
|
|
34
38
|
```ruby
|
35
39
|
Geolocal.in_us?(request.remote_ip)
|
36
|
-
Geolocal.
|
40
|
+
Geolocal.in_spain?('2a05:af06::') # optional IPv6 support
|
41
|
+
Geolocal.in_central_america?('200.16.66.0')
|
37
42
|
```
|
38
43
|
|
39
|
-
|
44
|
+
#### The in\_*area*? method
|
45
|
+
|
46
|
+
The `rake geolocal:update` task generates the Ruby file defining these methods. You can pass:
|
40
47
|
* a string: `Geolocal.in_us?("10.1.2.3")`
|
41
48
|
* an [IPAddr](http://www.ruby-doc.org/stdlib-2.2.0/libdoc/ipaddr/rdoc/IPAddr.html) object:
|
42
49
|
`Geolocal.in_eu?(IPAddr.new('2.16.54.0'))`
|
43
|
-
* an integer/family combo: `Geolocal.
|
50
|
+
* an integer/family combo: `Geolocal.in_asia?(167838211, Socket::AF_INET)`
|
51
|
+
|
52
|
+
It returns true if the IP address is in the area, false if not.
|
44
53
|
|
45
54
|
## Config
|
46
55
|
|
47
56
|
Here are the supported configuration options:
|
48
57
|
|
49
|
-
* **provider**: Where to download the geocoding data. Default: DB_IP.
|
58
|
+
* **provider**: Where to download the geocoding data. See [Providers](#providers) below. Default: DB_IP.
|
50
59
|
* **module**: The name of the module to receive the `in_*` methods. Default: 'Geolocal'.
|
51
60
|
* **file**: Path to the file to contain the generated code. Default: `lib/#{module}.rb`.
|
52
61
|
* **tmpdir**: the directory to contain intermediate files. They will require tens of megabytes
|
@@ -57,8 +66,20 @@ Here are the supported configuration options:
|
|
57
66
|
|
58
67
|
## Examples
|
59
68
|
|
60
|
-
|
61
|
-
|
69
|
+
There are some examples in the [contrib](https://github.com/bronson/geolocal/tree/master/contrib) directory.
|
70
|
+
Run them like this:
|
71
|
+
|
72
|
+
```sh
|
73
|
+
git clone https://github.com/bronson/geolocal
|
74
|
+
cd geolocal
|
75
|
+
rake geolocal config=contrib/continents.rb
|
76
|
+
```
|
77
|
+
|
78
|
+
|
79
|
+
#### in_eu?
|
80
|
+
|
81
|
+
It's easy to use the [Countries](https://github.com/hexorx/countries) gem
|
82
|
+
to create a `Geolocal.in_eu?(ip_addr)` method:
|
62
83
|
|
63
84
|
```ruby
|
64
85
|
require 'countries'
|
@@ -70,13 +91,15 @@ Geolocal.configure do |config|
|
|
70
91
|
end
|
71
92
|
```
|
72
93
|
|
73
|
-
|
74
|
-
|
94
|
+
If European Union membership ever changes, just run `bundle update countries`
|
95
|
+
and `rake geolocal` to bring your app back up to date.
|
96
|
+
|
97
|
+
|
75
98
|
|
76
99
|
## Providers
|
77
100
|
|
78
|
-
This gem currently only supoports the [DB-IP](https://db-ip.com/about/) database.
|
79
|
-
There are lots of other databases available and this gem is organized to support them.
|
101
|
+
This gem currently only supoports the [DB-IP](https://db-ip.com/about/) Countries database.
|
102
|
+
There are lots of other databases available and this gem is organized to support them one day.
|
80
103
|
Patches welcome.
|
81
104
|
|
82
105
|
|
@@ -91,8 +114,7 @@ environments like Heroku.
|
|
91
114
|
|
92
115
|
## TODO
|
93
116
|
|
94
|
-
- [ ]
|
95
|
-
- [ ] performance information? benchmarks. space saving by going ipv4-only?
|
117
|
+
- [ ] performance information / benchmarks?
|
96
118
|
- [ ] Add support for cities
|
97
119
|
- [ ] other sources for this data? [MainFacts](http://mainfacts.com/ip-address-space-addresses), [NirSoft](http://www.nirsoft.net/countryip/)
|
98
120
|
Also maybe allow providers to accept their own options?
|
data/config/geolocal.rb
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
require 'geolocal'
|
2
2
|
|
3
3
|
Geolocal.configure do |config|
|
4
|
+
# This example creates a Geolocal.in_us?(addr) command:
|
4
5
|
config.countries = { us: 'US' }
|
6
|
+
|
7
|
+
# This is where the output goes. You should commit it to your project.
|
8
|
+
# config.module = 'Geolocal'
|
9
|
+
# config.file = 'lib/geolocal.rb'
|
10
|
+
|
11
|
+
# If your host is IPv4 only, you can save space by omitting IPv6 data and vice versa.
|
12
|
+
# config.ipv6 = true
|
13
|
+
# config.ipv4 = true
|
14
|
+
|
15
|
+
# Download and process the geocoding data in this directory. Don't commit it.
|
16
|
+
# config.tmpdir = './tmp/geolocal'
|
5
17
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
|
3
|
+
# This shows that Ranges are about twice as fast as 2-element Arrays
|
4
|
+
# Unless the range is (1.to_i..2.to_i), which is identical to Arrays
|
5
|
+
|
6
|
+
N = 10_000_000
|
7
|
+
|
8
|
+
$a = []
|
9
|
+
$r = []
|
10
|
+
|
11
|
+
def array
|
12
|
+
$a << [1,2]
|
13
|
+
end
|
14
|
+
|
15
|
+
def range
|
16
|
+
$r << (1..2)
|
17
|
+
end
|
18
|
+
|
19
|
+
Benchmark.bm(15, 'array/range') do |x|
|
20
|
+
array_report = x.report('array:') { N.times { array } }
|
21
|
+
range_report = x.report('range:') { N.times { range } }
|
22
|
+
[array_report / range_report]
|
23
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'geolocal'
|
2
|
+
require 'countries'
|
3
|
+
|
4
|
+
|
5
|
+
# creates in_x? methods for each continent: in_north_america?, in_europe?, etc.
|
6
|
+
|
7
|
+
Geolocal.configure do |config|
|
8
|
+
all_countries = Country.all.map { |c| Country[c[1]] }
|
9
|
+
|
10
|
+
# { 'antarctica' => ['AQ', 'BV', ...], 'australia' => ['AS', 'AU', ...], ... }
|
11
|
+
by_continent = all_countries.reduce({}) { |hash,country|
|
12
|
+
continent = country.continent.downcase;
|
13
|
+
hash[continent] ||= [];
|
14
|
+
hash[continent] << country.alpha2;
|
15
|
+
hash
|
16
|
+
}
|
17
|
+
|
18
|
+
config.countries = by_continent
|
19
|
+
config.ipv6 = false
|
20
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'geolocal'
|
2
|
+
|
3
|
+
Geolocal.configure do |config|
|
4
|
+
# This example creates a Geolocal.in_us?(addr) command:
|
5
|
+
config.countries = { us: 'US' }
|
6
|
+
|
7
|
+
# This is where the output goes. You should commit it to your project.
|
8
|
+
# config.module = 'Geolocal'
|
9
|
+
# config.file = 'lib/geolocal.rb'
|
10
|
+
|
11
|
+
# If your host is IPv4 only, you can save space by omitting IPv6 data and vice versa.
|
12
|
+
# config.ipv6 = true
|
13
|
+
# config.ipv4 = true
|
14
|
+
|
15
|
+
# Download and process the geocoding data in this directory. Don't commit it.
|
16
|
+
# config.tmpdir = './tmp/geolocal'
|
17
|
+
end
|
@@ -21,29 +21,42 @@ module Geolocal
|
|
21
21
|
hiaddr = IPAddr.new(histr)
|
22
22
|
lofam = loaddr.family
|
23
23
|
hifam = hiaddr.family
|
24
|
-
|
24
|
+
if lofam != hifam
|
25
|
+
raise "#{lostr} and #{histr} must be in the same address family"
|
26
|
+
end
|
27
|
+
|
28
|
+
loval = loaddr.to_i
|
29
|
+
hival = hiaddr.to_i
|
30
|
+
if loval > hival
|
31
|
+
raise "range supplied in the wrong order: #{lostr}..#{histr}"
|
32
|
+
end
|
25
33
|
|
26
34
|
if lofam == Socket::AF_INET
|
27
|
-
namefam = name + 'v4' if config[:ipv4]
|
35
|
+
namefam = name.upcase + 'v4' if config[:ipv4]
|
28
36
|
elsif lofam == Socket::AF_INET6
|
29
|
-
namefam = name + 'v6' if config[:ipv6]
|
37
|
+
namefam = name.upcase + 'v6' if config[:ipv6]
|
30
38
|
else
|
31
|
-
raise "unknown family #{lofam} for #{lostr}"
|
39
|
+
raise "unknown address family #{lofam} for #{lostr}"
|
32
40
|
end
|
33
41
|
|
34
42
|
if namefam
|
35
|
-
results[namefam] <<
|
43
|
+
results[namefam] << (loaddr.to_i..hiaddr.to_i)
|
36
44
|
end
|
45
|
+
namefam
|
37
46
|
end
|
38
47
|
|
39
|
-
def
|
40
|
-
countries
|
41
|
-
|
48
|
+
def countries
|
49
|
+
@countries ||= config[:countries].sort_by { |k, v| k }.reduce({}) { |a, (k, v)|
|
50
|
+
k = k.to_s.gsub(/[ -]/, '_')
|
51
|
+
raise "invalid identifier: '#{k}'" if k =~ /^[^A-Za-z_]|[^A-Za-z0-9_]|^\s*$/
|
52
|
+
a.merge! k.to_s.downcase => Array(v).map { |c| c.to_s.upcase }.sort.to_set
|
42
53
|
}
|
54
|
+
end
|
43
55
|
|
56
|
+
def update
|
44
57
|
results = countries.keys.reduce({}) { |a, k|
|
45
|
-
a.merge! k.upcase+'v4' =>
|
46
|
-
a.merge! k.upcase+'v6' =>
|
58
|
+
a.merge! k.upcase+'v4' => [] if config[:ipv4]
|
59
|
+
a.merge! k.upcase+'v6' => [] if config[:ipv6]
|
47
60
|
a
|
48
61
|
}
|
49
62
|
|
@@ -70,7 +83,7 @@ module Geolocal
|
|
70
83
|
|
71
84
|
write_header file, modname
|
72
85
|
|
73
|
-
|
86
|
+
countries.keys.each do |name|
|
74
87
|
v4mod = config[:ipv4] ? name.to_s.upcase + 'v4' : 'nil'
|
75
88
|
v6mod = config[:ipv6] ? name.to_s.upcase + 'v6' : 'nil'
|
76
89
|
write_method file, name, v4mod, v6mod
|
@@ -78,21 +91,43 @@ module Geolocal
|
|
78
91
|
file.write "end\n\n"
|
79
92
|
|
80
93
|
status " writing "
|
81
|
-
results.each do |name,
|
82
|
-
|
83
|
-
|
94
|
+
results.each do |name, ranges|
|
95
|
+
coalesced = coalesce_ranges(ranges)
|
96
|
+
status "#{name}-#{ranges.length - coalesced.length} "
|
97
|
+
write_ranges file, modname, name, coalesced
|
84
98
|
end
|
85
99
|
status "\n"
|
86
100
|
end
|
87
101
|
|
88
102
|
|
103
|
+
def coalesce_ranges ranges
|
104
|
+
ranges = ranges.sort_by { |r| r.min }
|
105
|
+
|
106
|
+
uniques = []
|
107
|
+
lastr = ranges.shift
|
108
|
+
uniques << lastr if lastr
|
109
|
+
|
110
|
+
ranges.each do |thisr|
|
111
|
+
if lastr.last >= thisr.first - 1
|
112
|
+
lastr = lastr.first..[thisr.last, lastr.last].max
|
113
|
+
uniques[-1] = lastr
|
114
|
+
else
|
115
|
+
lastr = thisr
|
116
|
+
uniques << lastr
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
uniques
|
121
|
+
end
|
122
|
+
|
123
|
+
|
89
124
|
def write_header file, modname
|
90
125
|
file.write <<EOL
|
91
126
|
# This file is autogenerated by the Geolocal gem
|
92
127
|
|
93
128
|
module #{modname}
|
94
129
|
|
95
|
-
def self.search address, family
|
130
|
+
def self.search address, family, v4module, v6module
|
96
131
|
address = IPAddr.new(address) if address.is_a?(String)
|
97
132
|
family = address.family unless family
|
98
133
|
num = address.to_i
|
@@ -117,10 +152,11 @@ EOL
|
|
117
152
|
EOL
|
118
153
|
end
|
119
154
|
|
120
|
-
def write_ranges file, modname, name,
|
155
|
+
def write_ranges file, modname, name, ranges
|
121
156
|
file.write <<EOL
|
122
157
|
#{modname}::#{name} = [
|
123
|
-
#{
|
158
|
+
#{ranges.join(",\n")}
|
159
|
+
]
|
124
160
|
|
125
161
|
EOL
|
126
162
|
end
|
@@ -68,6 +68,14 @@ class Geolocal::Provider::DB_IP < Geolocal::Provider::Base
|
|
68
68
|
"#{(@current_byte/1024/elapsed).round(1)} KB/sec\n"
|
69
69
|
end
|
70
70
|
|
71
|
+
# a bit of debugging code to print all non-matched country codes. should be deleted one day.
|
72
|
+
def check_country_codes(countries, row)
|
73
|
+
@known_codes ||= countries.reduce(Set.new) { |a,(_,v)| a.merge v; a }
|
74
|
+
unless @known_codes.include?(row[2])
|
75
|
+
puts "#{row[2]}: #{row[0]}..#{row[1]}"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
71
79
|
def read_ranges countries
|
72
80
|
status "computing ranges\n"
|
73
81
|
|
@@ -84,6 +92,7 @@ class Geolocal::Provider::DB_IP < Geolocal::Provider::Base
|
|
84
92
|
match_count += 1
|
85
93
|
yield name, row[0], row[1]
|
86
94
|
end
|
95
|
+
# check_country_codes(countries, row)
|
87
96
|
end
|
88
97
|
end
|
89
98
|
end
|
data/lib/geolocal/version.rb
CHANGED
data/lib/tasks/geolocal.rake
CHANGED
@@ -1,4 +1,14 @@
|
|
1
|
-
|
1
|
+
# Provides geolocal's rake tasks.
|
2
|
+
|
3
|
+
# we use this file by default
|
4
|
+
config='config/geolocal'
|
5
|
+
|
6
|
+
# but you can specify the config file on the command line:
|
7
|
+
# `rake geolocal config=contrib/continents`
|
8
|
+
config=ENV['config'] if ENV['config']
|
9
|
+
puts "loading geolocal configuration from #{config}"
|
10
|
+
require './'+config
|
11
|
+
|
2
12
|
|
3
13
|
namespace :geolocal do
|
4
14
|
desc "Downloads the most recent geocoding information"
|
File without changes
|
@@ -0,0 +1,181 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'geolocal/provider/base'
|
3
|
+
|
4
|
+
|
5
|
+
describe Geolocal::Provider::Base do
|
6
|
+
let(:it) { described_class }
|
7
|
+
let(:provider) { it.new }
|
8
|
+
|
9
|
+
|
10
|
+
describe '#add_to_results' do
|
11
|
+
it 'adds a v4 address to the results' do
|
12
|
+
result = { 'USv4'=>[] }
|
13
|
+
expect(
|
14
|
+
provider.add_to_results(result, 'US', '192.168.1.3', '192.168.1.4')
|
15
|
+
).to eq 'USv4'
|
16
|
+
expect(result).to eq({ "USv4" => [3232235779..3232235780] })
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'adds a v6 address to the results' do
|
20
|
+
result = { 'USv6'=>[] }
|
21
|
+
expect(
|
22
|
+
provider.add_to_results(result, 'US', '2001:400::', '2001:404::')
|
23
|
+
).to eq 'USv6'
|
24
|
+
# wow, rspec bug? this expectation causes rspec to enter an infinite loop
|
25
|
+
# expect(result).to eq({ "USv6" => ["3232235779..3232235780,\n"] })
|
26
|
+
expect(result).to eq({ "USv6" =>
|
27
|
+
[42540569291614257367232052214305390592..42540569608526907424289402588481191936] })
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'complains if given a bad range' do
|
31
|
+
expect {
|
32
|
+
provider.add_to_results({}, 'US', '192.168.1.5', '192.168.1.4')
|
33
|
+
}.to raise_error(/wrong order: 192.168.1.5\.\.192.168.1.4/)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'complains if given an illegal address' do
|
37
|
+
expect {
|
38
|
+
provider.add_to_results({}, 'US', 'dog', 'cat')
|
39
|
+
}.to raise_error(/invalid address/)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'complains if given non-matching addresses' do
|
43
|
+
expect {
|
44
|
+
provider.add_to_results({}, 'US', '192.168.1.5', '2001:400::')
|
45
|
+
}.to raise_error(/must be in the same address family/)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
describe '#countries' do
|
51
|
+
it 'preprocesses the countries' do
|
52
|
+
Geolocal.configure do |config|
|
53
|
+
config.countries = { ua: :UA, na: ['MX', 'CA', :US] }
|
54
|
+
end
|
55
|
+
|
56
|
+
expect(provider.countries).to eq({ 'na' => Set['CA', 'MX', 'US'], 'ua' => Set['UA'] })
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'preprocesses countries with an odd name' do
|
60
|
+
Geolocal.configure do |config|
|
61
|
+
config.countries = { 'U s-a': 'US', 'Mex': 'MX' }
|
62
|
+
end
|
63
|
+
|
64
|
+
expect(provider.countries).to eq({ 'mex' => Set['MX'], 'u_s_a' => Set['US'] })
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'refuses invalid identifiers' do
|
68
|
+
Geolocal.configure do |config|
|
69
|
+
config.countries = { '1nation': 'US' }
|
70
|
+
end
|
71
|
+
|
72
|
+
expect { provider.countries }.to raise_error(/invalid identifier/)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
describe '#coalesce_ranges' do
|
78
|
+
it 'can coalesce some ranges' do
|
79
|
+
expect(provider.coalesce_ranges([1..2, 3..4])).to eq [1..4]
|
80
|
+
expect(provider.coalesce_ranges([8..9, 6..7])).to eq [6..9]
|
81
|
+
expect(provider.coalesce_ranges([1..2, 4..5])).to eq [1..2, 4..5]
|
82
|
+
expect(provider.coalesce_ranges([7..9, 0..2])).to eq [0..2, 7..9]
|
83
|
+
expect(provider.coalesce_ranges([1..1, 2..2])).to eq [1..2]
|
84
|
+
expect(provider.coalesce_ranges([3..3, 1..1])).to eq [1..1, 3..3]
|
85
|
+
expect(provider.coalesce_ranges([1..2, 3..4, 2..6, 6..8, 8..9])).to eq [1..9]
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'can coalesce some oddball cases' do
|
89
|
+
expect(provider.coalesce_ranges([])).to eq []
|
90
|
+
expect(provider.coalesce_ranges([1..1])).to eq [1..1]
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'can coalesce some questionable ranges' do
|
94
|
+
expect(provider.coalesce_ranges([1..2, 2..4])).to eq [1..4]
|
95
|
+
expect(provider.coalesce_ranges([1..8, 2..9])).to eq [1..9]
|
96
|
+
expect(provider.coalesce_ranges([2..4, 1..2])).to eq [1..4]
|
97
|
+
expect(provider.coalesce_ranges([2..9, 1..8])).to eq [1..9]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
describe 'generating' do
|
103
|
+
let(:example_results) { {
|
104
|
+
'USv4' => [0..16777215, 34603520..34604031, 34605568..34606591],
|
105
|
+
'USv6' => [0..42540528726795050063891204319802818559,
|
106
|
+
42540569291614257367232052214305390592..42540570559264857595461453711008595967]
|
107
|
+
} }
|
108
|
+
|
109
|
+
before do
|
110
|
+
Geolocal.configure do |config|
|
111
|
+
config.countries = { 'US': 'US' }
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'can generate both ipv4 and ipv6' do
|
116
|
+
Geolocal.configure do |config|
|
117
|
+
config.module = 'Geolocal_v4v6'
|
118
|
+
end
|
119
|
+
|
120
|
+
io = StringIO.new
|
121
|
+
provider.output io, example_results
|
122
|
+
expect(io.string).to include '0..42540528726795050063891204319802818559'
|
123
|
+
expect(io.string).to include '34605568..34606591'
|
124
|
+
|
125
|
+
eval io.string
|
126
|
+
expect(Geolocal_v4v6.in_us? '2.16.13.255').to eq true
|
127
|
+
expect(Geolocal_v4v6.in_us? IPAddr.new('2.16.13.255')).to eq true
|
128
|
+
expect(Geolocal_v4v6.in_us? 34606591, Socket::AF_INET).to eq true
|
129
|
+
expect(Geolocal_v4v6.in_us? 34606592, Socket::AF_INET).to eq nil
|
130
|
+
|
131
|
+
expect(Geolocal_v4v6.in_us? '2001:400::').to eq true
|
132
|
+
expect(Geolocal_v4v6.in_us? IPAddr.new('2001:400::')).to eq true
|
133
|
+
expect(Geolocal_v4v6.in_us? 42540570559264857595461453711008595967, Socket::AF_INET6).to eq true
|
134
|
+
expect(Geolocal_v4v6.in_us? 42540570559264857595461453711008595968, Socket::AF_INET6).to eq nil
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'can turn off ipv4' do
|
138
|
+
Geolocal.configure do |config|
|
139
|
+
config.ipv4 = false
|
140
|
+
config.module = 'Geolocal_v6'
|
141
|
+
end
|
142
|
+
|
143
|
+
io = StringIO.new
|
144
|
+
provider.output io, example_results.tap { |h| h.delete('USv4') }
|
145
|
+
expect(io.string).to include '0..42540528726795050063891204319802818559'
|
146
|
+
expect(io.string).not_to include '34605568..34606591'
|
147
|
+
|
148
|
+
eval io.string
|
149
|
+
expect{ Geolocal_v6.in_us? '2.16.13.255' }.to raise_error(/ipv4 was not compiled in/)
|
150
|
+
expect( Geolocal_v6.in_us? '2001:400::' ).to eq true
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'can turn off ipv6' do
|
154
|
+
Geolocal.configure do |config|
|
155
|
+
config.ipv6 = false
|
156
|
+
config.module = 'Geolocal_v4'
|
157
|
+
end
|
158
|
+
|
159
|
+
io = StringIO.new
|
160
|
+
provider.output io, example_results.tap { |h| h.delete('USv6') }
|
161
|
+
expect(io.string).to include '34605568..34606591'
|
162
|
+
expect(io.string).not_to include '0..42540528726795050063891204319802818559'
|
163
|
+
|
164
|
+
eval io.string
|
165
|
+
expect{ Geolocal_v4.in_us? '2001:400::' }.to raise_error(/ipv6 was not compiled in/)
|
166
|
+
expect( Geolocal_v4.in_us? '2.16.13.255' ).to eq true
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'can turn off both ipv4 and ipv6' do
|
170
|
+
Geolocal.configure do |config|
|
171
|
+
config.ipv4 = false
|
172
|
+
config.ipv6 = false
|
173
|
+
end
|
174
|
+
|
175
|
+
io = StringIO.new
|
176
|
+
provider.output io, example_results.tap { |h| h.delete('USv4'); h.delete('USv6') }
|
177
|
+
expect(io.string).not_to include '34605568..34606591'
|
178
|
+
expect(io.string).not_to include '0..42540528726795050063891204319802818559'
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
@@ -61,7 +61,7 @@ describe Geolocal::Provider::DB_IP do
|
|
61
61
|
|
62
62
|
module Geolocal
|
63
63
|
|
64
|
-
def self.search address, family
|
64
|
+
def self.search address, family, v4module, v6module
|
65
65
|
address = IPAddr.new(address) if address.is_a?(String)
|
66
66
|
family = address.family unless family
|
67
67
|
num = address.to_i
|
@@ -98,30 +98,30 @@ EOL
|
|
98
98
|
|
99
99
|
it 'can generate countries from a csv' do
|
100
100
|
example_output = <<EOL
|
101
|
-
def self.in_us? address, family=nil
|
102
|
-
search address, family, USv4, USv6
|
103
|
-
end
|
104
|
-
|
105
101
|
def self.in_au? address, family=nil
|
106
102
|
search address, family, AUv4, AUv6
|
107
103
|
end
|
108
104
|
|
105
|
+
def self.in_us? address, family=nil
|
106
|
+
search address, family, USv4, USv6
|
107
|
+
end
|
108
|
+
|
109
109
|
end
|
110
110
|
|
111
|
-
Geolocal::
|
112
|
-
|
111
|
+
Geolocal::AUv4 = [
|
112
|
+
16777216..16777471
|
113
113
|
]
|
114
114
|
|
115
|
-
Geolocal::
|
116
|
-
|
115
|
+
Geolocal::AUv6 = [
|
116
|
+
55832834671488781931518904937387917312..55832834671488781949965649011097468927
|
117
117
|
]
|
118
118
|
|
119
|
-
Geolocal::
|
120
|
-
|
119
|
+
Geolocal::USv4 = [
|
120
|
+
0..16777215
|
121
121
|
]
|
122
122
|
|
123
|
-
Geolocal::
|
124
|
-
|
123
|
+
Geolocal::USv6 = [
|
124
|
+
55854460156896106951106838613354086400..55854460790721407065221539361705689087
|
125
125
|
]
|
126
126
|
|
127
127
|
EOL
|
@@ -133,22 +133,22 @@ EOL
|
|
133
133
|
|
134
134
|
it 'can generate countries from a csv when ipv6 is turned off' do
|
135
135
|
example_output = <<EOL
|
136
|
-
def self.in_us? address, family=nil
|
137
|
-
search address, family, USv4, nil
|
138
|
-
end
|
139
|
-
|
140
136
|
def self.in_au? address, family=nil
|
141
137
|
search address, family, AUv4, nil
|
142
138
|
end
|
143
139
|
|
140
|
+
def self.in_us? address, family=nil
|
141
|
+
search address, family, USv4, nil
|
142
|
+
end
|
143
|
+
|
144
144
|
end
|
145
145
|
|
146
|
-
Geolocal::
|
147
|
-
|
146
|
+
Geolocal::AUv4 = [
|
147
|
+
16777216..16777471
|
148
148
|
]
|
149
149
|
|
150
|
-
Geolocal::
|
151
|
-
|
150
|
+
Geolocal::USv4 = [
|
151
|
+
0..16777215
|
152
152
|
]
|
153
153
|
|
154
154
|
EOL
|
@@ -161,22 +161,22 @@ EOL
|
|
161
161
|
|
162
162
|
it 'can generate countries from a csv when ipv4 is turned off' do
|
163
163
|
example_output = <<EOL
|
164
|
-
def self.in_us? address, family=nil
|
165
|
-
search address, family, nil, USv6
|
166
|
-
end
|
167
|
-
|
168
164
|
def self.in_au? address, family=nil
|
169
165
|
search address, family, nil, AUv6
|
170
166
|
end
|
171
167
|
|
168
|
+
def self.in_us? address, family=nil
|
169
|
+
search address, family, nil, USv6
|
170
|
+
end
|
171
|
+
|
172
172
|
end
|
173
173
|
|
174
|
-
Geolocal::
|
175
|
-
|
174
|
+
Geolocal::AUv6 = [
|
175
|
+
55832834671488781931518904937387917312..55832834671488781949965649011097468927
|
176
176
|
]
|
177
177
|
|
178
|
-
Geolocal::
|
179
|
-
|
178
|
+
Geolocal::USv6 = [
|
179
|
+
55854460156896106951106838613354086400..55854460790721407065221539361705689087
|
180
180
|
]
|
181
181
|
|
182
182
|
EOL
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: geolocal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: '0.8'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Scott Bronson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-02-
|
11
|
+
date: 2015-02-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -82,7 +82,12 @@ files:
|
|
82
82
|
- README.md
|
83
83
|
- Rakefile
|
84
84
|
- config/geolocal.rb
|
85
|
+
- contrib/array-vs-range-benchmark.rb
|
86
|
+
- contrib/continents.rb
|
85
87
|
- geolocal.gemspec
|
88
|
+
- lib/generators/geolocal/USAGE
|
89
|
+
- lib/generators/geolocal/geolocal_generator.rb
|
90
|
+
- lib/generators/geolocal/templates/geolocal.rb
|
86
91
|
- lib/geolocal.rb
|
87
92
|
- lib/geolocal/configuration.rb
|
88
93
|
- lib/geolocal/provider/base.rb
|
@@ -91,10 +96,10 @@ files:
|
|
91
96
|
- lib/geolocal/railtie.rb
|
92
97
|
- lib/geolocal/version.rb
|
93
98
|
- lib/tasks/geolocal.rake
|
94
|
-
- spec/configuration_spec.rb
|
95
99
|
- spec/data/dbip-country.csv.gz
|
96
|
-
- spec/
|
97
|
-
- spec/provider/
|
100
|
+
- spec/geolocal/configuration_spec.rb
|
101
|
+
- spec/geolocal/provider/base_spec.rb
|
102
|
+
- spec/geolocal/provider/db_ip_spec.rb
|
98
103
|
- spec/spec_helper.rb
|
99
104
|
homepage: http://github.com/bronson/geolocal
|
100
105
|
licenses:
|
@@ -121,8 +126,8 @@ signing_key:
|
|
121
126
|
specification_version: 4
|
122
127
|
summary: Generates plain Ruby if statements to geocode IP addresses
|
123
128
|
test_files:
|
124
|
-
- spec/configuration_spec.rb
|
125
129
|
- spec/data/dbip-country.csv.gz
|
126
|
-
- spec/
|
127
|
-
- spec/provider/
|
130
|
+
- spec/geolocal/configuration_spec.rb
|
131
|
+
- spec/geolocal/provider/base_spec.rb
|
132
|
+
- spec/geolocal/provider/db_ip_spec.rb
|
128
133
|
- spec/spec_helper.rb
|
data/spec/provider/base_spec.rb
DELETED
@@ -1,86 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require 'geolocal/provider/base'
|
3
|
-
|
4
|
-
|
5
|
-
describe Geolocal::Provider::Base do
|
6
|
-
let(:it) { described_class }
|
7
|
-
let(:provider) { it.new }
|
8
|
-
|
9
|
-
let(:example_results) { {
|
10
|
-
'USv4' => "0..16777215,\n34603520..34604031,\n34605568..34606591,\n",
|
11
|
-
'USv6' => "0..42540528726795050063891204319802818559,\n" +
|
12
|
-
"42540569291614257367232052214305390592..42540570559264857595461453711008595967,\n"
|
13
|
-
} }
|
14
|
-
|
15
|
-
before do
|
16
|
-
Geolocal.configure do |config|
|
17
|
-
config.countries = { 'us': 'US' }
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
it 'can generate both ipv4 and ipv6' do
|
22
|
-
Geolocal.configure do |config|
|
23
|
-
config.module = 'Geolocal_v4v6'
|
24
|
-
end
|
25
|
-
|
26
|
-
io = StringIO.new
|
27
|
-
provider.output io, example_results
|
28
|
-
expect(io.string).to include '0..42540528726795050063891204319802818559'
|
29
|
-
expect(io.string).to include '34605568..34606591'
|
30
|
-
|
31
|
-
eval io.string
|
32
|
-
expect(Geolocal_v4v6.in_us? '2.16.13.255').to eq true
|
33
|
-
expect(Geolocal_v4v6.in_us? IPAddr.new('2.16.13.255')).to eq true
|
34
|
-
expect(Geolocal_v4v6.in_us? 34606591, Socket::AF_INET).to eq true
|
35
|
-
expect(Geolocal_v4v6.in_us? 34606592, Socket::AF_INET).to eq nil
|
36
|
-
|
37
|
-
expect(Geolocal_v4v6.in_us? '2001:400::').to eq true
|
38
|
-
expect(Geolocal_v4v6.in_us? IPAddr.new('2001:400::')).to eq true
|
39
|
-
expect(Geolocal_v4v6.in_us? 42540570559264857595461453711008595967, Socket::AF_INET6).to eq true
|
40
|
-
expect(Geolocal_v4v6.in_us? 42540570559264857595461453711008595968, Socket::AF_INET6).to eq nil
|
41
|
-
end
|
42
|
-
|
43
|
-
it 'can turn off ipv4' do
|
44
|
-
Geolocal.configure do |config|
|
45
|
-
config.ipv4 = false
|
46
|
-
config.module = 'Geolocal_v6'
|
47
|
-
end
|
48
|
-
|
49
|
-
io = StringIO.new
|
50
|
-
provider.output io, example_results.tap { |h| h.delete('USv4') }
|
51
|
-
expect(io.string).to include '0..42540528726795050063891204319802818559'
|
52
|
-
expect(io.string).not_to include '34605568..34606591'
|
53
|
-
|
54
|
-
eval io.string
|
55
|
-
expect{ Geolocal_v6.in_us? '2.16.13.255' }.to raise_error(/ipv4 was not compiled in/)
|
56
|
-
expect( Geolocal_v6.in_us? '2001:400::' ).to eq true
|
57
|
-
end
|
58
|
-
|
59
|
-
it 'can turn off ipv6' do
|
60
|
-
Geolocal.configure do |config|
|
61
|
-
config.ipv6 = false
|
62
|
-
config.module = 'Geolocal_v4'
|
63
|
-
end
|
64
|
-
|
65
|
-
io = StringIO.new
|
66
|
-
provider.output io, example_results.tap { |h| h.delete('USv6') }
|
67
|
-
expect(io.string).to include '34605568..34606591'
|
68
|
-
expect(io.string).not_to include '0..42540528726795050063891204319802818559'
|
69
|
-
|
70
|
-
eval io.string
|
71
|
-
expect{ Geolocal_v4.in_us? '2001:400::' }.to raise_error(/ipv6 was not compiled in/)
|
72
|
-
expect( Geolocal_v4.in_us? '2.16.13.255' ).to eq true
|
73
|
-
end
|
74
|
-
|
75
|
-
it 'can turn off both ipv4 and ipv6' do
|
76
|
-
Geolocal.configure do |config|
|
77
|
-
config.ipv4 = false
|
78
|
-
config.ipv6 = false
|
79
|
-
end
|
80
|
-
|
81
|
-
io = StringIO.new
|
82
|
-
provider.output io, example_results.tap { |h| h.delete('USv4'); h.delete('USv6') }
|
83
|
-
expect(io.string).not_to include '34605568..34606591'
|
84
|
-
expect(io.string).not_to include '0..42540528726795050063891204319802818559'
|
85
|
-
end
|
86
|
-
end
|