locality 0.0.1
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/README.md +80 -0
- data/lib/locality/aregion.rb +22 -0
- data/lib/locality/configurable.rb +25 -0
- data/lib/locality/configuration.rb +68 -0
- data/lib/locality/ip.rb +96 -0
- data/lib/locality/postnummerservice/entry.rb +35 -0
- data/lib/locality/postnummerservice.rb +40 -0
- data/lib/locality.rb +8 -0
- metadata +205 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: cc08ca579c909a36f517e55af8c15dbc8b3e7bf6
|
4
|
+
data.tar.gz: 5eb0d6c9e2e211110857db7fb3be22e4d22fb1f3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5d1b4079773f7ec8b22c2687a9e10ca2861eba4f8bb48a9512a1ee0b016af10b5deee0e20c4ccb88d489d7eb5009fcdd203441a1106ddc6b2d867ff2d37c31bc
|
7
|
+
data.tar.gz: 1f3157cf23f35c5b8a221631dcc8fd5c717c92167013c1bfe67b2cc945d16eac5bd985c39669d45c233ed0573c5756bb3bfaaee96e7abb2f6574b074c86b099d
|
data/README.md
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# Locality
|
2
|
+
|
3
|
+
## Swedish A-Region
|
4
|
+
|
5
|
+
See https://sv.wikipedia.org/wiki/A-region
|
6
|
+
|
7
|
+
### Installation
|
8
|
+
|
9
|
+
```bash
|
10
|
+
# Go and buy the rec2LK database from postnummerservice.se
|
11
|
+
# Put it in the ./db directory
|
12
|
+
```
|
13
|
+
|
14
|
+
### Configuration
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
Locality.configure do |config|
|
18
|
+
|
19
|
+
# By default, locality will look for the rec2LK database in
|
20
|
+
# these locations: `Locality.config.postnummerfilen_paths`
|
21
|
+
# You can add a custom location like so:
|
22
|
+
config.postnummerfilen_path = '/some/custom/rec2LK.csv'
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
```
|
27
|
+
### Usage
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
lookup = Locality::Aregion.new 21 # Gotland
|
31
|
+
|
32
|
+
lookup.zip_codes # => Array of Integers
|
33
|
+
# More to come...
|
34
|
+
```
|
35
|
+
|
36
|
+
## IP Lookup
|
37
|
+
|
38
|
+
### Installation
|
39
|
+
|
40
|
+
```bash
|
41
|
+
# Get the libmaxminddb, e.g. via homebrew on Mac OS
|
42
|
+
brew install libmaxminddb
|
43
|
+
# Tell bundler where to find it
|
44
|
+
bundle config build.hive_geoip2 --with-opt-dir=$(brew --prefix)
|
45
|
+
# Download and extract the MaxMind GeoIp2 database
|
46
|
+
curl http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.mmdb.gz -o ./db && gunzip ./db/GeoLite2-City.mmdb.gz
|
47
|
+
```
|
48
|
+
|
49
|
+
### Configuration
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
Locality.configure do |config|
|
53
|
+
|
54
|
+
# By default, locality will look for the maxmind database in
|
55
|
+
# these locations: `Locality.config.maxmind_geoip2_path`
|
56
|
+
# You can add a custom location like so:
|
57
|
+
config.maxmind_geoip2_path = '/some/custom/maxmind_geolite2.mmdb'
|
58
|
+
|
59
|
+
# If you want, you can add custom lookups like so:
|
60
|
+
config.add_custom_ip_location '198.51.100.99/24', city_name: 'Main Office', country_name: 'Australia'
|
61
|
+
end
|
62
|
+
```
|
63
|
+
|
64
|
+
### Usage
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
lookup = Locality::IP.new '198.51.100.55'
|
68
|
+
|
69
|
+
# All these attributes return either a String or nil.
|
70
|
+
lookup.ip
|
71
|
+
lookup.city_name
|
72
|
+
lookup.province_name
|
73
|
+
lookup.state_name
|
74
|
+
lookup.country_name
|
75
|
+
lookup.human_readable_name
|
76
|
+
|
77
|
+
# Get them all combined in a Hash
|
78
|
+
lookup.to_hash
|
79
|
+
```
|
80
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'locality/postnummerservice'
|
2
|
+
|
3
|
+
module Locality
|
4
|
+
# See https://sv.wikipedia.org/wiki/A-region
|
5
|
+
class Aregion
|
6
|
+
|
7
|
+
def initialize(raw_code)
|
8
|
+
@raw_code = raw_code
|
9
|
+
end
|
10
|
+
|
11
|
+
def zip_codes
|
12
|
+
@zip_codes ||= backend.aregions[1].map(&:zip_code)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def backend
|
18
|
+
Locality::Postnummerservice
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'locality/configuration'
|
2
|
+
|
3
|
+
module Locality
|
4
|
+
module Configurable
|
5
|
+
|
6
|
+
# Public: Returns the the configuration instance.
|
7
|
+
#
|
8
|
+
def config
|
9
|
+
@config ||= Configuration.new
|
10
|
+
end
|
11
|
+
|
12
|
+
# Public: Yields the configuration instance.
|
13
|
+
#
|
14
|
+
def configure(&block)
|
15
|
+
yield config
|
16
|
+
end
|
17
|
+
|
18
|
+
# Public: Reset the configuration (useful for testing)
|
19
|
+
#
|
20
|
+
def reset!
|
21
|
+
@config = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module Locality
|
4
|
+
class Configuration
|
5
|
+
|
6
|
+
# IP
|
7
|
+
|
8
|
+
attr_writer :maxmind_geoip2_path
|
9
|
+
|
10
|
+
def custom_ip_locations
|
11
|
+
@custom_ip_locations ||= {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def add_custom_ip_location(ip, data)
|
15
|
+
custom_ip_locations[IPAddr.new(ip)] = data.to_hash
|
16
|
+
end
|
17
|
+
|
18
|
+
def maxmind_geoip2_path
|
19
|
+
maxmind_geoip2_paths.detect &:readable?
|
20
|
+
end
|
21
|
+
|
22
|
+
def maxmind_geoip2_paths
|
23
|
+
paths maxmind_geoip2_filename, custom_maxmind_geoip2_path
|
24
|
+
end
|
25
|
+
|
26
|
+
# Postnummerservice
|
27
|
+
|
28
|
+
attr_writer :postnummerfilen_path
|
29
|
+
|
30
|
+
def postnummerfilen_path
|
31
|
+
postnummerfilen_paths.detect &:readable?
|
32
|
+
end
|
33
|
+
|
34
|
+
def postnummerfilen_paths
|
35
|
+
paths postnummerfilen_filename, custom_postnummerfilen_path
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def maxmind_geoip2_filename
|
41
|
+
'GeoLite2-City.mmdb'
|
42
|
+
end
|
43
|
+
|
44
|
+
def postnummerfilen_filename
|
45
|
+
'rec2LK.csv'
|
46
|
+
end
|
47
|
+
|
48
|
+
def custom_maxmind_geoip2_path
|
49
|
+
return unless @maxmind_geoip2_path
|
50
|
+
Pathname.new @maxmind_geoip2_path
|
51
|
+
end
|
52
|
+
|
53
|
+
def custom_postnummerfilen_path
|
54
|
+
return unless @postnummerfilen_path
|
55
|
+
Pathname.new @postnummerfilen_path
|
56
|
+
end
|
57
|
+
|
58
|
+
def paths(appendix = nil, additional = nil)
|
59
|
+
result = []
|
60
|
+
result << Rails.root.join('db', appendix) if defined?(Rails)
|
61
|
+
result << Pathname.new('/mnt').join('databases', appendix)
|
62
|
+
result << Pathname.new(Dir.pwd).join('db', appendix)
|
63
|
+
result << additional
|
64
|
+
result.compact
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
data/lib/locality/ip.rb
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'active_support/core_ext/object/blank'
|
2
|
+
require 'ipaddr'
|
3
|
+
require 'operation'
|
4
|
+
require 'trouble'
|
5
|
+
require 'geocoder'
|
6
|
+
|
7
|
+
require 'locality/configurable'
|
8
|
+
|
9
|
+
module Locality
|
10
|
+
class IP
|
11
|
+
|
12
|
+
def initialize(raw_ip)
|
13
|
+
@raw_ip = raw_ip
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_hash
|
17
|
+
{
|
18
|
+
ip: ip.to_s,
|
19
|
+
city_name: city_name,
|
20
|
+
province_name: province_name,
|
21
|
+
state_name: state_name,
|
22
|
+
country_name: country_name,
|
23
|
+
human_readable_location: human_readable_location,
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def ip
|
28
|
+
@ip ||= ::IPAddr.new raw_ip
|
29
|
+
rescue => exception
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def human_readable_location
|
34
|
+
city_name || province_name || state_name || country_name
|
35
|
+
end
|
36
|
+
|
37
|
+
def city_name
|
38
|
+
return unless lookup
|
39
|
+
lookup.city.presence
|
40
|
+
end
|
41
|
+
|
42
|
+
def province_name
|
43
|
+
return unless lookup
|
44
|
+
lookup.province.presence
|
45
|
+
end
|
46
|
+
|
47
|
+
def state_name
|
48
|
+
return unless lookup
|
49
|
+
lookup.state.presence
|
50
|
+
end
|
51
|
+
|
52
|
+
def country_name
|
53
|
+
return unless lookup
|
54
|
+
lookup.country.presence
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
attr_reader :raw_ip
|
60
|
+
|
61
|
+
def lookup
|
62
|
+
@lookup ||= custom_lookup! || lookup!
|
63
|
+
end
|
64
|
+
|
65
|
+
def lookup!
|
66
|
+
return unless ip.present?
|
67
|
+
ensure_upstream_configuration
|
68
|
+
Geocoder::Lookup.get(:geoip2).search(ip.to_s).first
|
69
|
+
|
70
|
+
rescue IOError => exception
|
71
|
+
Trouble.notify exception
|
72
|
+
configure_upstream! # Maybe the database file just moved somewhere else, repair by force-reconfiguring
|
73
|
+
nil
|
74
|
+
end
|
75
|
+
|
76
|
+
def custom_lookup!
|
77
|
+
::Locality.config.custom_ip_locations.each do |key, value|
|
78
|
+
next unless key.include? ip
|
79
|
+
input = Hashie::Mash.new value
|
80
|
+
# Quacking like Geocoder::Result::GeoIP2
|
81
|
+
return Hashie::Mash.new(city: input.city_name, province: input.province_name, state: input.state_name, country: input.country_name)
|
82
|
+
end
|
83
|
+
nil
|
84
|
+
end
|
85
|
+
|
86
|
+
def ensure_upstream_configuration
|
87
|
+
return if Geocoder.config[:ip_lookup] == :geoip2
|
88
|
+
configure_upstream!
|
89
|
+
end
|
90
|
+
|
91
|
+
def configure_upstream!
|
92
|
+
Geocoder.configure ip_lookup: :geoip2, units: :km, geoip2: { cache: Hash.new, lib: 'hive_geoip2', file: ::Locality.config.maxmind_geoip2_path }
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Locality
|
2
|
+
module Postnummerservice
|
3
|
+
class Entry < Array
|
4
|
+
|
5
|
+
def zip_code
|
6
|
+
self[0].to_i
|
7
|
+
end
|
8
|
+
|
9
|
+
def city_name
|
10
|
+
self[1]
|
11
|
+
end
|
12
|
+
|
13
|
+
def state_code
|
14
|
+
self[2]
|
15
|
+
end
|
16
|
+
|
17
|
+
def state_name
|
18
|
+
self[3]
|
19
|
+
end
|
20
|
+
|
21
|
+
def province_name
|
22
|
+
self[4]
|
23
|
+
end
|
24
|
+
|
25
|
+
def province_code
|
26
|
+
self[5]
|
27
|
+
end
|
28
|
+
|
29
|
+
def aregion
|
30
|
+
self[6].to_i
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'csv'
|
2
|
+
require 'locality/postnummerservice/entry'
|
3
|
+
|
4
|
+
module Locality
|
5
|
+
module Postnummerservice
|
6
|
+
|
7
|
+
def self.aregions
|
8
|
+
load
|
9
|
+
@aregions
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def self.load
|
15
|
+
load! unless loaded?
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.loaded?
|
19
|
+
!!@loaded
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.load!
|
23
|
+
@aregions = {}
|
24
|
+
|
25
|
+
::CSV.foreach(::Locality.config.postnummerfilen_path, encoding: encoding) do |row|
|
26
|
+
next if row.blank?
|
27
|
+
entry = Entry.new(row)
|
28
|
+
@aregions[entry.aregion] ||= []
|
29
|
+
@aregions[entry.aregion] << entry
|
30
|
+
end
|
31
|
+
|
32
|
+
@loaded = true
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.encoding
|
36
|
+
'ISO-8859-1:UTF-8' # from:to
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
data/lib/locality.rb
ADDED
metadata
ADDED
@@ -0,0 +1,205 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: locality
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- bukowskis
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-01-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: geocoder
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.2.6
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.2.6
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: hashie
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: hive_geoip2
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.1.2
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.1.2
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: i18n
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: operation
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: trouble
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rspec
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: guard-rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: rb-fsevent
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: webmock
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
description: See https://github.com/bukowskis/locality
|
168
|
+
email:
|
169
|
+
executables: []
|
170
|
+
extensions: []
|
171
|
+
extra_rdoc_files: []
|
172
|
+
files:
|
173
|
+
- README.md
|
174
|
+
- lib/locality.rb
|
175
|
+
- lib/locality/aregion.rb
|
176
|
+
- lib/locality/configurable.rb
|
177
|
+
- lib/locality/configuration.rb
|
178
|
+
- lib/locality/ip.rb
|
179
|
+
- lib/locality/postnummerservice.rb
|
180
|
+
- lib/locality/postnummerservice/entry.rb
|
181
|
+
homepage: https://github.com/bukowskis/locality
|
182
|
+
licenses: []
|
183
|
+
metadata: {}
|
184
|
+
post_install_message:
|
185
|
+
rdoc_options: []
|
186
|
+
require_paths:
|
187
|
+
- lib
|
188
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
189
|
+
requirements:
|
190
|
+
- - ">="
|
191
|
+
- !ruby/object:Gem::Version
|
192
|
+
version: '0'
|
193
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
194
|
+
requirements:
|
195
|
+
- - ">="
|
196
|
+
- !ruby/object:Gem::Version
|
197
|
+
version: '0'
|
198
|
+
requirements: []
|
199
|
+
rubyforge_project:
|
200
|
+
rubygems_version: 2.2.2
|
201
|
+
signing_key:
|
202
|
+
specification_version: 4
|
203
|
+
summary: Wrapping databases provided by maxmind.com and postnummerservice.se
|
204
|
+
test_files: []
|
205
|
+
has_rdoc:
|