attack-spotlight 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2009 John Nunemaker (for initial structure)
2
+ Copyright (c) 2009 Mark G.
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining
5
+ a copy of this software and associated documentation files (the
6
+ "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish,
8
+ distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so, subject to
10
+ the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,86 @@
1
+ = spotlight
2
+
3
+ A simple interface to the www.hostip.info API.
4
+
5
+ = HostIP.info
6
+
7
+ hostip.info is a "Community Geotarget IP Project" and provides a simple API
8
+ to transform an "IP Address" into a location (including Country, City, and
9
+ coordinates).
10
+
11
+ No API key or sign-up is required to use the service.
12
+
13
+ hostip.info does request that if a lot of databases lookups are being done
14
+ that you download the entire database from them instead.
15
+
16
+ = dependencies
17
+
18
+ - HTTParty (url=http://github.com/jnunemaker/httparty/tree/master)
19
+ [version used=0.3.1]
20
+
21
+ == TESTING environment only
22
+
23
+ - fakeweb [version used=1.2.0]
24
+
25
+ = example
26
+
27
+ == from the command line
28
+
29
+ # spotlight 209.85.171.100
30
+
31
+ == from a ruby script
32
+
33
+ require 'rubygems'
34
+ require 'spotlight'
35
+
36
+ spotlight = Spotlight.new("209.85.171.100")
37
+
38
+ puts spotlight.location.coordinates
39
+
40
+ == from a rails application
41
+
42
+ === in config/environment
43
+
44
+ require 'spotlight'
45
+
46
+ === in your controller
47
+
48
+ @spotlight = Spotlight.new("209.85.171.100")
49
+
50
+ === in your view
51
+
52
+ @spotlight.location.coordinates
53
+
54
+ == from a rails application with caching
55
+
56
+ Using caching is highly advised if possible. The ip_address to location
57
+ conversion doesn't normally change over time (if ever) so why continuously
58
+ query it?
59
+
60
+ Assumption: caching is already enabled and working.\
61
+
62
+ === in config/environment
63
+
64
+ require 'spotlight'
65
+
66
+ === in your controller
67
+
68
+ ip_address = "209.85.171.100"
69
+ @spotlight = Rails.cache.fetch(Spotlight.key(ip_address), :expires_in => 1.weeks) do
70
+ Spotlight.new(ip_address)
71
+ end
72
+
73
+ === in your view
74
+
75
+ @spotlight.location.coordinates
76
+
77
+ = credits
78
+
79
+ Other then the code written by me (url=http://github.com/attack) and the code
80
+ generated by 'jeweler' (url=http://github.com/jnunemaker) for making and
81
+ maintaining gems, there is strong influence from jnunemaker
82
+ (url=http://github.com/jnunemaker) and the gem google-weather.
83
+
84
+ == Copyright
85
+
86
+ Copyright (c) 2009 Mark G. See LICENSE for details.
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 1
4
+ :patch: 0
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.dirname(__FILE__) + '/../lib/spotlight'
4
+
5
+ if ARGV.size == 0
6
+ puts 'Spotlight [Powered by hostip.info]'
7
+ puts 'USAGE: spotlight [ip_address]'
8
+ puts 'EXAMPLES:'
9
+ puts ' spotlight 209.85.171.100'
10
+ exit
11
+ end
12
+
13
+ spotlight = Spotlight.new(ARGV[0])
14
+
15
+ puts "location -- name: #{spotlight.location.name}, latitude: #{spotlight.location.latitude}, longitude: #{spotlight.location.longitude}"
16
+ puts "country -- name: #{spotlight.country.name}, code: #{spotlight.country.code}"
@@ -0,0 +1,67 @@
1
+ require 'rubygems'
2
+ gem 'jnunemaker-httparty'
3
+ require 'httparty'
4
+
5
+ require File.dirname(__FILE__) + '/spotlight/data'
6
+ require File.dirname(__FILE__) + '/spotlight/country'
7
+ require File.dirname(__FILE__) + '/spotlight/location'
8
+
9
+ class Spotlight
10
+ include HTTParty
11
+ base_uri "api.hostip.info"
12
+
13
+ attr_reader :ip
14
+
15
+ def initialize(ip_address)
16
+ # validate ip_address format
17
+ raise unless Spotlight.valid?(ip_address)
18
+ # we are all clear, save the ip
19
+ @ip = ip_address
20
+ # query hostip.info
21
+ self.shine
22
+ self
23
+ end
24
+
25
+ # generate a key from the ip_address
26
+ def self.key(ip_address, prefix="spotlight")
27
+ # validate ip_address format
28
+ raise unless self.valid?(ip_address)
29
+ # all clear, generate the key
30
+ segments = [prefix]
31
+ # this ip_address does not need to be encoded as it is a valid
32
+ # ip_address and only contains key-friendly chars
33
+ segments << ip_address
34
+ segments.join('-')
35
+ end
36
+
37
+ def country
38
+ @country ||= Country.new(@result)
39
+ end
40
+
41
+ def location
42
+ @location ||= Location.new(@result)
43
+ end
44
+
45
+ protected
46
+
47
+ # actually query HostIP.info with the 'IP address'
48
+ # NOTE: 'shine' might not be the best name for the method, other possibles
49
+ # include: query, fetch, get, direct, ask, locate, target ... ???
50
+ def shine
51
+ @result ||= self.class.get(
52
+ "/",
53
+ :query => {:ip => @ip},
54
+ :format => :xml
55
+ )['HostipLookupResultSet']['gml:featureMember']
56
+ end
57
+
58
+ # validate an ip_address
59
+ # regular expression from http://www.ruby-forum.com/topic/62553
60
+ def self.valid?(ip_address)
61
+ return false unless ip_address.is_a?(String)
62
+ regexp = Regexp.new(/(?:25[0-5]|(?:2[0-4]|1\d|[1-9])?\d)(?:\.(?:25[0-5]|(?:2[0-4]|1\d|[1-9])?\d)){3}/)
63
+ return false unless regexp =~ ip_address
64
+ return true
65
+ end
66
+
67
+ end
@@ -0,0 +1,21 @@
1
+ class Spotlight
2
+ class Country < Data
3
+
4
+ def name
5
+ begin
6
+ return @data['Hostip']['countryName']
7
+ rescue
8
+ return nil
9
+ end
10
+ end
11
+
12
+ def code
13
+ begin
14
+ return @data['Hostip']['countryAbbrev']
15
+ rescue
16
+ return nil
17
+ end
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,10 @@
1
+ class Spotlight
2
+ class Data
3
+ attr_reader :data
4
+
5
+ def initialize(data)
6
+ @data = data
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,31 @@
1
+ class Spotlight
2
+ class Location < Data
3
+
4
+ def name
5
+ begin
6
+ return @data['Hostip']['gml:name']
7
+ rescue
8
+ return nil
9
+ end
10
+ end
11
+
12
+ def coordinates
13
+ begin
14
+ return @data['Hostip']['ipLocation']['gml:PointProperty']['gml:Point']['gml:coordinates']
15
+ rescue
16
+ return nil
17
+ end
18
+ end
19
+
20
+ def longitude
21
+ return unless self.coordinates
22
+ self.coordinates.split(',')[0].to_f
23
+ end
24
+
25
+ def latitude
26
+ return unless self.coordinates
27
+ self.coordinates.split(',')[1].to_f
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1 @@
1
+ <HostipLookupResultSet xsi:schemaLocation='http://www.hostip.info/api/hostip-1.0.0.xsd' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:gml='http://www.opengis.net/gml' version='1.0.0' xmlns='http://www.hostip.info/api'><gml:description>This is the Hostip Lookup Service</gml:description><gml:name>hostip</gml:name><gml:boundedBy><gml:Null>inapplicable</gml:Null></gml:boundedBy><gml:featureMember><Hostip><gml:name>TORONTO, ON</gml:name><countryName>CANADA</countryName><countryAbbrev>CA</countryAbbrev><!-- Co-ordinates are available as lng,lat --><ipLocation><gml:PointProperty><gml:Point srsName='http://www.opengis.net/gml/srs/epsg.xml#4326'><gml:coordinates>-79.3833,43.65</gml:coordinates></gml:Point></gml:PointProperty></ipLocation></Hostip></gml:featureMember></HostipLookupResultSet>
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ require 'fakeweb'
4
+
5
+ FakeWeb.allow_net_connect = false
6
+
7
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
8
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
9
+ require 'spotlight'
10
+
11
+ Spec::Runner.configure do |config|
12
+
13
+ end
@@ -0,0 +1,191 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Initialization" do
4
+
5
+ before(:each) do
6
+ FakeWeb.register_uri(:get,
7
+ "http://api.hostip.info/?ip=199.246.67.211",
8
+ :string => File.read(File.join(File.dirname(__FILE__),
9
+ 'fixtures',
10
+ '199_246_67_211.xml')
11
+ )
12
+ )
13
+ end
14
+
15
+ it "should require an ip address" do
16
+ lambda { Spotlight.new }.should raise_error
17
+
18
+ Spotlight.new("199.246.67.211").ip.should == "199.246.67.211"
19
+ end
20
+
21
+ it "should require a proper ip address" do
22
+ lambda { Spotlight.new(199) }.should raise_error
23
+ lambda { Spotlight.new(199.246) }.should raise_error
24
+ lambda { Spotlight.new({:test => "test"}) }.should raise_error
25
+ lambda { Spotlight.new(["test"]) }.should raise_error
26
+ lambda { Spotlight.new("199") }.should raise_error
27
+ lambda { Spotlight.new("199.246") }.should raise_error
28
+ lambda { Spotlight.new("199.246.67") }.should raise_error
29
+ lambda { Spotlight.new("aaa.246.67.211") }.should raise_error
30
+ lambda { Spotlight.new("199.aaa.67.211") }.should raise_error
31
+ lambda { Spotlight.new("199.246.aaa.211") }.should raise_error
32
+ lambda { Spotlight.new("199.246.67.aaa") }.should raise_error
33
+ lambda { Spotlight.new("") }.should raise_error
34
+ lambda { Spotlight.new(" ") }.should raise_error
35
+ lambda { Spotlight.new("aaa") }.should raise_error
36
+
37
+ # sanity check
38
+ lambda { Spotlight.new("199.246.67.211") }.should_not raise_error
39
+ end
40
+
41
+ end
42
+
43
+ describe "Spotlight" do
44
+
45
+ it "should create an ip based key" do
46
+ Spotlight.key("199.246.67.211").should == "spotlight-199.246.67.211"
47
+ end
48
+
49
+ it "should create an ip based key with defined prefix" do
50
+ Spotlight.key("199.246.67.211", "alternate").should == "alternate-199.246.67.211"
51
+ end
52
+
53
+ end
54
+
55
+ describe "Data" do
56
+
57
+ it "should require data" do
58
+ lambda { Spotlight::Data.new }.should raise_error
59
+
60
+ data = {'foo' => {'data' => 'bar'}}
61
+ Spotlight::Data.new(data).data.should == data
62
+ end
63
+
64
+ end
65
+
66
+ describe "Location" do
67
+
68
+ it "should require data" do
69
+ lambda { Spotlight::Location.new }.should raise_error
70
+
71
+ data = {'foo' => {'data' => 'bar'}}
72
+ Spotlight::Location.new(data).data.should == data
73
+ end
74
+
75
+ it "should return nil when the data does not exist" do
76
+ data = {}
77
+ location = Spotlight::Location.new(data)
78
+ location.should_not be_nil
79
+
80
+ location.name.should be_nil
81
+ location.coordinates.should be_nil
82
+ location.longitude.should be_nil
83
+ location.latitude.should be_nil
84
+ end
85
+
86
+ # @data['Hostip']['gml:name']
87
+ it "should respond to name" do
88
+ data = {'Hostip' => {'gml:name' => "test_name"}}
89
+ location = Spotlight::Location.new(data)
90
+ location.should_not be_nil
91
+
92
+ location.name.should == "test_name"
93
+ end
94
+
95
+ # @data['Hostip']['ipLocation']['gml:PointProperty']['gml:Point']['gml:coordinates']
96
+ it "should respond to coordinates" do
97
+ data = {'Hostip' => {'ipLocation' => {'gml:PointProperty' => {'gml:Point' => {'gml:coordinates' => "-79.3833,43.65"}}}}}
98
+ location = Spotlight::Location.new(data)
99
+ location.should_not be_nil
100
+
101
+ location.coordinates.should == "-79.3833,43.65"
102
+ end
103
+
104
+ # @data['Hostip']['ipLocation']['gml:PointProperty']['gml:Point']['gml:coordinates']
105
+ it "should respond to longitude" do
106
+ data = {'Hostip' => {'ipLocation' => {'gml:PointProperty' => {'gml:Point' => {'gml:coordinates' => "-79.3833,43.65"}}}}}
107
+ location = Spotlight::Location.new(data)
108
+ location.should_not be_nil
109
+
110
+ location.longitude.should == -79.3833
111
+ end
112
+
113
+ # @data['Hostip']['ipLocation']['gml:PointProperty']['gml:Point']['gml:coordinates']
114
+ it "should respond to latitude" do
115
+ data = {'Hostip' => {'ipLocation' => {'gml:PointProperty' => {'gml:Point' => {'gml:coordinates' => "-79.3833,43.65"}}}}}
116
+ location = Spotlight::Location.new(data)
117
+ location.should_not be_nil
118
+
119
+ location.latitude.should == 43.65
120
+ end
121
+
122
+ end
123
+
124
+ describe "Country" do
125
+
126
+ it "should require data" do
127
+ lambda { Spotlight::Country.new }.should raise_error
128
+
129
+ data = {'foo' => {'data' => 'bar'}}
130
+ Spotlight::Country.new(data).data.should == data
131
+ end
132
+
133
+ it "should return nil when the data does not exist" do
134
+ data = {}
135
+ country = Spotlight::Country.new(data)
136
+ country.should_not be_nil
137
+
138
+ country.name.should be_nil
139
+ country.code.should be_nil
140
+ end
141
+
142
+ # @data['Hostip']['countryName']
143
+ it "should respond to name" do
144
+ data = {'Hostip' => {'countryName' => "test_name"}}
145
+ country = Spotlight::Country.new(data)
146
+ country.should_not be_nil
147
+
148
+ country.name.should == "test_name"
149
+ end
150
+
151
+ # @data['Hostip']['countryAbbrev']
152
+ it "should respond to coordinates" do
153
+ data = {'Hostip' => {'countryAbbrev' => "CA"}}
154
+ country = Spotlight::Country.new(data)
155
+ country.should_not be_nil
156
+
157
+ country.code.should == "CA"
158
+ end
159
+
160
+ end
161
+
162
+ describe "Fetching" do
163
+
164
+ before(:each) do
165
+ FakeWeb.register_uri(:get,
166
+ "http://api.hostip.info/?ip=199.246.67.211",
167
+ :string => File.read(File.join(File.dirname(__FILE__),
168
+ 'fixtures',
169
+ '199_246_67_211.xml')
170
+ )
171
+ )
172
+ @spotlight = Spotlight.new("199.246.67.211")
173
+ end
174
+
175
+ it "should have location information" do
176
+ @spotlight.location.should_not be_nil
177
+ location = @spotlight.location
178
+ location.name.should == 'TORONTO, ON'
179
+ location.coordinates.should == '-79.3833,43.65'
180
+ location.longitude.should == -79.3833
181
+ location.latitude.should == 43.65
182
+ end
183
+
184
+ it "should have country information" do
185
+ @spotlight.country.should_not be_nil
186
+ country = @spotlight.country
187
+ country.name.should == 'CANADA'
188
+ country.code.should == 'CA'
189
+ end
190
+
191
+ end
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: attack-spotlight
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Mark G
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-04-02 00:00:00 -07:00
13
+ default_executable: spotlight
14
+ dependencies: []
15
+
16
+ description:
17
+ email: spotlight@attackcorp.com
18
+ executables:
19
+ - spotlight
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.rdoc
24
+ - LICENSE
25
+ files:
26
+ - README.rdoc
27
+ - VERSION.yml
28
+ - bin/spotlight
29
+ - lib/spotlight
30
+ - lib/spotlight/country.rb
31
+ - lib/spotlight/data.rb
32
+ - lib/spotlight/location.rb
33
+ - lib/spotlight.rb
34
+ - spec/fixtures
35
+ - spec/fixtures/199_246_67_211.xml
36
+ - spec/spec_helper.rb
37
+ - spec/spotlight_spec.rb
38
+ - LICENSE
39
+ has_rdoc: true
40
+ homepage: http://github.com/attack/spotlight
41
+ post_install_message:
42
+ rdoc_options:
43
+ - --inline-source
44
+ - --charset=UTF-8
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: "0"
52
+ version:
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ version:
59
+ requirements: []
60
+
61
+ rubyforge_project:
62
+ rubygems_version: 1.2.0
63
+ signing_key:
64
+ specification_version: 2
65
+ summary: TODO
66
+ test_files: []
67
+