simple_geolocator 1.3.2 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|