geolocal 0.6.2 → 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 +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
|