ralexa 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ralexa.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Flippa
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,131 @@
1
+ Ralexa
2
+ ======
3
+
4
+ Ruby client for Amazon Alexa APIs.
5
+
6
+ These include [Alexa Top Sites][1] and [Alexa Web Information Service][2].
7
+
8
+ HTTP transport thanks to `Net::HTTP`,
9
+ with a little help from [addressable][3].
10
+
11
+ XML parsing courtesy of [Nokogiri][4].
12
+
13
+ Thorough test coverage provided by `MiniTest::Spec`.
14
+
15
+ [1]: http://aws.amazon.com/awis/
16
+ [2]: http://aws.amazon.com/alexatopsites/
17
+ [3]: https://github.com/sporkmonger/addressable
18
+ [4]: http://nokogiri.org/
19
+
20
+
21
+ Installation
22
+ ------------
23
+
24
+ ```sh
25
+ # with bundler
26
+ echo 'gem "ralexa"' >> Gemfile
27
+ bundle
28
+
29
+ # or just the gem
30
+ gem install ralexa
31
+ ```
32
+
33
+
34
+ Usage Examples
35
+ --------------
36
+
37
+ ```ruby
38
+
39
+ # grab a Ralexa::Session instance to hold your credentials.
40
+ session = Ralexa.session("aws_access_key_id", "aws_secret_access_key")
41
+
42
+ # all countries
43
+ countries = session.top_sites.list_countries
44
+
45
+ # global top sites
46
+ global = session.top_sites.global
47
+
48
+ # per-country top sites
49
+ first_by_country = {}
50
+ countries.each do |c|
51
+ first_by_country[c.name] = session.top_sites.country(c.code).first.url
52
+ end
53
+
54
+ # individual country lookup
55
+ puts "Top Australian Sites"
56
+ session.top_sites.country("AU").each do |s|
57
+ puts "#{s.url} (#{s.page_views} pageviews)"
58
+ end
59
+
60
+ puts "bam!"
61
+ ```
62
+
63
+
64
+ Status
65
+ ------
66
+
67
+ <table>
68
+ <tr>
69
+ <th>Service</th>
70
+ <th>Action</th>
71
+ <th>ResponseGroup</th>
72
+ <th>Supported</th>
73
+ </tr>
74
+ <tr>
75
+ <td rowspan="3">Alexa Top Sites</th>
76
+ <td>TopSites</th>
77
+ <td>Global</th>
78
+ <td>Yes</th>
79
+ </tr>
80
+ <tr>
81
+ <td></th>
82
+ <td>Country</th>
83
+ <td>Yes</th>
84
+ </tr>
85
+ <tr>
86
+ <td></th>
87
+ <td>ListCountries</th>
88
+ <td>Yes</th>
89
+ </tr>
90
+ <tr>
91
+ <td rowspan="5">Alexa Web Information Services</th>
92
+ <td>UrlInfo</th>
93
+ <td>*</th>
94
+ <td>Send a pull request!</th>
95
+ </tr>
96
+ <tr>
97
+ <td>TrafficHistory</th>
98
+ <td>*</th>
99
+ <td>Send a pull request!</th>
100
+ </tr>
101
+ <tr>
102
+ <td>CategoryBrowse</th>
103
+ <td>*</th>
104
+ <td>Send a pull request!</th>
105
+ </tr>
106
+ <tr>
107
+ <td>CategoryListings</th>
108
+ <td>*</th>
109
+ <td>Send a pull request!</th>
110
+ </tr>
111
+ <tr>
112
+ <td>SitesLinkingIn</th>
113
+ <td>*</th>
114
+ <td>Send a pull request!</th>
115
+ </tr>
116
+ </table>
117
+
118
+
119
+ Contributing
120
+ ------------
121
+
122
+ 1. Fork it
123
+ 2. Create your feature branch (`git checkout -b some-new-feature`)
124
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
125
+ 4. Push to the branch (`git push origin some-new-feature`)
126
+ 5. Create new Pull Request
127
+
128
+ License
129
+ -------
130
+
131
+ (c) 2012 Flippa, The MIT License.
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env rake
2
+
3
+ task default: :test
4
+
5
+ # Bundler
6
+ require "bundler/gem_tasks"
7
+
8
+ # MiniTest
9
+ require "rake/testtask"
10
+ Rake::TestTask.new do |t|
11
+ # ENV["TESTOPTS"] = "-v"
12
+ t.pattern = "spec/*_spec.rb"
13
+ end
@@ -0,0 +1,27 @@
1
+ require "nokogiri"
2
+
3
+ module Ralexa
4
+ class AbstractXmlService
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ private
11
+
12
+ # Returns the response as an XML document stripped of namespaces.
13
+ def dispatch(*params)
14
+ Nokogiri::XML.parse(
15
+ @client.get(host, path, merged_params(*params))
16
+ ).remove_namespaces!
17
+ end
18
+
19
+ # A hash of the provided params hashes merged into the default_params.
20
+ def merged_params(*params)
21
+ params.reduce(default_params) do |merged_params, params|
22
+ merged_params.merge(params)
23
+ end
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,23 @@
1
+ require "uri"
2
+
3
+ module Ralexa
4
+ class CanonicalizedQueryString
5
+
6
+ def initialize(parameters)
7
+ @parameters = parameters
8
+ end
9
+
10
+ def to_s
11
+ @parameters.sort.map do |k, v|
12
+ "%s=%s" % [ escape_rfc3986(k), escape_rfc3986(v) ]
13
+ end.join("&")
14
+ end
15
+
16
+ private
17
+
18
+ def escape_rfc3986(string)
19
+ URI.escape(string.to_s, /[^A-Za-z0-9\-_.~]/)
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,49 @@
1
+ require "addressable/uri"
2
+ require "net/http"
3
+
4
+ require "ralexa/uri_signer"
5
+
6
+ module Ralexa
7
+ class Client
8
+
9
+ def initialize(access_key_id, secret_access_key)
10
+ @access_key_id = access_key_id
11
+ @secret_access_key = secret_access_key
12
+ end
13
+
14
+ # Dependency injectors.
15
+ attr_writer :net_http
16
+
17
+ def get(host, path, query_values)
18
+ uri = signed_uri(host, path, query_values)
19
+ response = net_http.get_response(uri)
20
+ response.error! unless response.is_a?(Net::HTTPSuccess)
21
+ response.body
22
+ end
23
+
24
+ private
25
+
26
+ def signed_uri(host, path, query_values)
27
+ uri_signer.sign_uri(
28
+ Addressable::URI.new(
29
+ scheme: "http",
30
+ host: host,
31
+ path: path,
32
+ query_values: query_values
33
+ )
34
+ )
35
+ end
36
+
37
+ def uri_signer
38
+ UriSigner.new(@access_key_id, @secret_access_key)
39
+ end
40
+
41
+ ##
42
+ # Injectable dependencies.
43
+
44
+ def net_http
45
+ @net_http || Net::HTTP
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,18 @@
1
+ require "ralexa/client"
2
+ require "ralexa/top_sites"
3
+
4
+ module Ralexa
5
+ class Session
6
+
7
+ def initialize(access_key_id, secret_access_key)
8
+ @client = Client.new(access_key_id, secret_access_key)
9
+ end
10
+
11
+ # Provides a TopSites instance with an authenticated client.
12
+ # See: http://docs.amazonwebservices.com/AlexaTopSites/latest/index.html
13
+ def top_sites
14
+ TopSites.new(@client)
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,59 @@
1
+ require "ralexa/abstract_xml_service"
2
+
3
+ module Ralexa
4
+ class TopSites < AbstractXmlService
5
+
6
+ # A global list of top sites.
7
+ def global(params = {})
8
+ top_sites_from_document(dispatch(
9
+ {"ResponseGroup" => "Country"},
10
+ params
11
+ ))
12
+ end
13
+
14
+ # Top sites for the specified two letter country code.
15
+ def country(code, params = {})
16
+ top_sites_from_document(dispatch(
17
+ {"ResponseGroup" => "Country", "CountryCode" => code.to_s.upcase},
18
+ params
19
+ ))
20
+ end
21
+
22
+ # All countries that have Alexa top sites.
23
+ def list_countries(params = {})
24
+ dispatch(
25
+ {"ResponseGroup" => "ListCountries"},
26
+ params
27
+ ).at("//TopSitesResult/Alexa/TopSites/Countries").elements.map do |node|
28
+ Country.new(
29
+ node.at("Name").text,
30
+ node.at("Code").text,
31
+ node.at("TotalSites").text.to_i,
32
+ node.at("PageViews").text.to_f * 1_000_000,
33
+ node.at("Users").text.to_f * 1_000_000,
34
+ )
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def host; "ats.amazonaws.com" end
41
+ def path; "/" end
42
+ def default_params; {"Action" => "TopSites"} end
43
+
44
+ def top_sites_from_document(document)
45
+ document.at("//TopSites/Country/Sites").elements.map do |node|
46
+ Site.new(
47
+ node.at("DataUrl").text,
48
+ node.at("Country/Rank").text.to_i,
49
+ node.at("Country/Reach/PerMillion").text.to_i * 1_000_000,
50
+ (node.at("Country/PageViews/PerMillion").text.to_f * 1_000_000).to_i,
51
+ node.at("Country/PageViews/PerUser").text.to_f
52
+ )
53
+ end
54
+ end
55
+
56
+ Country = Struct.new(:name, :code, :total_sites, :page_views, :users)
57
+ Site = Struct.new(:url, :rank, :reach, :page_views, :page_views_per_user)
58
+ end
59
+ end
@@ -0,0 +1,89 @@
1
+ require "base64"
2
+ require "digest/sha2"
3
+ require "openssl"
4
+
5
+ require "ralexa/canonicalized_query_string"
6
+
7
+ module Ralexa
8
+ class UriSigner
9
+
10
+ SIGNATURE_VERSION = 2
11
+
12
+ def initialize(access_key_id, secret_access_key)
13
+ @access_key_id = access_key_id
14
+ @secret_access_key = secret_access_key
15
+ end
16
+
17
+ # dependency injectors
18
+ attr_writer :digest
19
+ attr_writer :hmac_signer
20
+ attr_writer :base64_encoder
21
+ attr_writer :time_utc
22
+
23
+ def sign_uri(original_uri)
24
+ original_uri.dup.tap do |uri|
25
+ uri.query_values = uri.query_values.merge(
26
+ "AWSAccessKeyId" => @access_key_id,
27
+ "Timestamp" => timestamp,
28
+ "SignatureVersion" => SIGNATURE_VERSION.to_s,
29
+ "SignatureMethod" => signature_method,
30
+ )
31
+ uri.query_values = uri.query_values.merge(
32
+ "Signature" => signature(uri)
33
+ )
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def string_to_sign(uri)
40
+ [
41
+ "GET",
42
+ uri.host.downcase,
43
+ uri.path,
44
+ CanonicalizedQueryString.new(uri.query_values)
45
+ ].join("\n")
46
+ end
47
+
48
+ def timestamp
49
+ time_utc.strftime("%Y-%m-%dT%H:%M:%S.000Z")
50
+ end
51
+
52
+ def signature_method
53
+ "Hmac#{digest.name}"
54
+ end
55
+
56
+ def signature(uri)
57
+ encode(hmac(string_to_sign(uri)))
58
+ end
59
+
60
+ def hmac(string_to_sign)
61
+ hmac_signer.digest(digest, @secret_access_key, string_to_sign)
62
+ end
63
+
64
+ def encode(input)
65
+ base64_encoder.encode64(input).chop
66
+ end
67
+
68
+ ##
69
+ # Injectable dependencies.
70
+
71
+ def digest
72
+ @digest ||= OpenSSL::Digest::SHA256.new
73
+ end
74
+
75
+ def hmac_signer
76
+ @hmac_signer || OpenSSL::HMAC
77
+ end
78
+
79
+ def base64_encoder
80
+ @base64_encoder || Base64
81
+ end
82
+
83
+ def time_utc
84
+ # don't store in ivar.
85
+ @time_utc || Time.now.utc
86
+ end
87
+
88
+ end
89
+ end
@@ -0,0 +1,3 @@
1
+ module Ralexa
2
+ VERSION = "0.0.1"
3
+ end
data/lib/ralexa.rb ADDED
@@ -0,0 +1,22 @@
1
+ %w{
2
+ abstract_xml_service
3
+ awis
4
+ canonicalized_query_string
5
+ client
6
+ session
7
+ top_sites
8
+ uri_signer
9
+ version
10
+ }.each do |file|
11
+ require "ralexa/#{file}"
12
+ end
13
+
14
+ module Ralexa
15
+
16
+ # An authenticated Session instance.
17
+ # Holds credentials, provides access to service instances.
18
+ def self.session(access_key_id, secret_access_key)
19
+ Session.new(access_key_id, secret_access_key)
20
+ end
21
+
22
+ end
data/ralexa.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/ralexa/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Paul Annesley"]
6
+ gem.email = ["paul@annesley.cc"]
7
+ gem.description = %q{Ruby client for Amazon Alexa APIs.}
8
+ gem.summary = %q{Ruby client for Amazon Alexa APIs such as TopSites.}
9
+ gem.homepage = "https://github.com/flippa/ralexa"
10
+
11
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ gem.files = `git ls-files`.split("\n")
13
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ gem.name = "ralexa"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Ralexa::VERSION
17
+
18
+ gem.add_runtime_dependency "addressable"
19
+ gem.add_runtime_dependency "nokogiri"
20
+
21
+ gem.add_development_dependency "minitest"
22
+ gem.add_development_dependency "rake"
23
+ end
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+
3
+ require_relative "spec_helper"
4
+
5
+ require "ralexa/canonicalized_query_string"
6
+
7
+ module Ralexa
8
+ describe CanonicalizedQueryString do
9
+
10
+ def q(parameters)
11
+ CanonicalizedQueryString.new(parameters).to_s
12
+ end
13
+
14
+ it "sorts parameters by key into a query string" do
15
+ q("a" => "3", "c" => "2", "b" => "1").must_equal "a=3&b=1&c=2"
16
+ end
17
+
18
+ it "does not URL-encode RFC 3986 unreserved characters" do
19
+ q("AZaz09-_.~" => "AZaz09-_.~").must_equal "AZaz09-_.~=AZaz09-_.~"
20
+ end
21
+
22
+ it "percent-encodes other characters" do
23
+ q("=&/" => "✈").must_equal "%3D%26%2F=%E2%9C%88"
24
+ end
25
+
26
+ it "encodes space as '%20' not '+'" do
27
+ q("a b" => "c d").must_equal "a%20b=c%20d"
28
+ end
29
+
30
+ it "handles empty parameters" do
31
+ q("a" => "", "b" => nil).must_equal "a=&b="
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,32 @@
1
+ require_relative "spec_helper"
2
+
3
+ require "ralexa/client"
4
+
5
+ module Ralexa
6
+ describe Client do
7
+
8
+ let(:client) do
9
+ Client.new("id", "secret").tap do |c|
10
+ c.net_http = net_http
11
+ end
12
+ end
13
+
14
+ let(:net_http) { MiniTest::Mock.new }
15
+ let(:response) { MiniTest::Mock.new }
16
+
17
+ it "returns the response body of HTTP GET to a signed URI" do
18
+ net_http.expect :get_response, response, [ %r{^http://example.org/test\?.*Signature=} ]
19
+ response.expect :is_a?, true, [ Net::HTTPSuccess ]
20
+ response.expect :body, "<xml/>"
21
+ client.get("example.org", "/test", "a" => "b").must_equal "<xml/>"
22
+ end
23
+
24
+ it "raises an error if the HTTP request failed" do
25
+ net_http.expect :get_response, response, [ %r{^http://example.org/test\?.*Signature=} ]
26
+ response.expect :is_a?, false, [ Net::HTTPSuccess ]
27
+ def response.error!; raise Net::HTTPError.new("", self); end
28
+ ->{ client.get("example.org", "/test", "a" => "b") }.must_raise Net::HTTPError
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,45 @@
1
+ <?xml version="1.0"?>
2
+ <aws:TopSitesResponse xmlns:aws="http://alexa.amazonaws.com/doc/2005-10-05/"><aws:Response xmlns:aws="http://ats.amazonaws.com/doc/2005-11-21"><aws:OperationRequest><aws:RequestId>8a6c3a3f-a3c8-48d8-4775-3914d8f36e88</aws:RequestId></aws:OperationRequest><aws:TopSitesResult><aws:Alexa>
3
+
4
+ <aws:TopSites>
5
+ <aws:Country>
6
+ <aws:CountryName>GLOBAL</aws:CountryName>
7
+ <aws:CountryCode>*</aws:CountryCode>
8
+ <aws:TotalSites>366720</aws:TotalSites>
9
+ <aws:Sites>
10
+ <aws:Site>
11
+ <aws:DataUrl>wikipedia.org</aws:DataUrl>
12
+ <aws:Country>
13
+ <aws:Rank>6</aws:Rank>
14
+ <aws:Reach>
15
+ <aws:PerMillion>143000</aws:PerMillion>
16
+ </aws:Reach>
17
+ <aws:PageViews>
18
+ <aws:PerMillion>5219</aws:PerMillion>
19
+ <aws:PerUser>3.88</aws:PerUser>
20
+ </aws:PageViews>
21
+ </aws:Country>
22
+ <aws:Global>
23
+ <aws:Rank>6</aws:Rank>
24
+ </aws:Global>
25
+ </aws:Site>
26
+ <aws:Site>
27
+ <aws:DataUrl>bbc.co.uk</aws:DataUrl>
28
+ <aws:Country>
29
+ <aws:Rank>52</aws:Rank>
30
+ <aws:Reach>
31
+ <aws:PerMillion>20540</aws:PerMillion>
32
+ </aws:Reach>
33
+ <aws:PageViews>
34
+ <aws:PerMillion>898.5</aws:PerMillion>
35
+ <aws:PerUser>4.65</aws:PerUser>
36
+ </aws:PageViews>
37
+ </aws:Country>
38
+ <aws:Global>
39
+ <aws:Rank>52</aws:Rank>
40
+ </aws:Global>
41
+ </aws:Site>
42
+ </aws:Sites>
43
+ </aws:Country>
44
+ </aws:TopSites>
45
+ </aws:Alexa></aws:TopSitesResult><aws:ResponseStatus xmlns:aws="http://alexa.amazonaws.com/doc/2005-10-05/"><aws:StatusCode>Success</aws:StatusCode></aws:ResponseStatus></aws:Response></aws:TopSitesResponse>
@@ -0,0 +1,22 @@
1
+ <?xml version="1.0"?>
2
+ <aws:TopSitesResponse xmlns:aws="http://alexa.amazonaws.com/doc/2005-10-05/"><aws:Response xmlns:aws="http://ats.amazonaws.com/doc/2005-11-21"><aws:OperationRequest><aws:RequestId>a914be78-f2cf-71be-97dd-6e51501050b2</aws:RequestId></aws:OperationRequest><aws:TopSitesResult><aws:Alexa>
3
+
4
+ <aws:TopSites>
5
+ <aws:Countries>
6
+ <aws:Country>
7
+ <aws:Name>Australia</aws:Name>
8
+ <aws:Code>AU</aws:Code>
9
+ <aws:TotalSites>42920</aws:TotalSites>
10
+ <aws:PageViews>1.074</aws:PageViews>
11
+ <aws:Users>1.148</aws:Users>
12
+ </aws:Country>
13
+ <aws:Country>
14
+ <aws:Name>United States</aws:Name>
15
+ <aws:Code>US</aws:Code>
16
+ <aws:TotalSites>484842</aws:TotalSites>
17
+ <aws:PageViews>18.023</aws:PageViews>
18
+ <aws:Users>18.998</aws:Users>
19
+ </aws:Country>
20
+ </aws:Countries>
21
+ </aws:TopSites>
22
+ </aws:Alexa></aws:TopSitesResult><aws:ResponseStatus xmlns:aws="http://alexa.amazonaws.com/doc/2005-10-05/"><aws:StatusCode>Success</aws:StatusCode></aws:ResponseStatus></aws:Response></aws:TopSitesResponse>
@@ -0,0 +1,13 @@
1
+ require_relative "spec_helper"
2
+
3
+ require "ralexa"
4
+
5
+ describe Ralexa do
6
+
7
+ describe ".session" do
8
+ it "returns a Ralexa::Session" do
9
+ Ralexa.session("id", "secret").must_be_kind_of ::Ralexa::Session
10
+ end
11
+ end
12
+
13
+ end
@@ -0,0 +1,15 @@
1
+ require_relative "spec_helper"
2
+
3
+ require "ralexa/session"
4
+
5
+ module Ralexa
6
+ describe Session do
7
+
8
+ describe ".top_sites" do
9
+ it "returns a Ralexa::TopSites instance" do
10
+ Session.new("id", "secret").top_sites.must_be_kind_of ::Ralexa::TopSites
11
+ end
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,7 @@
1
+ gem "minitest"
2
+ require "minitest/autorun"
3
+ $LOAD_PATH.unshift "./lib"
4
+
5
+ def fixture(filename)
6
+ File.read("#{File.dirname(__FILE__)}/fixtures/#{filename}")
7
+ end
@@ -0,0 +1,87 @@
1
+ require_relative "spec_helper"
2
+
3
+ require "ralexa/top_sites"
4
+
5
+ module Ralexa
6
+ describe TopSites do
7
+
8
+ let(:top_sites) { TopSites.new(client) }
9
+ let(:client) { MiniTest::Mock.new }
10
+
11
+ describe "#global" do
12
+ before do
13
+ expect_parameters = {
14
+ "Action" => "TopSites",
15
+ "ResponseGroup" => "Country",
16
+ }
17
+ client.expect :get, fixture("global.xml"),
18
+ ["ats.amazonaws.com", "/", expect_parameters]
19
+ end
20
+ it "fetches, parses and returns top sites" do
21
+ top_sites_match_fixture(top_sites.global)
22
+ end
23
+ end
24
+
25
+ describe "#country" do
26
+ before do
27
+ expect_parameters = {
28
+ "Action" => "TopSites",
29
+ "ResponseGroup" => "Country",
30
+ "CountryCode" => "AU",
31
+ }
32
+ client.expect :get, fixture("global.xml"),
33
+ ["ats.amazonaws.com", "/", expect_parameters]
34
+ end
35
+ it "fetches, parses and returns top sites for specified country" do
36
+ top_sites_match_fixture(top_sites.country("AU"))
37
+ end
38
+ end
39
+
40
+ describe "#list_countries" do
41
+ before do
42
+ expect_parameters = {
43
+ "Action" => "TopSites",
44
+ "ResponseGroup" => "ListCountries",
45
+ }
46
+ client.expect :get, fixture("list_countries.xml"),
47
+ ["ats.amazonaws.com", "/", expect_parameters]
48
+ end
49
+
50
+ it "fetches, parses and returns countries" do
51
+ countries = top_sites.list_countries
52
+ countries.size.must_equal 2
53
+ a, u = countries
54
+
55
+ u.name.must_equal "United States"
56
+ u.code.must_equal "US"
57
+ u.total_sites.must_equal 484842
58
+ u.page_views.must_equal 18_023_000
59
+ u.users.must_equal 18_998_000
60
+
61
+ a.name.must_equal "Australia"
62
+ a.code.must_equal "AU"
63
+ a.total_sites.must_equal 42920
64
+ a.page_views.must_equal 1_074_000
65
+ a.users.must_equal 1_148_000
66
+ end
67
+ end
68
+
69
+ def top_sites_match_fixture(sites)
70
+ sites.size.must_equal 2
71
+ w, b = sites
72
+
73
+ w.url.must_equal "wikipedia.org"
74
+ w.rank.must_equal 6
75
+ w.reach.must_equal 143_000_000_000
76
+ w.page_views.must_equal 5_219_000_000
77
+ w.page_views_per_user.must_equal 3.88
78
+
79
+ b.url.must_equal "bbc.co.uk"
80
+ b.rank.must_equal 52
81
+ b.reach.must_equal 20_540_000_000
82
+ b.page_views.must_equal 898_500_000
83
+ b.page_views_per_user.must_equal 4.65
84
+ end
85
+
86
+ end
87
+ end
@@ -0,0 +1,62 @@
1
+ require_relative "spec_helper"
2
+
3
+ require "addressable/uri"
4
+ require "ralexa/uri_signer"
5
+
6
+ module Ralexa
7
+ describe UriSigner do
8
+
9
+ let(:time_utc) { Time.now.utc }
10
+ let(:digest) { MiniTest::Mock.new }
11
+ let(:hmac_signer) { MiniTest::Mock.new }
12
+ let(:base64_encoder) { MiniTest::Mock.new }
13
+
14
+ def signer
15
+ UriSigner.new("id", "secret").tap do |rs|
16
+ rs.digest = digest
17
+ rs.hmac_signer = hmac_signer
18
+ rs.base64_encoder = base64_encoder
19
+ rs.time_utc = time_utc
20
+ end
21
+ end
22
+
23
+ describe "for http://example.org/path?b=c&a=b" do
24
+
25
+ let(:uri) { Addressable::URI.parse("http://example.org/path?b=c&a=b") }
26
+ let(:signed_uri) { signer.sign_uri(uri) }
27
+ let(:timestamp) { time_utc.strftime("%Y-%m-%dT%H:%M:%S.000Z") }
28
+
29
+ before do
30
+ ts = URI.escape(timestamp, /:/)
31
+ combined = [
32
+ "GET",
33
+ "example.org",
34
+ "/path",
35
+ "AWSAccessKeyId=id&SignatureMethod=HmacFAKE&SignatureVersion=2&Timestamp=#{ts}&a=b&b=c"
36
+ ].join("\n")
37
+ hmac_signer.expect :digest, "the_hmac", [ digest, "secret", combined ]
38
+ base64_encoder.expect :encode64, "base64_encoded\n", [ "the_hmac" ]
39
+ digest.expect :name, "FAKE"
40
+ end
41
+
42
+ it "returns a signed URL" do
43
+ signed_uri.host.must_equal "example.org"
44
+ signed_uri.path.must_equal "/path"
45
+ signed_uri.query_values.must_equal(
46
+ "b" => "c",
47
+ "a" => "b",
48
+ "AWSAccessKeyId" => "id",
49
+ "Signature" => "base64_encoded",
50
+ "SignatureMethod" => "HmacFAKE",
51
+ "SignatureVersion" => "2",
52
+ "Timestamp" => timestamp,
53
+ )
54
+ end
55
+
56
+ it "does not modify the passed uri" do
57
+ signed_uri.wont_be_same_as uri
58
+ end
59
+ end
60
+
61
+ end
62
+ end
metadata ADDED
@@ -0,0 +1,122 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ralexa
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Paul Annesley
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-03-26 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: addressable
16
+ requirement: &70290636275640 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70290636275640
25
+ - !ruby/object:Gem::Dependency
26
+ name: nokogiri
27
+ requirement: &70290636275220 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70290636275220
36
+ - !ruby/object:Gem::Dependency
37
+ name: minitest
38
+ requirement: &70290636274800 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70290636274800
47
+ - !ruby/object:Gem::Dependency
48
+ name: rake
49
+ requirement: &70290636274380 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70290636274380
58
+ description: Ruby client for Amazon Alexa APIs.
59
+ email:
60
+ - paul@annesley.cc
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - .gitignore
66
+ - Gemfile
67
+ - LICENSE
68
+ - README.md
69
+ - Rakefile
70
+ - lib/ralexa.rb
71
+ - lib/ralexa/abstract_xml_service.rb
72
+ - lib/ralexa/canonicalized_query_string.rb
73
+ - lib/ralexa/client.rb
74
+ - lib/ralexa/session.rb
75
+ - lib/ralexa/top_sites.rb
76
+ - lib/ralexa/uri_signer.rb
77
+ - lib/ralexa/version.rb
78
+ - ralexa.gemspec
79
+ - spec/canonicalized_query_string_spec.rb
80
+ - spec/client_spec.rb
81
+ - spec/fixtures/global.xml
82
+ - spec/fixtures/list_countries.xml
83
+ - spec/ralexa_spec.rb
84
+ - spec/session_spec.rb
85
+ - spec/spec_helper.rb
86
+ - spec/top_sites_spec.rb
87
+ - spec/uri_signer_spec.rb
88
+ homepage: https://github.com/flippa/ralexa
89
+ licenses: []
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ! '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ! '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 1.8.11
109
+ signing_key:
110
+ specification_version: 3
111
+ summary: Ruby client for Amazon Alexa APIs such as TopSites.
112
+ test_files:
113
+ - spec/canonicalized_query_string_spec.rb
114
+ - spec/client_spec.rb
115
+ - spec/fixtures/global.xml
116
+ - spec/fixtures/list_countries.xml
117
+ - spec/ralexa_spec.rb
118
+ - spec/session_spec.rb
119
+ - spec/spec_helper.rb
120
+ - spec/top_sites_spec.rb
121
+ - spec/uri_signer_spec.rb
122
+ has_rdoc: