simple_geolocator 1.3.2 → 2.0.0
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/CHANGELOG.md +26 -0
- data/bin/simplegeo +16 -19
- data/lib/simple_geolocator/ipapi_response.rb +83 -0
- data/lib/simple_geolocator.rb +16 -180
- metadata +43 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d34a6dd5b6ab7a2083148bfda6cea13bea50c1d4
|
4
|
+
data.tar.gz: 97e8790d7cae108624fbdf427f4c0ff5003bbb56
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: db40c6977128f87c0413d473a5547a1c6e8bdf5af73b728284553e580a27458b2217e3e65283cea0c183d40470e98292cc5c76e563a4ee3004b345dd389f4469
|
7
|
+
data.tar.gz: 6d91d3dcf14e9b96517e0aec6a62cec24a959911c044d87f20abb23dcded0e1f052f0ae1843cb2a74addffe120c9c4d5272fed06402c11a269dbe14f938196a4
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,30 @@
|
|
1
1
|
# Changelog
|
2
|
+
## Version 2
|
3
|
+
### Version 2.0.0
|
4
|
+
* Complete rewrite of the gem. Includes the following changes:
|
5
|
+
* SimpleGeolocator is no longer a helper module containing methods for every type of data. Now, you call the
|
6
|
+
`SimpleGeolocator#get` function, which returns an IPAPIResponse object. This object has instance attributes to
|
7
|
+
replace almost all of the old SimpleGeolocator module functions. The exception to this is connection, which has
|
8
|
+
been replaced by the `IPAPIResponse#mobile?` and `IPAPIResponse#proxy?` functions.
|
9
|
+
* Proper error handling has been introduced. Functions will no longer return error strings. Now,
|
10
|
+
`IPAPIResponse#initialize` fails with the according errors, to quickly alert the developer or user that something
|
11
|
+
has gone wrong.
|
12
|
+
* `zip` is no longer an Integer, because that implies that some math should be done on it. It is a String now. You
|
13
|
+
can call `#to_i` if you for some reason want it to be an Integer.
|
14
|
+
* Region (`#region`) and country (`#country`) are now represented by a LOCATION_STRUCT Struct, which has 2 instance
|
15
|
+
attributes: name and code.
|
16
|
+
* Longitude and latitude (`#ll`) are no longer represented as an array, but a Pair from the data_types gem. This
|
17
|
+
makes significantly more conceptual sense.
|
18
|
+
* `#isp_name` and `#organization_name` are now represented by the new named `isp` and `organization` instance
|
19
|
+
attributes.
|
20
|
+
* Changes to the libraries and requirements:
|
21
|
+
* HTTPClient is no longer used. It had largely too much overhead for a gem this simple. When we do not need
|
22
|
+
keepalive, complicated requests, or even just a "full" client, I found it didn't make sense to use it. Now, Curb
|
23
|
+
is used.
|
24
|
+
* The stdlib JSON library is not used anymore. It has been replaced by Optimized JSON (Oj) for performance reasons.
|
25
|
+
* data_types is now a library used by SimpleGeolocator.
|
26
|
+
* Pessimistic version requirements are now used.
|
27
|
+
|
2
28
|
## Version 1
|
3
29
|
### Version 1.3.2
|
4
30
|
* License as MIT
|
data/bin/simplegeo
CHANGED
@@ -9,30 +9,27 @@ require 'string-utility'
|
|
9
9
|
require_relative '../lib/simple_geolocator'
|
10
10
|
|
11
11
|
if ARGV.empty?
|
12
|
-
|
13
|
-
exit
|
12
|
+
fail 'You must provide an IP.'
|
14
13
|
end
|
15
14
|
|
16
15
|
ip = ARGV[0]
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
16
|
+
response = SimpleGeolocator.get(ip)
|
17
|
+
city = response.city
|
18
|
+
region = response.region.name
|
19
|
+
country = response.country.name
|
20
|
+
zip = response.zip
|
21
|
+
ll = response.ll
|
22
|
+
timezone = response.timezone
|
23
|
+
isp = response.isp
|
24
|
+
org = response.organization
|
25
|
+
mobile = response.mobile?
|
26
|
+
proxy = response.proxy?
|
26
27
|
|
27
28
|
puts "Here is the data for #{ip}:"
|
28
29
|
puts Rainbow("ISP: #{isp}").color(StringUtility.random_color_six)
|
29
30
|
puts Rainbow("Organization: #{org}").color(StringUtility.random_color_six)
|
30
31
|
puts Rainbow("Timezone: #{timezone}").color(StringUtility.random_color_six)
|
31
|
-
puts Rainbow("Location: #{city}, #{region}, #{country}, #{zip}")
|
32
|
-
|
33
|
-
puts Rainbow(
|
34
|
-
|
35
|
-
puts Rainbow('They are using a mobile connection.')
|
36
|
-
.color(StringUtility.random_color_six) if connection_attributes[:mobile]
|
37
|
-
puts Rainbow('They are using a proxy.')
|
38
|
-
.color(StringUtility.random_color_six) if connection_attributes[:proxy]
|
32
|
+
puts Rainbow("Location: #{city}, #{region}, #{country}, #{zip}").color(StringUtility.random_color_six)
|
33
|
+
puts Rainbow("Exact location: #{ll.left}, #{ll.right}").color(StringUtility.random_color_six)
|
34
|
+
puts Rainbow('They are using a mobile connection.').color(StringUtility.random_color_six) if mobile
|
35
|
+
puts Rainbow('They are using a proxy.').color(StringUtility.random_color_six) if proxy
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# noinspection RubyTooManyInstanceVariablesInspection
|
2
|
+
module SimpleGeolocator
|
3
|
+
class IPAPIResponse
|
4
|
+
# @return [Hash<String, Any>] The full parsed response given by the API.
|
5
|
+
attr_reader :full_response
|
6
|
+
|
7
|
+
# @return [LOCATION_STRUCT] The country name and code.
|
8
|
+
attr_reader :country
|
9
|
+
|
10
|
+
# @return [LOCATION_STRUCT] The region name and code.
|
11
|
+
attr_reader :region
|
12
|
+
|
13
|
+
# @return [String] The name of the city.
|
14
|
+
attr_reader :city
|
15
|
+
|
16
|
+
# @return [String] The zip code.
|
17
|
+
attr_reader :zip
|
18
|
+
|
19
|
+
# @return [Pair<Float, Float>] The pair of the longitude and latitude.
|
20
|
+
attr_reader :ll
|
21
|
+
|
22
|
+
# @return [String] The name of the timezone, e.g., America/Los Angeles.
|
23
|
+
attr_reader :timezone
|
24
|
+
|
25
|
+
# @return [String] The name of the ISP that the IP is using.
|
26
|
+
attr_reader :isp
|
27
|
+
|
28
|
+
# @return [String] The name of the organization that the IP is within, or their ISP name.
|
29
|
+
attr_reader :organization
|
30
|
+
|
31
|
+
# A simple struct that stores the name and code for the location.
|
32
|
+
# @param name [String] The name of the location.
|
33
|
+
# @param code [String] The location code.
|
34
|
+
LOCATION_STRUCT = Struct.new(:name, :code)
|
35
|
+
|
36
|
+
# Creates a new IPAPIResponse object.
|
37
|
+
# @param response [Hash] The response given by the IP API.
|
38
|
+
# @raise [RuntimeError] When the request fails, raises a RuntimeError depending on the error message.
|
39
|
+
def initialize(response = {})
|
40
|
+
@full_response = response
|
41
|
+
@status = response['status']
|
42
|
+
if successful?
|
43
|
+
@country = LOCATION_STRUCT.new(response['country'], response['countryCode'])
|
44
|
+
@region = LOCATION_STRUCT.new(response['regionName'], response['region'])
|
45
|
+
@city = response['city']
|
46
|
+
@zip = response['zip']
|
47
|
+
@ll = Pair.new(response['lat'], response['lon'])
|
48
|
+
@isp = response['isp']
|
49
|
+
@timezone = response['timezone']
|
50
|
+
@organization = response['org']
|
51
|
+
@mobile = response['mobile']
|
52
|
+
@proxy = response['proxy']
|
53
|
+
else
|
54
|
+
case response['message']
|
55
|
+
when 'private range'
|
56
|
+
fail 'The IP address is part of a private range.'
|
57
|
+
when 'reserved range'
|
58
|
+
fail 'The IP address is part of a reserved range.'
|
59
|
+
when 'invalid query'
|
60
|
+
fail 'The IP address or domain name is invalid.'
|
61
|
+
when 'quota'
|
62
|
+
fail 'You have reached the IP API rate limit.'
|
63
|
+
else
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# @return [Boolean] Whether the request was successful.
|
69
|
+
def successful?
|
70
|
+
@status == 'success'
|
71
|
+
end
|
72
|
+
|
73
|
+
# @return [Boolean] Whether the IP is on a mobile device.
|
74
|
+
def mobile?
|
75
|
+
@mobile
|
76
|
+
end
|
77
|
+
|
78
|
+
# @return [Boolean] Whether the IP is on a proxy.
|
79
|
+
def proxy?
|
80
|
+
@proxy
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/lib/simple_geolocator.rb
CHANGED
@@ -1,188 +1,24 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
1
|
+
require 'curb'
|
2
|
+
require 'oj'
|
3
|
+
require 'data_types/pair'
|
4
|
+
require_relative 'simple_geolocator/ipapi_response'
|
3
5
|
|
4
6
|
module SimpleGeolocator
|
5
|
-
|
7
|
+
module_function
|
6
8
|
|
7
|
-
@client = HTTPClient.new
|
8
9
|
@cache = {}
|
9
10
|
|
10
|
-
|
11
|
-
# a single request.
|
12
|
-
# @param ip [String] The IP to get data for.
|
13
|
-
# @return [JSON] A parsed JSON object containing the response.
|
14
|
-
def get_full_response(ip)
|
15
|
-
return @cache[ip] unless @cache[ip].nil?
|
16
|
-
url = "http://ip-api.com/json/#{ip}?fields=258047"
|
17
|
-
uri = URI.parse(url)
|
18
|
-
response = @client.get(uri)
|
19
|
-
@cache[ip] = JSON.parse(response.body)
|
20
|
-
end
|
21
|
-
|
22
|
-
# Gets whether the request failed or not.
|
23
|
-
# @param response [JSON] The parsed response body (#get_full_response) to
|
24
|
-
# check.
|
25
|
-
# @return [Boolean] True if successful, false if errored.
|
26
|
-
def request_successful?(response)
|
27
|
-
case response['status']
|
28
|
-
when 'success'
|
29
|
-
true
|
30
|
-
when 'fail'
|
31
|
-
false
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
# Gets the country data for the IP.
|
36
|
-
# @param ip [String] See #get_full_response
|
37
|
-
# @return [Hash] A hash containing data formatted as
|
38
|
-
# { :name => 'United States', :code => 'US' }
|
39
|
-
# @return [String] A string containing the error message.
|
40
|
-
def country(ip)
|
41
|
-
response = get_full_response(ip)
|
42
|
-
err = error(response)
|
43
|
-
return err unless err.nil?
|
44
|
-
ret = {
|
45
|
-
name: response['country'],
|
46
|
-
code: response['countryCode']
|
47
|
-
}
|
48
|
-
ret
|
49
|
-
end
|
50
|
-
|
51
|
-
# Gets the region data for the IP.
|
52
|
-
# @param ip [String] See #get_full_response
|
53
|
-
# @return [Hash] A hash containing data formatted as
|
54
|
-
# { :name => 'Oregon', :code => 'OR'}
|
55
|
-
# @return [String] A string containing the error message.
|
56
|
-
def region(ip)
|
57
|
-
response = get_full_response(ip)
|
58
|
-
err = error(response)
|
59
|
-
return err unless err.nil?
|
60
|
-
ret = {
|
61
|
-
name: response['regionName'],
|
62
|
-
code: response['region']
|
63
|
-
}
|
64
|
-
ret
|
65
|
-
end
|
66
|
-
|
67
|
-
# Gets the city name for the IP.
|
68
|
-
# @param ip [String] See #get_full_response
|
69
|
-
# @return [String] The name of the city that the IP is located in.
|
70
|
-
# @return [String] A string containing the error message.
|
71
|
-
def city(ip)
|
72
|
-
response = get_full_response(ip)
|
73
|
-
err = error(response)
|
74
|
-
return err unless err.nil?
|
75
|
-
|
76
|
-
response['city']
|
77
|
-
end
|
78
|
-
|
79
|
-
# Gets the zip code for the IP.
|
80
|
-
# @param ip [String] See #get_full_response
|
81
|
-
# @return [Int] The zip code that the IP is located in.
|
82
|
-
# @return [String] A string containing the error message.
|
83
|
-
def zip(ip)
|
84
|
-
response = get_full_response(ip)
|
85
|
-
err = error(response)
|
86
|
-
return err unless err.nil?
|
87
|
-
|
88
|
-
response['zip'].to_i
|
89
|
-
end
|
90
|
-
|
91
|
-
# Gets the latitude, longitude for the IP.
|
92
|
-
# @param ip [String] See #get_full_response
|
93
|
-
# @return [Array] An array of Floats formatted as lat, lon
|
94
|
-
# @return [String] A string containing the error message.
|
95
|
-
def ll(ip)
|
96
|
-
response = get_full_response(ip)
|
97
|
-
err = error(response)
|
98
|
-
return err unless err.nil?
|
99
|
-
|
100
|
-
ret = [response['lat'], response['lon']]
|
101
|
-
ret
|
102
|
-
end
|
103
|
-
|
104
|
-
# Gets the timezone for the IP.
|
105
|
-
# @param ip [String] See #get_full_response
|
106
|
-
# @return [String] The timezone (UTC, PST, etc.) that the IP is in.
|
107
|
-
# @return [String] A string containing the error message.
|
108
|
-
def timezone(ip)
|
109
|
-
response = get_full_response(ip)
|
110
|
-
err = error(response)
|
111
|
-
return err unless err.nil?
|
112
|
-
|
113
|
-
response['timezone']
|
114
|
-
end
|
115
|
-
|
116
|
-
# Gets the name of the IP's Internet Service Provider.
|
117
|
-
# @param ip [String] See #get_full_response
|
118
|
-
# @return [String] The ISP name, such as Comcast Cable.
|
119
|
-
# @return [String] A string containing the error message.
|
120
|
-
def isp_name(ip)
|
121
|
-
response = get_full_response(ip)
|
122
|
-
err = error(response)
|
123
|
-
return err unless err.nil?
|
124
|
-
|
125
|
-
response['isp']
|
126
|
-
end
|
127
|
-
|
128
|
-
# Gets the name of the IP's organization. For most people, this is identical
|
129
|
-
# to their ISP name.
|
130
|
-
# @param ip [String] See #get_full_response
|
131
|
-
# @return [String] The organization name, such as Google.
|
132
|
-
# @return [String] A string containing the error message.
|
133
|
-
def organization_name(ip)
|
134
|
-
response = get_full_response(ip)
|
135
|
-
err = error(response)
|
136
|
-
return err unless err.nil?
|
11
|
+
URL_FORMAT = 'http://ip-api.com/json/%s?fields=258047'.freeze
|
137
12
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
#
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
response = get_full_response(ip)
|
149
|
-
err = error(response)
|
150
|
-
return err unless err.nil?
|
151
|
-
ret = {
|
152
|
-
mobile: response['mobile'],
|
153
|
-
proxy: response['proxy']
|
154
|
-
}
|
155
|
-
ret
|
156
|
-
end
|
157
|
-
|
158
|
-
# Gets the according description for the semi-ambiguous error returned by the
|
159
|
-
# API.
|
160
|
-
# @param error [String] The error message returned by #error
|
161
|
-
# @return [String] The error description.
|
162
|
-
# @return [Nil] If you provided an invalid error message.
|
163
|
-
def get_error_description(error)
|
164
|
-
case error
|
165
|
-
when 'private range'
|
166
|
-
return 'The IP address is part of a private range.'
|
167
|
-
when 'reserved range'
|
168
|
-
return 'The IP address is part of a reserved range.'
|
169
|
-
when 'invalid query'
|
170
|
-
return 'The IP address or domain name is invalid.'
|
171
|
-
when 'quota'
|
172
|
-
return 'You have reached the quota.'
|
173
|
-
else
|
174
|
-
return nil
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
private
|
179
|
-
|
180
|
-
# Gets the error message from a response.
|
181
|
-
# @param response [JSON] See #request_successful?
|
182
|
-
# @return [String] The error message.
|
183
|
-
# @return [Nil] If there was no error message to begin with.
|
184
|
-
def error(response)
|
185
|
-
return response['message'] unless request_successful?(response)
|
186
|
-
nil
|
13
|
+
# Gets the full response. From here, all the data related to the IP can be accessed. Caches the result in order to
|
14
|
+
# prevent reaching the rate limit.
|
15
|
+
# @param ip [String] The IP to get data for.
|
16
|
+
# @return [IPAPIResponse] The full parsed response object.
|
17
|
+
def get(ip)
|
18
|
+
return @cache[ip] if @cache.key?(ip)
|
19
|
+
url = format(URL_FORMAT, ip)
|
20
|
+
response = Curl.get(url).body_str
|
21
|
+
ipapi = SimpleGeolocator::IPAPIResponse.new(Oj.load(response))
|
22
|
+
@cache[ip] = ipapi
|
187
23
|
end
|
188
24
|
end
|
metadata
CHANGED
@@ -1,69 +1,88 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simple_geolocator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eli Foster
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-04-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rainbow
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 2
|
19
|
+
version: '2'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 2
|
26
|
+
version: '2'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: string-utility
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 2
|
33
|
+
version: '2'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 2
|
40
|
+
version: '2'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: curb
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
48
|
-
|
47
|
+
version: '0.9'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.9'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: oj
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
49
60
|
- !ruby/object:Gem::Version
|
50
|
-
version: 2
|
61
|
+
version: '2'
|
51
62
|
type: :runtime
|
52
63
|
prerelease: false
|
53
64
|
version_requirements: !ruby/object:Gem::Requirement
|
54
65
|
requirements:
|
55
66
|
- - "~>"
|
56
67
|
- !ruby/object:Gem::Version
|
57
|
-
version: '2
|
58
|
-
|
68
|
+
version: '2'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: data_types
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
59
81
|
- !ruby/object:Gem::Version
|
60
|
-
version:
|
61
|
-
description:
|
62
|
-
|
63
|
-
|
64
|
-
found that those APIs are unpredictable and often-times broken. This Gem has been
|
65
|
-
made to be as simple to use as possible. It also includes a CLI that can be called
|
66
|
-
simple as 'simplegeo' followed by the IP.
|
82
|
+
version: '1'
|
83
|
+
description: |
|
84
|
+
Accessing the IP API through Curb. This gem has been made to be as simple to use as possible. As such, it even includes
|
85
|
+
a CLI that can be called through `simplegeo <ip>`.
|
67
86
|
email: elifosterwy@gmail.com
|
68
87
|
executables:
|
69
88
|
- simplegeo
|
@@ -73,6 +92,7 @@ files:
|
|
73
92
|
- CHANGELOG.md
|
74
93
|
- bin/simplegeo
|
75
94
|
- lib/simple_geolocator.rb
|
95
|
+
- lib/simple_geolocator/ipapi_response.rb
|
76
96
|
homepage: https://github.com/elifoster/simple_geolocator
|
77
97
|
licenses:
|
78
98
|
- MIT
|