attack-spotlight 0.1.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.
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
+