rmoriz-alexa 0.0.4.1

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Wojciech Wnętrzak
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,16 @@
1
+ = Alexa Web Information Service
2
+
3
+ == Usage
4
+ Alexa.url_info(:access_key_id => 'key', :secret_access_key => 'secret', :host => 'site.com')
5
+ # also you can specify option :response_group => 'Rank,ContactInfo' or any other valid group,
6
+ # see: http://docs.amazonwebservices.com/AlexaWebInfoService/2005-07-11/
7
+ # default response group takes all the available options
8
+
9
+ returns object with methods:
10
+ :xml_response, :rank, :data_url, :site_title, :site_description, :language_locale, :language_encoding,
11
+ :links_in_count, :keywords, :related_links, :speed_median_load_time, :speed_percentile,
12
+ :rank_by_country, :rank_by_city, :usage_statistics
13
+
14
+ == Copyright
15
+
16
+ Copyright (c) 2009 Wojciech Wnętrzak. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,58 @@
1
+ # encoding: UTF-8
2
+ require 'rubygems'
3
+ require 'rake'
4
+
5
+ begin
6
+ require 'jeweler'
7
+ Jeweler::Tasks.new do |gem|
8
+ gem.name = "alexa"
9
+ gem.summary = %Q{Alexa Web Information Service library}
10
+ gem.email = "w.wnetrzak@gmail.com"
11
+ gem.homepage = "http://github.com/morgoth/alexa"
12
+ gem.authors = ["Wojciech Wnętrzak"]
13
+ gem.add_dependency('xml-simple')
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
19
+ end
20
+
21
+ require 'rake/testtask'
22
+ Rake::TestTask.new(:test) do |test|
23
+ test.libs << 'lib' << 'test'
24
+ test.pattern = 'test/**/*_test.rb'
25
+ test.verbose = true
26
+ end
27
+
28
+ begin
29
+ require 'rcov/rcovtask'
30
+ Rcov::RcovTask.new do |test|
31
+ test.libs << 'test'
32
+ test.pattern = 'test/**/*_test.rb'
33
+ test.verbose = true
34
+ end
35
+ rescue LoadError
36
+ task :rcov do
37
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
38
+ end
39
+ end
40
+
41
+
42
+ task :default => :test
43
+
44
+ require 'rake/rdoctask'
45
+ Rake::RDocTask.new do |rdoc|
46
+ if File.exist?('VERSION.yml')
47
+ config = YAML.load(File.read('VERSION.yml'))
48
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
49
+ else
50
+ version = ""
51
+ end
52
+
53
+ rdoc.rdoc_dir = 'rdoc'
54
+ rdoc.title = "alexa #{version}"
55
+ rdoc.rdoc_files.include('README*')
56
+ rdoc.rdoc_files.include('lib/**/*.rb')
57
+ end
58
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.4
data/alexa.gemspec ADDED
@@ -0,0 +1,53 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{alexa}
5
+ s.version = "0.0.4.1"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Wojciech Wnętrzak"]
9
+ s.date = %q{2009-07-07}
10
+ s.email = %q{w.wnetrzak@gmail.com}
11
+ s.extra_rdoc_files = [
12
+ "LICENSE",
13
+ "README.rdoc"
14
+ ]
15
+ s.files = [
16
+ ".document",
17
+ ".gitignore",
18
+ "LICENSE",
19
+ "README.rdoc",
20
+ "Rakefile",
21
+ "VERSION",
22
+ "alexa.gemspec",
23
+ "lib/alexa.rb",
24
+ "lib/alexa/url_info.rb",
25
+ "test/alexa_test.rb",
26
+ "test/fixtures/empty.xml",
27
+ "test/fixtures/polsl.xml",
28
+ "test/fixtures/polsl_small.xml",
29
+ "test/test_helper.rb"
30
+ ]
31
+ s.homepage = %q{http://github.com/morgoth/alexa}
32
+ s.rdoc_options = ["--charset=UTF-8"]
33
+ s.require_paths = ["lib"]
34
+ s.rubygems_version = %q{1.3.4}
35
+ s.summary = %q{Alexa Web Information Service library}
36
+ s.test_files = [
37
+ "test/alexa_test.rb",
38
+ "test/test_helper.rb"
39
+ ]
40
+
41
+ if s.respond_to? :specification_version then
42
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
43
+ s.specification_version = 3
44
+
45
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
46
+ s.add_runtime_dependency(%q<xml-simple>, [">= 0"])
47
+ else
48
+ s.add_dependency(%q<xml-simple>, [">= 0"])
49
+ end
50
+ else
51
+ s.add_dependency(%q<xml-simple>, [">= 0"])
52
+ end
53
+ end
@@ -0,0 +1,95 @@
1
+ module Alexa
2
+ class UrlInfo
3
+ RESPONSE_GROUP = "Rank,ContactInfo,AdultContent,Speed,Language,Keywords,OwnedDomains,LinksInCount,SiteData,RelatedLinks,RankByCountry,RankByCity,UsageStats"
4
+ attr_accessor :access_key_id, :secret_access_key, :host, :response_group, :xml_response,
5
+ :rank, :data_url, :site_title, :site_description, :language_locale, :language_encoding,
6
+ :links_in_count, :keywords, :related_links, :speed_median_load_time, :speed_percentile,
7
+ :rank_by_country, :rank_by_city, :usage_statistics
8
+
9
+ def initialize(options = {} )
10
+ @access_key_id = options[:access_key_id] or raise ArgumentError.new("you must specify access_key_id")
11
+ @secret_access_key = options[:secret_access_key] or raise ArgumentError.new("you must specify secret_access_key")
12
+ @host = options[:host] or raise ArgumentError.new("you must specify host")
13
+ @response_group = options[:response_group] || RESPONSE_GROUP
14
+ end
15
+
16
+ def connect
17
+ action = "UrlInfo"
18
+ timestamp = ( Time::now ).utc.strftime("%Y-%m-%dT%H:%M:%S.000Z")
19
+ signature = generate_signature(secret_access_key, action, timestamp)
20
+ url = generate_url(action, access_key_id, signature, timestamp, response_group, host)
21
+ response = Net::HTTP.start(url.host) do |http|
22
+ http.get url.request_uri
23
+ end
24
+ @xml_response = handle_response(response).body
25
+ end
26
+
27
+ def parse_xml(xml)
28
+ xml = XmlSimple.xml_in(force_encoding(xml), 'ForceArray' => false)
29
+ group = response_group.split(',')
30
+ alexa = xml['Response']['UrlInfoResult']['Alexa']
31
+ @rank = alexa['TrafficData']['Rank'].to_i if group.include?('Rank') and !alexa['TrafficData']['Rank'].empty?
32
+ @data_url = alexa['TrafficData']['DataUrl']['content'] if group.include?('Rank')
33
+ @rank_by_country = alexa['TrafficData']['RankByCountry']['Country'] if group.include?('RankByCountry')
34
+ @rank_by_city = alexa['TrafficData']['RankByCity']['City'] if group.include?('RankByCity')
35
+ @usage_statistics = alexa['TrafficData']['UsageStatistics']["UsageStatistic"] if group.include?('UsageStats') and !alexa['TrafficData']['UsageStatistics'].nil?
36
+
37
+ @site_title = alexa['ContentData']['SiteData']['Title'] if group.include?('SiteData')
38
+ @site_description = alexa['ContentData']['SiteData']['Description'] if group.include?('SiteData')
39
+ @language_locale = alexa['ContentData']['Language']['Locale'] if group.include?('Language')
40
+ @language_encoding = alexa['ContentData']['Language']['Encoding'] if group.include?('Language')
41
+ @links_in_count = alexa['ContentData']['LinksInCount'].to_i if group.include?('LinksInCount') and !alexa['ContentData']['LinksInCount'].empty?
42
+ @keywords = alexa['ContentData']['Keywords']['Keyword'] if group.include?('Keywords')
43
+ @speed_median_load_time = alexa['ContentData']['Speed']['MedianLoadTime'].to_i if group.include?('Speed') and !alexa['ContentData']['Speed']['MedianLoadTime'].empty?
44
+ @speed_percentile = alexa['ContentData']['Speed']['Percentile'].to_i if group.include?('Speed') and !alexa['ContentData']['Speed']['Percentile'].empty?
45
+
46
+ @related_links = alexa['Related']['RelatedLinks']['RelatedLink'] if group.include?('RelatedLinks')
47
+ end
48
+
49
+ private
50
+
51
+ def force_encoding(xml)
52
+ if RUBY_VERSION >= '1.9'
53
+ xml.force_encoding(Encoding::UTF_8)
54
+ else
55
+ xml
56
+ end
57
+ end
58
+
59
+ def handle_response(response)
60
+ case response.code.to_i
61
+ when 200...300
62
+ response
63
+ when 300...600
64
+ if response.body.nil?
65
+ raise StandardError.new(response)
66
+ else
67
+ @xml_response = response.body
68
+ xml = XmlSimple.xml_in(response.body, 'ForceArray' => false)
69
+ message = xml['Errors']['Error']['Message']
70
+ raise StandardError.new(message)
71
+ end
72
+ else
73
+ raise StandardError.new("Unknown code: #{respnse.code}")
74
+ end
75
+ end
76
+
77
+ def generate_signature(secret_acces_key, action, timestamp)
78
+ Base64.encode64( OpenSSL::HMAC.digest( OpenSSL::Digest::Digest.new( "sha1" ), secret_access_key, action + timestamp)).strip
79
+ end
80
+
81
+ def generate_url(action, access_key_id, signature, timestamp, response_group, host)
82
+ url = URI.parse(
83
+ "http://awis.amazonaws.com/?" +
84
+ {
85
+ "Action" => action,
86
+ "AWSAccessKeyId" => access_key_id,
87
+ "Signature" => signature,
88
+ "Timestamp" => timestamp,
89
+ "ResponseGroup" => response_group,
90
+ "Url" => host
91
+ }.to_a.collect{|item| item.first + "=" + CGI::escape(item.last) }.join("&") # Put key value pairs into http GET format
92
+ )
93
+ end
94
+ end
95
+ end
data/lib/alexa.rb ADDED
@@ -0,0 +1,21 @@
1
+ #/usr/bin/ruby
2
+ require "cgi"
3
+ require "base64"
4
+ require "openssl"
5
+ require "digest/sha1"
6
+ require "uri"
7
+ require "net/https"
8
+ require "xmlsimple"
9
+ require "time"
10
+
11
+ require 'alexa/url_info'
12
+
13
+ module Alexa
14
+ def self.url_info(options = {})
15
+ url_info = Alexa::UrlInfo.new(options)
16
+ xml = url_info.connect
17
+ url_info.parse_xml(xml)
18
+ url_info
19
+ end
20
+ end
21
+
@@ -0,0 +1,224 @@
1
+ require 'test_helper'
2
+
3
+ class AlexaTest < Test::Unit::TestCase
4
+ context "Alexa::UrlInfo" do
5
+ setup do
6
+ @alexa = Alexa::UrlInfo.new(
7
+ :access_key_id => "12345678901234567890",
8
+ :secret_access_key => "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDF",
9
+ :host => "some.host"
10
+ )
11
+ end
12
+
13
+ should "Generate signature" do
14
+ signature = @alexa.send :generate_signature, @alexa.secret_access_key, "UrlInfo", '2009-07-03T07:22:24.000Z'
15
+ assert_equal "I1mPdBy+flhhzqqUaamNq9gq190=", signature
16
+ end
17
+
18
+ should "Generate url" do
19
+ url = @alexa.send( :generate_url,
20
+ "UrlInfo",
21
+ @alexa.access_key_id,
22
+ "I1mPdBy+flhhzqqUaamNq9gq190=",
23
+ '2009-07-03T07:22:24.000Z',
24
+ "Rank,ContactInfo,AdultContent,Speed,Language,Keywords,OwnedDomains,LinksInCount,SiteData,RelatedLinks",
25
+ "heroku.com"
26
+ )
27
+ if RUBY_VERSION >= '1.9'
28
+ expected_uri = "/?Action=UrlInfo&AWSAccessKeyId=12345678901234567890&Signature=I1mPdBy%2BflhhzqqUaamNq9gq190%3D&Timestamp=2009-07-03T07%3A22%3A24.000Z&ResponseGroup=Rank%2CContactInfo%2CAdultContent%2CSpeed%2CLanguage%2CKeywords%2COwnedDomains%2CLinksInCount%2CSiteData%2CRelatedLinks&Url=heroku.com"
29
+ else
30
+ expected_uri = "/?Action=UrlInfo&Signature=I1mPdBy%2BflhhzqqUaamNq9gq190%3D&AWSAccessKeyId=12345678901234567890&Url=heroku.com&Timestamp=2009-07-03T07%3A22%3A24.000Z&ResponseGroup=Rank%2CContactInfo%2CAdultContent%2CSpeed%2CLanguage%2CKeywords%2COwnedDomains%2CLinksInCount%2CSiteData%2CRelatedLinks"
31
+ assert_equal expected_uri, url.request_uri
32
+ end
33
+ assert_equal "awis.amazonaws.com", url.host
34
+ end
35
+
36
+ context "should parse xml return by options LinksInCount,SiteData and" do
37
+ setup do
38
+ @alexa.response_group = "Rank,LinksInCount,SiteData"
39
+ xml = fixture_file('polsl_small.xml')
40
+ @alexa.parse_xml(xml)
41
+ end
42
+
43
+ should "return rank" do
44
+ assert_equal 86020, @alexa.rank
45
+ end
46
+
47
+ should "return data url" do
48
+ assert_equal "polsl.pl/", @alexa.data_url
49
+ end
50
+
51
+ should "return site title" do
52
+ assert_equal "Silesian University of Technology", @alexa.site_title
53
+ end
54
+
55
+ should "return site description" do
56
+ assert_equal "About the university, studies, faculties and departments, photo gallery.", @alexa.site_description
57
+ end
58
+
59
+ should "not crash" do
60
+ assert_nothing_raised do
61
+ @alexa.language_locale
62
+ end
63
+ assert_nil @alexa.language_locale
64
+ end
65
+ end
66
+
67
+ context "should parse xml with all options and" do
68
+ setup do
69
+ xml = fixture_file('polsl.xml')
70
+ @alexa.parse_xml(xml)
71
+ end
72
+
73
+ should "return rank" do
74
+ assert_equal 86020, @alexa.rank
75
+ end
76
+
77
+ should "return data url" do
78
+ assert_equal "polsl.pl", @alexa.data_url
79
+ end
80
+
81
+ should "return site title" do
82
+ assert_equal "Silesian University of Technology", @alexa.site_title
83
+ end
84
+
85
+ should "return site description" do
86
+ assert_equal "About the university, studies, faculties and departments, photo gallery.", @alexa.site_description
87
+ end
88
+
89
+ should "return language locale" do
90
+ assert_equal "pl-PL", @alexa.language_locale
91
+ end
92
+
93
+ should "return language encoding" do
94
+ assert_equal "iso-8859-2", @alexa.language_encoding
95
+ end
96
+
97
+ should "return links in count" do
98
+ assert_equal 281, @alexa.links_in_count
99
+ end
100
+
101
+ should "return keywords" do
102
+ assert_equal ["Polska", "Regionalne", "Gliwice"], @alexa.keywords
103
+ end
104
+
105
+ should "return related links" do
106
+ assert_equal 10, @alexa.related_links.count
107
+ end
108
+
109
+ should "return speed_median load time" do
110
+ assert_equal 266, @alexa.speed_median_load_time
111
+ end
112
+
113
+ should "return speed percentile" do
114
+ assert_equal 98, @alexa.speed_percentile
115
+ end
116
+
117
+ should "return rank by country" do
118
+ assert_equal 3, @alexa.rank_by_country.count
119
+ end
120
+
121
+ should "return rank by city" do
122
+ assert_equal 68, @alexa.rank_by_city.count
123
+ end
124
+
125
+ should "return usage statistics" do
126
+ assert_equal 4, @alexa.usage_statistics.count
127
+ end
128
+
129
+ end
130
+
131
+ context "should not crash when parsing empty xml response and" do
132
+ setup do
133
+ xml = fixture_file('empty.xml')
134
+ @alexa.parse_xml(xml)
135
+ end
136
+
137
+ should "return nil" do
138
+ assert_nil @alexa.rank
139
+ end
140
+
141
+ should "return nil" do
142
+ assert_nil @alexa.data_url
143
+ end
144
+
145
+ should "return nil" do
146
+ assert_nil @alexa.site_title
147
+ end
148
+
149
+ should "return nil" do
150
+ assert_nil @alexa.site_description
151
+ end
152
+
153
+ should "return nil" do
154
+ assert_nil @alexa.language_locale
155
+ end
156
+
157
+ should "return nil" do
158
+ assert_nil @alexa.language_encoding
159
+ end
160
+
161
+ should "return nil" do
162
+ assert_nil @alexa.links_in_count
163
+ end
164
+
165
+ should "return nil" do
166
+ assert_nil @alexa.keywords
167
+ end
168
+
169
+ should "return nil" do
170
+ assert_nil @alexa.related_links
171
+ end
172
+
173
+ should "return nil" do
174
+ assert_nil @alexa.speed_median_load_time
175
+ end
176
+
177
+ should "return nil" do
178
+ assert_nil @alexa.speed_percentile
179
+ end
180
+
181
+ should "return nil" do
182
+ assert_nil @alexa.rank_by_country
183
+ end
184
+
185
+ should "return nil" do
186
+ assert_nil @alexa.rank_by_city
187
+ end
188
+
189
+ should "return nil" do
190
+ assert_nil @alexa.usage_statistics
191
+ end
192
+
193
+ end
194
+
195
+ should "not raise error when response is OK" do
196
+ assert_nothing_raised do
197
+ @alexa.send :handle_response, Net::HTTPOK.new("1.1", "200", "OK")
198
+ end
199
+ end
200
+ end
201
+
202
+ should "Raise argumment error if keys or host are not present" do
203
+ assert_raise ArgumentError do
204
+ Alexa::UrlInfo.new(
205
+ :secret_access_key => "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDF",
206
+ :host => "some.host"
207
+ )
208
+ end
209
+
210
+ assert_raise ArgumentError do
211
+ Alexa::UrlInfo.new(
212
+ :access_key_id => "12345678901234567890",
213
+ :host => "some.host"
214
+ )
215
+ end
216
+
217
+ assert_raise ArgumentError do
218
+ Alexa::UrlInfo.new(
219
+ :access_key_id => "12345678901234567890",
220
+ :secret_access_key => "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDF"
221
+ )
222
+ end
223
+ end
224
+ end
@@ -0,0 +1,39 @@
1
+ <?xml version="1.0"?>
2
+ <aws:UrlInfoResponse xmlns:aws="http://alexa.amazonaws.com/doc/2005-10-05/"><aws:Response xmlns:aws="http://awis.amazonaws.com/doc/2005-07-11"><aws:OperationRequest><aws:RequestId>6a12c8f4-056f-4fce-95cd-2f9d2a353236</aws:RequestId></aws:OperationRequest><aws:UrlInfoResult><aws:Alexa>
3
+
4
+ <aws:ContactInfo>
5
+ <aws:DataUrl type="canonical">404</aws:DataUrl>
6
+ <aws:PhoneNumbers>
7
+ <aws:PhoneNumber/>
8
+ </aws:PhoneNumbers>
9
+ <aws:OwnerName/>
10
+ <aws:Email/>
11
+ <aws:PhysicalAddress/>
12
+ <aws:CompanyStockTicker/>
13
+ </aws:ContactInfo>
14
+ <aws:ContentData>
15
+ <aws:DataUrl type="canonical">404</aws:DataUrl>
16
+ <aws:SiteData>
17
+ <aws:Title>404</aws:Title>
18
+ </aws:SiteData>
19
+ <aws:Speed>
20
+ <aws:MedianLoadTime/>
21
+ <aws:Percentile/>
22
+ </aws:Speed>
23
+ <aws:AdultContent/>
24
+ <aws:Language/>
25
+ <aws:LinksInCount/>
26
+ <aws:Keywords/>
27
+ <aws:OwnedDomains/>
28
+ </aws:ContentData>
29
+ <aws:Related>
30
+ <aws:DataUrl type="canonical">404</aws:DataUrl>
31
+ <aws:RelatedLinks/>
32
+ </aws:Related>
33
+ <aws:TrafficData>
34
+ <aws:DataUrl type="canonical">404</aws:DataUrl>
35
+ <aws:Rank/>
36
+ <aws:RankByCountry/>
37
+ <aws:RankByCity/>
38
+ </aws:TrafficData>
39
+ </aws:Alexa></aws:UrlInfoResult><aws:ResponseStatus xmlns:aws="http://alexa.amazonaws.com/doc/2005-10-05/"><aws:StatusCode>Success</aws:StatusCode></aws:ResponseStatus></aws:Response></aws:UrlInfoResponse>