alexa 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. data/.travis.yml +4 -5
  2. data/Gemfile +1 -0
  3. data/README.md +76 -21
  4. data/alexa.gemspec +1 -9
  5. data/lib/alexa.rb +11 -22
  6. data/lib/alexa/api/category_browse.rb +62 -0
  7. data/lib/alexa/api/category_listings.rb +66 -0
  8. data/lib/alexa/api/sites_linking_in.rb +41 -0
  9. data/lib/alexa/api/traffic_history.rb +45 -0
  10. data/lib/alexa/api/url_info.rb +108 -0
  11. data/lib/alexa/client.rb +30 -0
  12. data/lib/alexa/connection.rb +86 -0
  13. data/lib/alexa/exceptions.rb +17 -0
  14. data/lib/alexa/utils.rb +19 -0
  15. data/lib/alexa/version.rb +1 -1
  16. data/test/api/category_browse_test.rb +27 -0
  17. data/test/api/category_listings_test.rb +19 -0
  18. data/test/api/sites_linking_in_test.rb +23 -0
  19. data/test/api/traffic_history_test.rb +24 -0
  20. data/test/api/url_info_test.rb +112 -0
  21. data/test/client_test.rb +65 -0
  22. data/test/connection_test.rb +37 -0
  23. data/test/fixtures/bad_request.txt +8 -0
  24. data/test/fixtures/category_browse/card_games.txt +480 -0
  25. data/test/fixtures/category_listings/card_games.txt +138 -0
  26. data/test/fixtures/sites_linking_in/github_count_3.txt +24 -0
  27. data/test/fixtures/traffic_history/alexa_example.txt +37 -0
  28. data/test/fixtures/traffic_history/github_range_2.txt +8 -0
  29. data/test/fixtures/{custom-response-group.txt → url_info/custom-response-group.txt} +0 -0
  30. data/test/fixtures/{empty_group.txt → url_info/empty_group.txt} +0 -0
  31. data/test/fixtures/{github_full.txt → url_info/github_full.txt} +0 -0
  32. data/test/fixtures/{github_rank.txt → url_info/github_rank.txt} +0 -0
  33. data/test/helper.rb +6 -8
  34. data/test/utils_test.rb +54 -0
  35. metadata +33 -23
  36. data/lib/alexa/url_info.rb +0 -109
  37. data/test/config_test.rb +0 -35
  38. data/test/url_info_test.rb +0 -152
data/.travis.yml CHANGED
@@ -2,11 +2,10 @@ notifications:
2
2
  disabled: true
3
3
  rvm:
4
4
  - ree
5
- - 1.9.2
6
5
  - 1.9.3
7
6
  env:
8
- - ALEXA_XML_PARSER=rexml
9
- - ALEXA_XML_PARSER=nokogiri
10
- - ALEXA_XML_PARSER=libxml
7
+ - XML_PARSER=rexml
8
+ - XML_PARSER=nokogiri
9
+ - XML_PARSER=libxml
11
10
  # does not work yet
12
- # - ALEXA_XML_PARSER=ox
11
+ # - XML_PARSER=ox
data/Gemfile CHANGED
@@ -1,6 +1,7 @@
1
1
  source "https://rubygems.org"
2
2
 
3
3
  gem "minitest", :platform => :ruby_18
4
+ gem "debugger", :platform => :ruby_19
4
5
  gem "rake"
5
6
 
6
7
  # xml parsers
data/README.md CHANGED
@@ -10,52 +10,107 @@ gem install alexa
10
10
 
11
11
  ## Usage
12
12
 
13
+ ### Url Info
14
+
13
15
  ``` ruby
14
- Alexa.url_info(:access_key_id => "key", :secret_access_key => "secret", :host => "site.com")
16
+ client = Alexa::Client.new(access_key_id: "key", secret_access_key: "secret")
17
+ url_info = client.url_info(url: "site.com")
15
18
 
16
19
  returns object with methods:
17
- :xml_response, :rank, :data_url, :site_title, :site_description, :language_locale, :language_encoding,
20
+ :rank, :data_url, :site_title, :site_description, :language_locale, :language_encoding,
18
21
  :links_in_count, :keywords, :related_links, :speed_median_load_time, :speed_percentile,
19
22
  :rank_by_country, :rank_by_city, :usage_statistics
20
23
  ```
