ralexa 0.0.1

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/.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: