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 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