21
24
 
22
- NOTE: You can specify option `:response_group => "Rank,ContactInfo"` or any other valid group.
25
+ NOTE: You can specify option `response_group: ["rank", "contact_info"]` or any other valid group.
23
26
  See: [Docs](http://docs.amazonwebservices.com/AlexaWebInfoService/latest/)
24
27
  Default response group takes all the available options.
25
28
 
26
- You can set configuration in block like this:
29
+ ### Sites Linking In
27
30
 
28
31
  ``` ruby
29
- Alexa.configure do |config|
30
- config.access_key_id = "key"
31
- config.secret_access_key = "secret"
32
- end
32
+ client = Alexa::Client.new(access_key_id: "key", secret_access_key: "secret")
33
+ sites_linking_in = client.sites_linking_in(url: "site.com")
34
+
35
+ # returns object with `sites` method
33
36
  ```
34
37
 
35
- alexa is using `multi_xml` to parse XML documents. Tested with:
38
+ NOTE: You can specify options:
36
39
 
37
- * rexml
38
- * nokogiri
39
- * libxml
40
+ * count (how many results to retrieve - default to max value 20)
41
+ * start (offset of results - default to 0)
42
+
43
+ ### Traffic History
44
+
45
+ ``` ruby
46
+ client = Alexa::Client.new(access_key_id: "key", secret_access_key: "secret")
47
+ traffic_history = client.traffic_history(url: "site.com")
48
+
49
+ # returns object with `data` method
50
+ ```
51
+
52
+ NOTE: You can specify options:
53
+
54
+ * range (how many days to retrieve - default to max value 31)
55
+ * start (start date - default to range days ago)
56
+
57
+ ### Category Browse
58
+
59
+ ``` ruby
60
+ client = Alexa::Client.new(access_key_id: "key", secret_access_key: "secret")
61
+ category_browse = client.category_browse(path: "Top/Games/Card_Games")
62
+
63
+ # returns object with `categories`, `language_categories`, `related_categories`, `letter_bars` methods.
64
+ ```
40
65
 
41
- Currently alexa wont work with `ox` gem
66
+ NOTE:
42
67
 
43
- ## TODO
68
+ You can find valid paths on [dmoz](http://www.dmoz.org/) site
44
69
 
45
- Support following:
70
+ You can specify options:
46
71
 
47
- * Traffic History
48
- * Category Browse
49
- * Category Listings
50
- * Sites Linking In
72
+ * response_group (any of: categories, related_categories, language_categories, letter_bars)
73
+ * descriptions (include descriptions - boolean, true by default)
74
+
75
+ ### Category Listings
76
+
77
+ ``` ruby
78
+ client = Alexa::Client.new(access_key_id: "key", secret_access_key: "secret")
79
+ category_listings = client.category_listings(path: "Top/Games/Card_Games")
80
+
81
+ # returns object with `recursive_count`, `listings` methods.
82
+ ```
83
+
84
+ NOTE:
85
+
86
+ You can find valid paths on [dmoz](http://www.dmoz.org/) site
87
+
88
+ You can specify options:
89
+
90
+ * sort_by (sort results by one of: popularity, title, average_review)
91
+ * recursive (results for the given category only, or including subcategories - boolean, true by default)
92
+ * count (how many results to retrieve - default to max value 20)
93
+ * start (offset of results - default to 0)
94
+ * descriptions (include descriptions - boolean, true by default)
95
+
96
+ ## Caveats
97
+
98
+ Alexa is using `multi_xml` to parse XML documents. Tested with:
99
+
100
+ * rexml
101
+ * nokogiri
102
+ * libxml
103
+
104
+ Currently alexa will not work with `ox` gem
51
105
 
52
106
  ## Contributors
53
107
 
54
108
  * [rmoriz](https://github.com/rmoriz)
55
109
 
56
110
  ## Continuous Integration
57
- [![Build Status](https://secure.travis-ci.org/morgoth/alexa.png)](http://travis-ci.org/morgoth/alexa)
111
+
112
+ [![Build Status](https://secure.travis-ci.org/morgoth/alexa.png?branch=master)](http://travis-ci.org/morgoth/alexa)
58
113
 
59
114
  ## Copyright
60
115
 
61
- Copyright (c) 2012 Wojciech Wnętrzak. See LICENSE for details.
116
+ Copyright (c) Wojciech Wnętrzak. See LICENSE for details.
data/alexa.gemspec CHANGED
@@ -18,13 +18,5 @@ Gem::Specification.new do |gem|
18
18
  gem.add_dependency "multi_xml", ">= 0.5.0"
19
19
 
20
20
  gem.add_development_dependency "mocha"
21
- gem.add_development_dependency "fakeweb"
22
-
23
- gem.post_install_message = %{
24
-
25
- Gem syntax of 0.4 version of this gem will change with backward incompatibilities.
26
- If you don't want to update your code, please specify in your Gemfile:
27
- gem "alexa", "~> 0.3.0"
28
-
29
- }
21
+ gem.add_development_dependency "webmock"
30
22
  end
data/lib/alexa.rb CHANGED
@@ -1,28 +1,17 @@
1
- require "cgi"
2
- require "base64"
3
- require "openssl"
4
- require "digest/sha1"
5
- require "net/https"
6
- require "time"
7
-
8
1
  require "multi_xml"
9
2
 
10
3
  require "alexa/version"
11
- require "alexa/url_info"
4
+ require "alexa/utils"
5
+ require "alexa/exceptions"
6
+ require "alexa/connection"
7
+ require "alexa/client"
8
+ require "alexa/api/category_browse"
9
+ require "alexa/api/category_listings"
10
+ require "alexa/api/sites_linking_in"
11
+ require "alexa/api/traffic_history"
12
+ require "alexa/api/url_info"
12
13
 
13
14
  module Alexa
14
- class << self
15
- attr_accessor :access_key_id, :secret_access_key
16
-
17
- def configure
18
- yield self
19
- end
20
-
21
- def url_info(options = {})
22
- url_info = Alexa::UrlInfo.new(options)
23
- xml = url_info.connect
24
- url_info.parse_xml(xml)
25
- url_info
26
- end
27
- end
15
+ API_VERSION = "2005-07-11"
16
+ API_HOST = "awis.amazonaws.com"
28
17
  end
@@ -0,0 +1,62 @@
1
+ module Alexa::API
2
+ class CategoryBrowse
3
+ include Alexa::Utils
4
+
5
+ DEFAULT_RESPONSE_GROUP = ["categories", "related_categories", "language_categories", "letter_bars"]
6
+
7
+ attr_reader :response_group, :path, :descriptions, :response_body
8
+
9
+ def initialize(credentials)
10
+ @credentials = credentials
11
+ end
12
+
13
+ def fetch(arguments = {})
14
+ @path = arguments[:path] || raise(ArgumentError.new("You must specify path"))
15
+ @response_group = Array(arguments.fetch(:response_group, DEFAULT_RESPONSE_GROUP))
16
+ @descriptions = arguments.fetch(:descriptions, true)
17
+ @response_body = Alexa::Connection.new(@credentials).get(params)
18
+ self
19
+ end
20
+
21
+ # Attributes
22
+
23
+ def categories
24
+ @categories ||= safe_retrieve(parsed_body, "CategoryBrowseResponse", "Response", "CategoryBrowseResult", "Alexa", "CategoryBrowse", "Categories", "Category")
25
+ end
26
+
27
+ def language_categories
28
+ @language_categories ||= safe_retrieve(parsed_body, "CategoryBrowseResponse", "Response", "CategoryBrowseResult", "Alexa", "CategoryBrowse", "LanguageCategories", "Category")
29
+ end
30
+
31
+ def related_categories
32
+ @related_categories ||= safe_retrieve(parsed_body, "CategoryBrowseResponse", "Response", "CategoryBrowseResult", "Alexa", "CategoryBrowse", "RelatedCategories", "Category")
33
+ end
34
+
35
+ def letter_bars
36
+ @letter_bars ||= safe_retrieve(parsed_body, "CategoryBrowseResponse", "Response", "CategoryBrowseResult", "Alexa", "CategoryBrowse", "LetterBars", "Category")
37
+ end
38
+
39
+ private
40
+
41
+ def params
42
+ {
43
+ "Action" => "CategoryBrowse",
44
+ "ResponseGroup" => response_group_param,
45
+ "Path" => path,
46
+ "Descriptions" => descriptions_param
47
+ }
48
+ end
49
+
50
+ def response_group_param
51
+ response_group.sort.map { |group| camelize(group) }.join(",")
52
+ end
53
+
54
+ def descriptions_param
55
+ descriptions.to_s.capitalize
56
+ end
57
+
58
+ def parsed_body
59
+ @parsed_body ||= MultiXml.parse(response_body)
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,66 @@
1
+ module Alexa::API
2
+ class CategoryListings
3
+ include Alexa::Utils
4
+
5
+ attr_reader :path, :sort_by, :recursive, :start, :count, :descriptions, :response_body
6
+
7
+ def initialize(credentials)
8
+ @credentials = credentials
9
+ end
10
+
11
+ def fetch(arguments = {})
12
+ @path = arguments[:path] || raise(ArgumentError.new("You must specify path"))
13
+ @sort_by = arguments.fetch(:sort_by, "popularity")
14
+ @recursive = arguments.fetch(:recursive, true)
15
+ @start = arguments.fetch(:start, 0)
16
+ @count = arguments.fetch(:count, 20)
17
+ @descriptions = arguments.fetch(:descriptions, true)
18
+ @response_body = Alexa::Connection.new(@credentials).get(params)
19
+ self
20
+ end
21
+
22
+ # Attributes
23
+
24
+ def recursive_count
25
+ return @recursive_count if defined?(@recursive_count)
26
+ if recursive_count = safe_retrieve(parsed_body, "CategoryListingsResponse", "Response", "CategoryListingsResult", "Alexa", "CategoryListings", "RecursiveCount")
27
+ @recursive_count = recursive_count.to_i
28
+ end
29
+ end
30
+
31
+ def listings
32
+ @listings ||= safe_retrieve(parsed_body, "CategoryListingsResponse", "Response", "CategoryListingsResult", "Alexa", "CategoryListings", "Listings", "Listing")
33
+ end
34
+
35
+ private
36
+
37
+ def params
38
+ {
39
+ "Action" => "CategoryListings",
40
+ "ResponseGroup" => "Listings",
41
+ "Path" => path,
42
+ "Recursive" => recursive_param,
43
+ "Descriptions" => descriptions_param,
44
+ "SortBy" => sort_by_param,
45
+ "Count" => count,
46
+ "Start" => start,
47
+ }
48
+ end
49
+
50
+ def recursive_param
51
+ recursive.to_s.capitalize
52
+ end
53
+
54
+ def descriptions_param
55
+ descriptions.to_s.capitalize
56
+ end
57
+
58
+ def sort_by_param
59
+ camelize(sort_by)
60
+ end
61
+
62
+ def parsed_body
63
+ @parsed_body ||= MultiXml.parse(response_body)
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,41 @@
1
+ module Alexa::API
2
+ class SitesLinkingIn
3
+ include Alexa::Utils
4
+
5
+ attr_reader :url, :count, :start, :response_body
6
+
7
+ def initialize(credentials)
8
+ @credentials = credentials
9
+ end
10
+
11
+ def fetch(arguments = {})
12
+ @url = arguments[:url] || raise(ArgumentError.new("You must specify url"))
13
+ @count = arguments.fetch(:count, 20)
14
+ @start = arguments.fetch(:start, 0)
15
+ @response_body = Alexa::Connection.new(@credentials).get(params)
16
+ self
17
+ end
18
+
19
+ # Attributes
20
+
21
+ def sites
22
+ @sites ||= safe_retrieve(parsed_body, "SitesLinkingInResponse", "Response", "SitesLinkingInResult", "Alexa", "SitesLinkingIn", "Site")
23
+ end
24
+
25
+ private
26
+
27
+ def params
28
+ {
29
+ "Action" => "SitesLinkingIn",
30
+ "ResponseGroup" => "SitesLinkingIn",
31
+ "Count" => count,
32
+ "Start" => start,
33
+ "Url" => url
34
+ }
35
+ end
36
+
37
+ def parsed_body
38
+ @parsed_body ||= MultiXml.parse(response_body)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,45 @@
1
+ module Alexa::API
2
+ class TrafficHistory
3
+ include Alexa::Utils
4
+
5
+ attr_reader :url, :range, :start, :response_body
6
+
7
+ def initialize(credentials)
8
+ @credentials = credentials
9
+ end
10
+
11
+ def fetch(arguments = {})
12
+ @url = arguments[:url] || raise(ArgumentError.new("You must specify url"))
13
+ @range = arguments.fetch(:range, 31)
14
+ @start = arguments.fetch(:start) { Time.now - (3600 * 24 * range.to_i) }
15
+ @response_body = Alexa::Connection.new(@credentials).get(params)
16
+ self
17
+ end
18
+
19
+ # Attributes
20
+
21
+ def data
22
+ @data ||= safe_retrieve(parsed_body, "TrafficHistoryResponse", "Response", "TrafficHistoryResult", "Alexa", "TrafficHistory", "HistoricalData", "Data")
23
+ end
24
+
25
+ private
26
+
27
+ def params
28
+ {
29
+ "Action" => "TrafficHistory",
30
+ "ResponseGroup" => "History",
31
+ "Range" => range,
32
+ "Start" => start_param,
33
+ "Url" => url
34
+ }
35
+ end
36
+
37
+ def start_param
38
+ start.respond_to?(:strftime) ? start.strftime("%Y%m%d") : start
39
+ end
40
+
41
+ def parsed_body
42
+ @parsed_body ||= MultiXml.parse(response_body)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,108 @@
1
+ module Alexa::API
2
+ class UrlInfo
3
+ include Alexa::Utils
4
+
5
+ DEFAULT_RESPONSE_GROUP = ["adult_content", "contact_info", "keywords", "language", "links_in_count", "owned_domains", "rank", "rank_by_city", "rank_by_country", "related_links", "site_data", "speed", "usage_stats"]
6
+
7
+ attr_reader :url, :response_group, :response_body
8
+
9
+ def initialize(credentials)
10
+ @credentials = credentials
11
+ end
12
+
13
+ def fetch(arguments = {})
14
+ @url = arguments[:url] || raise(ArgumentError.new("You must specify url"))
15
+ @response_group = Array(arguments.fetch(:response_group, DEFAULT_RESPONSE_GROUP))
16
+ @response_body = Alexa::Connection.new(@credentials).get(params)
17
+ self
18
+ end
19
+
20
+ # attributes
21
+
22
+ def rank
23
+ return @rank if defined?(@rank)
24
+ if rank = safe_retrieve(parsed_body, "UrlInfoResponse", "Response", "UrlInfoResult", "Alexa", "TrafficData", "Rank")
25
+ @rank = rank.to_i
26
+ end
27
+ end
28
+
29
+ def rank_by_country
30
+ @rank_by_country ||= safe_retrieve(parsed_body, "UrlInfoResponse", "Response", "UrlInfoResult", "Alexa", "TrafficData", "RankByCountry", "Country")
31
+ end
32
+
33
+ def rank_by_city
34
+ @rank_by_city ||= safe_retrieve(parsed_body, "UrlInfoResponse", "Response", "UrlInfoResult", "Alexa", "TrafficData", "RankByCity", "City")
35
+ end
36
+
37
+ def data_url
38
+ @data_url ||= safe_retrieve(parsed_body, "UrlInfoResponse", "Response", "UrlInfoResult", "Alexa", "TrafficData", "DataUrl", "__content__")
39
+ end
40
+
41
+ def usage_statistics
42
+ @usage_statistics ||= safe_retrieve(parsed_body, "UrlInfoResponse", "Response", "UrlInfoResult", "Alexa", "TrafficData", "UsageStatistics", "UsageStatistic")
43
+ end
44
+
45
+ def site_title
46
+ @site_title ||= safe_retrieve(parsed_body, "UrlInfoResponse", "Response", "UrlInfoResult", "Alexa", "ContentData", "SiteData", "Title")
47
+ end
48
+
49
+ def site_description
50
+ @site_description ||= safe_retrieve(parsed_body, "UrlInfoResponse", "Response", "UrlInfoResult", "Alexa", "ContentData", "SiteData", "Description")
51
+ end
52
+
53
+ def language_locale
54
+ @language_locale ||= safe_retrieve(parsed_body, "UrlInfoResponse", "Response", "UrlInfoResult", "Alexa", "ContentData", "Language", "Locale")
55
+ end
56
+
57
+ def language_encoding
58
+ @language_encoding ||= safe_retrieve(parsed_body, "UrlInfoResponse", "Response", "UrlInfoResult", "Alexa", "ContentData", "Language", "Encoding")
59
+ end
60
+
61
+ def links_in_count
62
+ return @links_in_count if defined?(@links_in_count)
63
+ if links_in_count = safe_retrieve(parsed_body, "UrlInfoResponse", "Response", "UrlInfoResult", "Alexa", "ContentData", "LinksInCount")
64
+ @links_in_count = links_in_count.to_i
65
+ end
66
+ end
67
+
68
+ def keywords
69
+ @keywords ||= safe_retrieve(parsed_body, "UrlInfoResponse", "Response", "UrlInfoResult", "Alexa", "ContentData", "Keywords", "Keyword")
70
+ end
71
+
72
+ def speed_median_load_time
73
+ return @speed_median_load_time if defined?(@speed_median_load_time)
74
+ if speed_median_load_time = safe_retrieve(parsed_body, "UrlInfoResponse", "Response", "UrlInfoResult", "Alexa", "ContentData", "Speed", "MedianLoadTime")
75
+ @speed_median_load_time = speed_median_load_time.to_i
76
+ end
77
+ end
78
+
79
+ def speed_percentile
80
+ return @speed_percentile if defined?(@speed_percentile)
81
+ if speed_percentile = safe_retrieve(parsed_body, "UrlInfoResponse", "Response", "UrlInfoResult", "Alexa", "ContentData", "Speed", "Percentile")
82
+ @speed_percentile = speed_percentile.to_i
83
+ end
84
+ end
85
+
86
+ def related_links
87
+ @related_links ||= safe_retrieve(parsed_body, "UrlInfoResponse", "Response", "UrlInfoResult", "Alexa", "Related", "RelatedLinks", "RelatedLink")
88
+ end
89
+
90
+ private
91
+
92
+ def params
93
+ {
94
+ "Action" => "UrlInfo",
95
+ "ResponseGroup" => response_group_param,
96
+ "Url" => url
97
+ }
98
+ end
99
+
100
+ def response_group_param
101
+ response_group.sort.map { |group| camelize(group) }.join(",")
102
+ end
103
+
104
+ def parsed_body
105
+ @parsed_body ||= MultiXml.parse(response_body)
106
+ end
107
+ end
108
+ end