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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5d547622e164acc542c21353ad5fff91f715e27a
4
- data.tar.gz: 509c64a23b967b85aed9ba610500fb361aeaa7f5
3
+ metadata.gz: d34a6dd5b6ab7a2083148bfda6cea13bea50c1d4
4
+ data.tar.gz: 97e8790d7cae108624fbdf427f4c0ff5003bbb56
5
5
  SHA512:
6
- metadata.gz: 7295ff74aace198794a8717274d28eec932e6c679f7e3ffdc4d5dc9e9aa12adf3f8dc4da021391f8cc251deaf183e73c8fdd853cb1de33f91372f160f35f390e
7
- data.tar.gz: 9295764ea999981303a66498fb7dedbd38001c184d86dd6af88b85753895fd0a667adf47e10db92d7e088622b4524ce755bb22ecb2394432b793bb02abe139b0
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
- puts 'You must provide an IP.'
13
- exit
12
+ fail 'You must provide an IP.'
14
13
  end
15
14
 
16
15
  ip = ARGV[0]
17
- city = SimpleGeolocator.city(ip)
18
- region = SimpleGeolocator.region(ip)[:name]
19
- country = SimpleGeolocator.country(ip)[:name]
20
- zip = SimpleGeolocator.zip(ip)
21
- ll = SimpleGeolocator.ll(ip)
22
- timezone = SimpleGeolocator.timezone(ip)
23
- isp = SimpleGeolocator.isp_name(ip)
24
- org = SimpleGeolocator.organization_name(ip)
25
- connection_attributes = SimpleGeolocator.connection(ip)
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
- .color(StringUtility.random_color_six)
33
- puts Rainbow("Exact location: #{ll[0]}, #{ll[1]}")
34
- .color(StringUtility.random_color_six)
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
@@ -1,188 +1,24 @@
1
- require 'httpclient'
2
- require 'json'
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
- extend self
7
+ module_function
6
8
 
7
- @client = HTTPClient.new
8
9
  @cache = {}
9
10
 
10
- # Gets the full JSON response, useful for getting multiple pieces of data in
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
- response['org']
139
- end
140
-
141
- # Gets the IP connection attributes - if it's a mobile and/or a proxy
142
- # connection.
143
- # @param ip [String] See #get_full_response
144
- # @return [Hash] A hash containing data formatted as
145
- # { :mobile => true, :proxy => true}
146
- # @return [String] A string containing the error message.
147
- def connection(ip)
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: 1.3.2
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-03-23 00:00:00.000000000 Z
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.0.0
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.0.0
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.5.0
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.5.0
40
+ version: '2'
41
41
  - !ruby/object:Gem::Dependency
42
- name: httpclient
42
+ name: curb
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '2.6'
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.6.0.1
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.6'
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: 2.6.0.1
61
- description: Accessing the IP API through HTTPClient. I found that many, if not all,
62
- Geolocation gems were very annoying and overly-complex to use. Thus, this gem was
63
- born. It does not use anything like Google or Yahoo! Geolocation because I have
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