elcamino-alexa 0.1.0

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
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+ gem 'rest-client', ">= 1.0"
6
+ gem 'nokogiri'
7
+
8
+ # Add dependencies to develop your gem here.
9
+ # Include everything needed to run rake, tests, features, etc.
10
+ group :development do
11
+ gem "shoulda", ">= 0"
12
+ gem "bundler", "~> 1.0.0"
13
+ gem "jeweler", "~> 1.6.4"
14
+ gem "rcov", ">= 0"
15
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Tobias Begalke
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,47 @@
1
+ = elcamino-alexa
2
+
3
+ A ruby library for the Amazon Alexa web API.
4
+
5
+ Currently, the methods url_info, traffic_history and sites_linking_in are
6
+ implemented.
7
+
8
+ == Hello World
9
+
10
+ Use RubyGems to install the library:
11
+
12
+ $ gem install elcamino-alexa
13
+
14
+ Provide your API Credentials:
15
+
16
+ require 'alexa'
17
+
18
+ Alexa.config do |c|
19
+ c.access_key_id = YOUR_API_ACCESS_KEY
20
+ c.secret_access_key = YOUR_SECRET_ACCESS_KEY
21
+ end
22
+
23
+ ui = Alexa::UrlInfo.new
24
+ data = ui.fetch('Url' => 'apple.com')
25
+
26
+ li = Alexa::SitesLinkingIn.new
27
+ data = ui.fetch('Url' => 'apple.com')
28
+
29
+ th = Alexa::TrafficHistory.new
30
+ data = th.fetch('Url' => 'apple.com')
31
+
32
+
33
+ == Contributing to alexa
34
+
35
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
36
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
37
+ * Fork the project
38
+ * Start a feature/bugfix branch
39
+ * Commit and push until you are happy with your contribution
40
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
41
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
42
+
43
+ == Copyright
44
+
45
+ Copyright (c) 2011 Tobias Begalke. See LICENSE.txt for
46
+ further details.
47
+
data/Rakefile ADDED
@@ -0,0 +1,53 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "elcamino-alexa"
18
+ gem.homepage = "http://github.com/elcamino/alexa"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Alexa Web API ruby library}
21
+ gem.description = %Q{Ruby library for the Amazon Alexa web API}
22
+ gem.email = "elcamino@spyz.org"
23
+ gem.authors = ["Tobias Begalke"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rake/testtask'
29
+ Rake::TestTask.new(:test) do |test|
30
+ test.libs << 'lib' << 'test'
31
+ test.pattern = 'test/**/test_*.rb'
32
+ test.verbose = true
33
+ end
34
+
35
+ require 'rcov/rcovtask'
36
+ Rcov::RcovTask.new do |test|
37
+ test.libs << 'test'
38
+ test.pattern = 'test/**/test_*.rb'
39
+ test.verbose = true
40
+ test.rcov_opts << '--exclude "gems/*"'
41
+ end
42
+
43
+ task :default => :test
44
+
45
+ require 'rake/rdoctask'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "alexa #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,78 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{elcamino-alexa}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Tobias Begalke"]
12
+ s.date = %q{2011-09-02}
13
+ s.description = %q{Ruby library for the Amazon Alexa web API}
14
+ s.email = %q{elcamino@spyz.org}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ "Gemfile",
22
+ "LICENSE.txt",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "elcamino-alexa.gemspec",
27
+ "lib/alexa.rb",
28
+ "lib/alexa/config.rb",
29
+ "lib/alexa/container.rb",
30
+ "lib/alexa/sites_linking_in.rb",
31
+ "lib/alexa/sites_linking_in/site.rb",
32
+ "lib/alexa/traffic_history.rb",
33
+ "lib/alexa/traffic_history/historical_data.rb",
34
+ "lib/alexa/url_info.rb",
35
+ "lib/alexa/url_info/categories.rb",
36
+ "lib/alexa/url_info/contact_info.rb",
37
+ "lib/alexa/url_info/content_data.rb",
38
+ "lib/alexa/url_info/contributing_subdomains.rb",
39
+ "lib/alexa/url_info/related.rb",
40
+ "lib/alexa/url_info/traffic_data.rb",
41
+ "lib/alexa/url_info/usage.rb",
42
+ "test/helper.rb",
43
+ "test/test_alexa.rb"
44
+ ]
45
+ s.homepage = %q{http://github.com/elcamino/alexa}
46
+ s.licenses = ["MIT"]
47
+ s.require_paths = ["lib"]
48
+ s.rubygems_version = %q{1.6.2}
49
+ s.summary = %q{Alexa Web API ruby library}
50
+
51
+ if s.respond_to? :specification_version then
52
+ s.specification_version = 3
53
+
54
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
55
+ s.add_runtime_dependency(%q<rest-client>, [">= 1.0"])
56
+ s.add_runtime_dependency(%q<nokogiri>, [">= 0"])
57
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
58
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
59
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
60
+ s.add_development_dependency(%q<rcov>, [">= 0"])
61
+ else
62
+ s.add_dependency(%q<rest-client>, [">= 1.0"])
63
+ s.add_dependency(%q<nokogiri>, [">= 0"])
64
+ s.add_dependency(%q<shoulda>, [">= 0"])
65
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
66
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
67
+ s.add_dependency(%q<rcov>, [">= 0"])
68
+ end
69
+ else
70
+ s.add_dependency(%q<rest-client>, [">= 1.0"])
71
+ s.add_dependency(%q<nokogiri>, [">= 0"])
72
+ s.add_dependency(%q<shoulda>, [">= 0"])
73
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
74
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
75
+ s.add_dependency(%q<rcov>, [">= 0"])
76
+ end
77
+ end
78
+
@@ -0,0 +1,15 @@
1
+ require "singleton"
2
+
3
+ module Alexa
4
+ class Config
5
+ include Singleton
6
+ attr_accessor :access_key_id, :secret_access_key, :proxy
7
+ end
8
+
9
+ def self.config
10
+ if block_given?
11
+ yield Config.instance
12
+ end
13
+ Config.instance
14
+ end
15
+ end
@@ -0,0 +1,54 @@
1
+ module Alexa
2
+ module Container
3
+
4
+ attr_accessor :data
5
+
6
+ def keys
7
+ @data.keys
8
+ end
9
+
10
+ def to_mongo
11
+ self.class.to_mongo(self)
12
+ end
13
+
14
+ def method_missing(name)
15
+ myname = ActiveSupport::Inflector.underscore(name).to_sym
16
+ raise Exception.new("there is no data field called \"#{name}\" here!") unless @data.has_key?(myname) || @data.has_key?(myname.to_s)
17
+
18
+ if @data.has_key?(myname)
19
+ return @data[myname]
20
+ end
21
+
22
+ if @data.has_key?(myname.to_s)
23
+ return @data[myname.to_s]
24
+ end
25
+
26
+ end
27
+
28
+ module ClassMethods
29
+
30
+ def to_mongo(value)
31
+ return nil if value.nil?
32
+ return value if value.is_a?(Hash)
33
+
34
+ value.data
35
+ end
36
+
37
+ def from_mongo(value)
38
+ return nil if value.nil?
39
+ return value if value.is_a?(self)
40
+
41
+ o = self.new
42
+ o.data = value
43
+
44
+ return o
45
+ end
46
+ end
47
+
48
+
49
+ def self.included(base)
50
+ base.extend(ClassMethods)
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,17 @@
1
+ module Alexa
2
+ class SitesLinkingIn
3
+ class Site
4
+ include Alexa::Container
5
+
6
+ def initialize(xml_node = nil)
7
+ @data = {}
8
+
9
+ if ! xml_node.nil?
10
+ @data[:title] = xml_node.xpath('Title').text.strip
11
+ @data[:url] = xml_node.xpath('Url').text.strip
12
+ end
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,98 @@
1
+
2
+ module Alexa
3
+
4
+ require 'nokogiri'
5
+ require 'alexa/container'
6
+ require 'alexa/sites_linking_in/site'
7
+
8
+ class SitesLinkingIn
9
+
10
+ attr_reader :links
11
+
12
+ def initialize(data = { })
13
+ @options = {
14
+ 'Action' => 'SitesLinkingIn',
15
+ 'AWSAccessKeyId' => Alexa.config.access_key_id,
16
+ 'Timestamp' => ( Time::now ).utc.strftime("%Y-%m-%dT%H:%M:%S.000Z"),
17
+ 'ResponseGroup' => 'SitesLinkingIn',
18
+ 'SignatureVersion' => 2,
19
+ 'SignatureMethod' => 'HmacSHA256',
20
+ 'Version' => '2005-07-11',
21
+ 'Count' => nil,
22
+ 'Start' => nil,
23
+ 'Url' => nil,
24
+ }
25
+
26
+ if data['links']
27
+ @links = data['links']
28
+ end
29
+
30
+ if Alexa.config.proxy
31
+ RestClient.proxy = Alexa.config.proxy
32
+ end
33
+ end
34
+
35
+ def signature
36
+ Alexa.sign(@options)
37
+ end
38
+
39
+ def url
40
+ qs = Alexa.query_string(@options)
41
+ 'http://' + ::Alexa::SERVICE_HOST + '/?' + qs + '&Signature=' + signature
42
+ end
43
+
44
+ def fetch(options = {})
45
+ @options.merge!(options)
46
+ @options.delete_if { |k,v| v.nil? }
47
+
48
+ raise ArgumentException.new('Url field is empty!') unless @options['Url']
49
+
50
+ response = RestClient.get(url)
51
+ data = Nokogiri::XML(response.to_s)
52
+ data.remove_namespaces!
53
+
54
+ @links = []
55
+
56
+ data.xpath('//SitesLinkingIn/Site').each do |s|
57
+ @links << ::Alexa::SitesLinkingIn::Site.new(s)
58
+ end
59
+
60
+ self
61
+ end
62
+
63
+ def self.to_mongo(value)
64
+ return nil if value.nil?
65
+ return value if value.is_a? Hash
66
+
67
+ return {
68
+ 'links' => value.links.map { |l| SitesLinkingIn::Site.to_mongo(l) },
69
+ }
70
+ end
71
+
72
+ def self.from_mongo(value)
73
+ return nil if value.nil?
74
+ return value if value.is_a?(self)
75
+
76
+ links = []
77
+ value['links'].each do |l|
78
+ links << SitesLinkingIn::Site.from_mongo(l)
79
+ end
80
+
81
+ self.new('links' => links)
82
+ end
83
+
84
+ def to_mongo
85
+ self.class.to_mongo(self)
86
+ end
87
+
88
+
89
+ #
90
+ # Exceptions
91
+ #
92
+
93
+ class ArgumentException < Exception
94
+ end
95
+ end
96
+
97
+
98
+ end
@@ -0,0 +1,30 @@
1
+ module Alexa
2
+ class TrafficHistory
3
+ class HistoricalData
4
+ require 'alexa/container'
5
+
6
+ include Alexa::Container
7
+
8
+ def initialize(xml_node = nil)
9
+ @data = {}
10
+
11
+ if ! xml_node.nil?
12
+ xml_node.xpath('Data').each do |d|
13
+ day = d.xpath('Date').text.strip
14
+
15
+ ddata = {}
16
+ ddata[:date] = day
17
+ ddata[:pageviews_permillion] = d.xpath('PageViews/PerMillion').text.strip
18
+ ddata[:pageviews_peruser] = d.xpath('PageViews/PerUser').text.strip
19
+ ddata[:rank] = d.xpath('Rank').text.strip
20
+ ddata[:reach_permillion] = d.xpath('Reach/PerMillion').text.strip
21
+
22
+ @data[day] = ddata
23
+ end
24
+ end
25
+
26
+ end
27
+ end
28
+ end
29
+ end
30
+
@@ -0,0 +1,109 @@
1
+
2
+ module Alexa
3
+
4
+ require 'nokogiri'
5
+ # require 'alexa/container'
6
+ require 'alexa/traffic_history/historical_data'
7
+
8
+ class TrafficHistory
9
+
10
+ attr_reader :historical_data, :range, :site, :start
11
+
12
+ def initialize(data = {})
13
+ @options = {
14
+ 'Action' => 'TrafficHistory',
15
+ 'AWSAccessKeyId' => Alexa.config.access_key_id,
16
+ 'Timestamp' => ( Time::now ).utc.strftime("%Y-%m-%dT%H:%M:%S.000Z"),
17
+ 'ResponseGroup' => 'History',
18
+ 'SignatureVersion' => 2,
19
+ 'SignatureMethod' => 'HmacSHA256',
20
+ 'Version' => '2005-07-11',
21
+ 'Range' => nil,
22
+ 'StartDate' => nil,
23
+ 'Url' => nil,
24
+ }
25
+
26
+ if data['historical_data']
27
+ @historical_data = data['historical_data']
28
+ end
29
+
30
+ if data['range']
31
+ @range = data['range']
32
+ end
33
+
34
+ if data['site']
35
+ @site = data['site']
36
+ end
37
+
38
+ if data['start']
39
+ @start = data['start']
40
+ end
41
+
42
+ if Alexa.config.proxy
43
+ RestClient.proxy = Alexa.config.proxy
44
+ end
45
+ end
46
+
47
+ def signature
48
+ Alexa.sign(@options)
49
+ end
50
+
51
+ def url
52
+ qs = Alexa.query_string(@options)
53
+ 'http://' + ::Alexa::SERVICE_HOST + '/?' + qs + '&Signature=' + signature
54
+ end
55
+
56
+ def fetch(options = {})
57
+ @options.merge!(options)
58
+ @options.delete_if { |k,v| v.nil? }
59
+
60
+ raise ArgumentException.new('Url field is empty!') unless @options['Url']
61
+
62
+ response = RestClient.get(url)
63
+ data = Nokogiri::XML(response.to_s)
64
+ data.remove_namespaces!
65
+
66
+ @range = data.xpath('//TrafficHistory/Range').first.text.strip
67
+ @site = data.xpath('//TrafficHistory/Site').first.text.strip
68
+ @start = data.xpath('//TrafficHistory/Start').first.text.strip
69
+ @historical_data = HistoricalData.new(data.xpath('//HistoricalData').first)
70
+
71
+ self
72
+ end
73
+
74
+ def self.to_mongo(value)
75
+ return nil if value.nil?
76
+ return value if value.is_a? Hash
77
+
78
+ return {
79
+ 'range' => value.range.to_s,
80
+ 'site' => value.site.to_s,
81
+ 'start' => value.start.to_s,
82
+ 'historical_data' => value.historical_data.to_mongo,
83
+ }
84
+ end
85
+
86
+ def self.from_mongo(value)
87
+ return nil if value.nil?
88
+ return value if value.is_a?(self)
89
+
90
+ self.new('range' => value['range'],
91
+ 'site' => value['site'],
92
+ 'start' => value['start'],
93
+ 'historical_data' => HistoricalData.from_mongo(value['historical_data']))
94
+ end
95
+
96
+ def to_mongo
97
+ self.class.to_mongo(self)
98
+ end
99
+
100
+ #
101
+ # Exceptions
102
+ #
103
+
104
+ class ArgumentException < Exception
105
+ end
106
+ end
107
+
108
+
109
+ end
@@ -0,0 +1,23 @@
1
+ module Alexa
2
+ class UrlInfo
3
+ class Categories
4
+ include Alexa::Container
5
+
6
+ def initialize(xml_node = nil)
7
+ @data = {}
8
+ @data[:categories] = []
9
+
10
+ if xml_node != nil
11
+ xml_node.xpath('CategoryData').each do |c|
12
+ @data[:categories] << {
13
+ :title => c.xpath('Title').text.strip,
14
+ :absolute_path => c.xpath('AbsolutePath').text.strip,
15
+ }
16
+ end
17
+ end
18
+
19
+ end
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,32 @@
1
+ module Alexa
2
+ class UrlInfo
3
+ class ContactInfo
4
+ include Alexa::Container
5
+
6
+ def initialize(xml_node = nil)
7
+ @data = {}
8
+
9
+ if ! xml_node.nil?
10
+ @data[:data_url] = xml_node.xpath('DataUrl').text.strip
11
+ @data[:phone_numbers] = []
12
+ xml_node.xpath('PhoneNumbers/PhoneNumber').each do |p|
13
+ @data[:phone_numbers] << p.text.strip
14
+ end
15
+ @data[:owner_name] = xml_node.xpath('OwnerName').text.strip
16
+ @data[:email] = xml_node.xpath('Email').text.strip
17
+ @data[:streets] = []
18
+ xml_node.xpath('PhysicalAddress/Streets/Street').each do |s|
19
+ @data[:streets] << s.text.strip
20
+ end
21
+ @data[:city] = xml_node.xpath('PhysicalAddress/City').text.strip
22
+ @data[:state] = xml_node.xpath('PhysicalAddress/State').text.strip
23
+ @data[:postal_code] = xml_node.xpath('PhysicalAddress/PostalCode').text.strip
24
+ @data[:country] = xml_node.xpath('PhysicalAddress/Country').text.strip
25
+ @data[:stock_symbol] = xml_node.xpath('CompanyStockTicker/Symbol').text.strip
26
+ end
27
+ end
28
+
29
+ end
30
+ end
31
+ end
32
+
@@ -0,0 +1,41 @@
1
+ module Alexa
2
+ class UrlInfo
3
+ class ContentData
4
+ include Alexa::Container
5
+
6
+ attr_accessor :data
7
+
8
+ def initialize(xml_node = nil)
9
+ @data = {}
10
+
11
+ if !xml_node.nil?
12
+ @data[:data_url] = xml_node.xpath('DataUrl').text.strip
13
+ @data[:title] = xml_node.xpath('SiteData/Title').text.strip
14
+ @data[:description] = xml_node.xpath('SiteData/Description').text.strip
15
+ @data[:online_since] = xml_node.xpath('SiteData/OnlineSince').text.strip
16
+ @data[:median_load_time] = xml_node.xpath('Speed/MedianLoadTime').text.strip
17
+ @data[:load_percentile] = xml_node.xpath('Speed/Percentile').text.strip
18
+ @data[:adult_content] = xml_node.xpath('AdultContent').text.strip
19
+ @data[:language] = xml_node.xpath('Language/Locale').text.strip
20
+ @data[:links_in_count] = xml_node.xpath('LinksInCount').text.strip
21
+
22
+ @data[:keywords] = []
23
+ xml_node.xpath('Keywords/Keyword').each do |kw|
24
+ @data[:keywords] << kw.text.strip
25
+ end
26
+
27
+ @data[:owned_domains] = []
28
+ xml_node.xpath('OwnedDomains/OwnedDomain').each do |d|
29
+ @data[:owned_domains] << {
30
+ :domain => d.xpath('Domain').text.strip,
31
+ :title => d.xpath('Title').text.strip
32
+ }
33
+ end
34
+ end
35
+
36
+ end
37
+
38
+ end
39
+ end
40
+ end
41
+
@@ -0,0 +1,31 @@
1
+ module Alexa
2
+ class UrlInfo
3
+ class ContributingSubdomains
4
+ include Alexa::Container
5
+
6
+ def initialize(xml_node = nil)
7
+ @data = {}
8
+
9
+ if !xml_node.nil?
10
+ xml_node.xpath('ContributingSubdomain').each do |cs|
11
+ range_node = cs.xpath('TimeRange').children.find_all { |c| c.name != 'text' }.first
12
+
13
+ domain = cs.xpath('DataUrl').text.strip
14
+
15
+ ddata = {}
16
+
17
+ ddata[:data_url] = domain
18
+ ddata[:range] = range_node.text.strip + ' ' + range_node.name.strip
19
+ ddata[:reach_percentage] = cs.xpath('Reach/Percentage').text.strip
20
+ ddata[:pageviews_percentage] = cs.xpath('PageViews/Percentage').text.strip
21
+ ddata[:pageviews_peruser] = cs.xpath('PageViews/PerUser').text.strip
22
+
23
+ @data[domain] = ddata
24
+ end
25
+ end
26
+
27
+ end
28
+ end
29
+ end
30
+ end
31
+
@@ -0,0 +1,27 @@
1
+ module Alexa
2
+ class UrlInfo
3
+ class Related
4
+ include Alexa::Container
5
+
6
+ def initialize(xml_node = nil)
7
+ @data = {}
8
+
9
+ if !xml_node.nil?
10
+
11
+ @data[:data_url] = xml_node.xpath('DataUrl').text.strip
12
+ @data[:links] = []
13
+
14
+ xml_node.xpath('RelatedLinks/RelatedLink').each do |l|
15
+ @data[:links] << {
16
+ :data_url => l.xpath('DataUrl').text.strip,
17
+ :navigable_url => l.xpath('NavigableUrl').text.strip,
18
+ :title => l.xpath('Title').text.strip,
19
+ }
20
+ end
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+ end
27
+
@@ -0,0 +1,42 @@
1
+ module Alexa
2
+ class UrlInfo
3
+ class TrafficData
4
+ include Alexa::Container
5
+
6
+ def initialize(xml_node = nil)
7
+ @data = {}
8
+
9
+ if !xml_node.nil?
10
+ @data[:data_url] = xml_node.xpath('DataUrl').text.strip
11
+ @data[:rank] = xml_node.xpath('Rank').text.strip
12
+
13
+ @data[:by_country] = []
14
+ xml_node.xpath('RankByCountry/Country').each do |r|
15
+ @data[:by_country] << {
16
+ :country => r['Code'],
17
+ :rank => r.xpath('Rank').text.strip,
18
+ :page_views => r.xpath('Contribution/PageViews').text.strip,
19
+ :users => r.xpath('Contribution/Users').text.strip,
20
+ }
21
+ end
22
+
23
+ @data[:by_city] = []
24
+ xml_node.xpath('RankByCity/City').each do |c|
25
+ @data[:by_city] << {
26
+ :code => c['Code'],
27
+ :name => c['Name'],
28
+ :rank => c.xpath('Rank').text.strip,
29
+ :page_views => c.xpath('Contribution/PageViews').text.strip,
30
+ :users => c.xpath('Contribution/Users').text.strip,
31
+ :average_page_views => c.xpath('Contribution/PerUser/AveragePageViews').text.strip,
32
+ }
33
+ end
34
+
35
+ @data[:usage] = []
36
+ end
37
+
38
+ end
39
+ end
40
+ end
41
+ end
42
+
@@ -0,0 +1,35 @@
1
+ module Alexa
2
+ class UrlInfo
3
+ class Usage
4
+ include Alexa::Container
5
+
6
+ def initialize(xml_node = nil)
7
+ @data = {}
8
+
9
+ if !xml_node.nil?
10
+ xml_node.xpath('//UsageStatistic').each do |s|
11
+ range_node = s.xpath('TimeRange').children.find_all { |c| c.name != 'text' }.first
12
+ key = range_node.text.strip + ' ' + range_node.name.strip
13
+
14
+ stats = {}
15
+ stats[:rank_value] = s.xpath('Rank/Value').text.strip
16
+ stats[:rank_delta] = s.xpath('Rank/Delta').text.strip
17
+ stats[:reach_rank_value] = s.xpath('Reach/Rank/Value').text.strip
18
+ stats[:reach_rank_delta] = s.xpath('Reach/Rank/Delta').text.strip
19
+ stats[:reach_permillion_value] = s.xpath('Reach/PerMillion/Value').text.strip
20
+ stats[:reach_permillion_delta] = s.xpath('Reach/PerMillion/Delta').text.strip
21
+ stats[:pageviews_permillion_value] = s.xpath('PageViews/PerMillion/Value').text.strip
22
+ stats[:pageviews_permillion_delta] = s.xpath('PageViews/PerMillion/Delta').text.strip
23
+ stats[:pageviews_rank_value] = s.xpath('PageViews/Rank/Value').text.strip
24
+ stats[:pageviews_rank_delta] = s.xpath('PageViews/Rank/Delta').text.strip
25
+ stats[:pageviews_peruser_value] = s.xpath('PageViews/PerUser/Value').text.strip
26
+ stats[:pageviews_peruser_delta] = s.xpath('PageViews/PerUser/Delta').text.strip
27
+
28
+ @data[key] = stats
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+
@@ -0,0 +1,131 @@
1
+
2
+ module Alexa
3
+
4
+ require 'nokogiri'
5
+ require 'alexa/container'
6
+ require 'alexa/url_info/contact_info'
7
+ require 'alexa/url_info/content_data'
8
+ require 'alexa/url_info/related'
9
+ require 'alexa/url_info/categories'
10
+ require 'alexa/url_info/traffic_data'
11
+ require 'alexa/url_info/usage'
12
+ require 'alexa/url_info/contributing_subdomains'
13
+
14
+ class UrlInfo
15
+
16
+
17
+ attr_reader :contact_info, :content_data, :related, :traffic_data, :usage, :contributing_subdomains, :categories
18
+
19
+ def initialize(data = { })
20
+ @options = {
21
+ 'Action' => 'UrlInfo',
22
+ 'AWSAccessKeyId' => Alexa.config.access_key_id,
23
+ 'Timestamp' => Time.now.utc.strftime("%Y-%m-%dT%H:%M:%S.000Z"),
24
+ 'ResponseGroup' => 'RelatedLinks,Categories,Rank,RankByCountry,RankByCity,UsageStats,ContactInfo,ContactInfo,AdultContent,Speed,Language,Keywords,OwnedDomains,LinksInCount,SiteData',
25
+ 'SignatureVersion' => 2,
26
+ 'SignatureMethod' => 'HmacSHA256',
27
+ 'Version' => '2005-07-11',
28
+ 'Url' => nil,
29
+ }
30
+
31
+ if data['contact_info']
32
+ @contact_info = data['contact_info']
33
+ end
34
+
35
+ if data['content_data']
36
+ @content_data = data['content_data']
37
+ end
38
+
39
+ if data['related']
40
+ @related = data['related']
41
+ end
42
+
43
+ if data['traffic_data']
44
+ @traffic_data = data['traffic_data']
45
+ end
46
+
47
+ if data['usage']
48
+ @usage = data['usage']
49
+ end
50
+
51
+ if data['contributing_subdomains']
52
+ @contributing_subdomains = data['contributing_subdomains']
53
+ end
54
+
55
+ if data['categories']
56
+ @categories = data['categories']
57
+ end
58
+
59
+ if Alexa.config.proxy
60
+ RestClient.proxy = Alexa.config.proxy
61
+ end
62
+ end
63
+
64
+ def signature
65
+ Alexa.sign(@options)
66
+ end
67
+
68
+ def url
69
+ qs = Alexa.query_string(@options)
70
+ sign = signature
71
+ 'http://' + ::Alexa::SERVICE_HOST + '/?' + qs + '&Signature=' + sign
72
+ end
73
+
74
+ def fetch(options = {})
75
+ @options.merge!(options)
76
+ @options.delete_if { |k,v| v.nil? }
77
+
78
+ response = RestClient.get(url)
79
+ data = Nokogiri::XML(response.to_s)
80
+ data.remove_namespaces!
81
+
82
+ @contact_info = ContactInfo.new(data.xpath('//ContactInfo').first)
83
+ @content_data = ContentData.new(data.xpath('//ContentData').first)
84
+ @related = Related.new(data.xpath('//Related').first)
85
+ @categories = Categories.new(data.xpath('//Categories').first)
86
+ @traffic_data = TrafficData.new(data.xpath('//TrafficData').first)
87
+ @usage = Usage.new(data.xpath('//UsageStatistics').first)
88
+ @contributing_subdomains = ContributingSubdomains.new(data.xpath('//ContributingSubdomains').first)
89
+
90
+ self
91
+ end
92
+
93
+ def self.to_mongo(value)
94
+ return nil if value.nil?
95
+ return value if value.is_a? Hash
96
+
97
+ return {
98
+ 'contact_info' => ContactInfo.to_mongo(value.contact_info),
99
+ 'content_data' => ContentData.to_mongo(value.content_data),
100
+ 'related' => Related.to_mongo(value.related),
101
+ 'categories' => Categories.to_mongo(value.categories),
102
+ 'usage' => Usage.to_mongo(value.usage),
103
+ 'contributing_subdomains' => ContributingSubdomains.to_mongo(value.contributing_subdomains),
104
+ }
105
+ end
106
+
107
+ def self.from_mongo(value)
108
+ return nil if value.nil?
109
+ return value if value.is_a?(self)
110
+
111
+ self.new(
112
+ 'contact_info' => ContactInfo.from_mongo(value['contact_info']),
113
+ 'content_data' => ContentData.from_mongo(value['content_data']),
114
+ 'related' => Related.from_mongo(value['related']),
115
+ 'categories' => Categories.from_mongo(value['categories']),
116
+ 'traffic_data' => TrafficData.from_mongo(value['traffic_data']),
117
+ 'usage' => Usage.from_mongo(value['usage']),
118
+ 'contributing_subdomains' => ContributingSubdomains.from_mongo(value['contributing_subdomains'])
119
+ )
120
+ end
121
+
122
+ #
123
+ # Exceptions
124
+ #
125
+
126
+ class ArgumentException < Exception
127
+ end
128
+ end
129
+
130
+
131
+ end
data/lib/alexa.rb ADDED
@@ -0,0 +1,29 @@
1
+
2
+
3
+ module Alexa
4
+ require 'uri'
5
+ require 'base64'
6
+ require 'openssl'
7
+ require 'rest-client'
8
+
9
+ require 'alexa/config'
10
+ require 'alexa/url_info'
11
+ require 'alexa/traffic_history'
12
+ require 'alexa/sites_linking_in'
13
+
14
+ SERVICE_HOST = 'awis.amazonaws.com'
15
+
16
+ def self.uri_escape(input)
17
+ URI.escape(input, /[^A-Za-z0-9\-_.~]/)
18
+ end
19
+
20
+ def self.query_string(options)
21
+ options.keys.sort.map { |k| k + '=' + self.uri_escape(options[k].to_s) }.join('&')
22
+ end
23
+
24
+ def self.sign(options)
25
+ sign_str = "GET\n" + SERVICE_HOST + "\n/\n" + query_string(options)
26
+ self.uri_escape(Base64.encode64( OpenSSL::HMAC.digest( OpenSSL::Digest::Digest.new( "sha256" ), Alexa.config.secret_access_key, sign_str)).strip)
27
+ end
28
+
29
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+ require 'shoulda'
12
+
13
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
15
+ require 'alexa'
16
+
17
+ class Test::Unit::TestCase
18
+ end
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestAlexa < Test::Unit::TestCase
4
+ def test_notest
5
+ true
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,148 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: elcamino-alexa
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.0
6
+ platform: ruby
7
+ authors:
8
+ - Tobias Begalke
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-09-02 00:00:00 +02:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: rest-client
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "1.0"
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: nokogiri
29
+ requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: "0"
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: shoulda
40
+ requirement: &id003 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: "0"
46
+ type: :development
47
+ prerelease: false
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: bundler
51
+ requirement: &id004 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ~>
55
+ - !ruby/object:Gem::Version
56
+ version: 1.0.0
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: *id004
60
+ - !ruby/object:Gem::Dependency
61
+ name: jeweler
62
+ requirement: &id005 !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ~>
66
+ - !ruby/object:Gem::Version
67
+ version: 1.6.4
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: *id005
71
+ - !ruby/object:Gem::Dependency
72
+ name: rcov
73
+ requirement: &id006 !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: "0"
79
+ type: :development
80
+ prerelease: false
81
+ version_requirements: *id006
82
+ description: Ruby library for the Amazon Alexa web API
83
+ email: elcamino@spyz.org
84
+ executables: []
85
+
86
+ extensions: []
87
+
88
+ extra_rdoc_files:
89
+ - LICENSE.txt
90
+ - README.rdoc
91
+ files:
92
+ - .document
93
+ - Gemfile
94
+ - LICENSE.txt
95
+ - README.rdoc
96
+ - Rakefile
97
+ - VERSION
98
+ - elcamino-alexa.gemspec
99
+ - lib/alexa.rb
100
+ - lib/alexa/config.rb
101
+ - lib/alexa/container.rb
102
+ - lib/alexa/sites_linking_in.rb
103
+ - lib/alexa/sites_linking_in/site.rb
104
+ - lib/alexa/traffic_history.rb
105
+ - lib/alexa/traffic_history/historical_data.rb
106
+ - lib/alexa/url_info.rb
107
+ - lib/alexa/url_info/categories.rb
108
+ - lib/alexa/url_info/contact_info.rb
109
+ - lib/alexa/url_info/content_data.rb
110
+ - lib/alexa/url_info/contributing_subdomains.rb
111
+ - lib/alexa/url_info/related.rb
112
+ - lib/alexa/url_info/traffic_data.rb
113
+ - lib/alexa/url_info/usage.rb
114
+ - test/helper.rb
115
+ - test/test_alexa.rb
116
+ has_rdoc: true
117
+ homepage: http://github.com/elcamino/alexa
118
+ licenses:
119
+ - MIT
120
+ post_install_message:
121
+ rdoc_options: []
122
+
123
+ require_paths:
124
+ - lib
125
+ required_ruby_version: !ruby/object:Gem::Requirement
126
+ none: false
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ hash: 1170584116001240053
131
+ segments:
132
+ - 0
133
+ version: "0"
134
+ required_rubygems_version: !ruby/object:Gem::Requirement
135
+ none: false
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: "0"
140
+ requirements: []
141
+
142
+ rubyforge_project:
143
+ rubygems_version: 1.6.2
144
+ signing_key:
145
+ specification_version: 3
146
+ summary: Alexa Web API ruby library
147
+ test_files: []
148
+