linkscape 0.2.6
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +228 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/lib/hash-ext.rb +24 -0
- data/lib/linkscape.rb +17 -0
- data/lib/linkscape/client.rb +210 -0
- data/lib/linkscape/constants.rb +418 -0
- data/lib/linkscape/constants/anchor-metrics.rb +75 -0
- data/lib/linkscape/constants/link-metrics.rb +100 -0
- data/lib/linkscape/constants/url-metrics.rb +183 -0
- data/lib/linkscape/errors.rb +4 -0
- data/lib/linkscape/request.rb +80 -0
- data/lib/linkscape/response.rb +138 -0
- data/lib/linkscape/signer.rb +13 -0
- data/lib/string-ext.rb +7 -0
- data/linkscape.gemspec +60 -0
- data/rails/init.rb +1 -0
- data/test.rb +43 -0
- metadata +94 -0
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009-2010 SEOmoz
|
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,228 @@
|
|
1
|
+
= Linkscape Gem
|
2
|
+
|
3
|
+
This is the official Gem to interact with the SEOmoz API. For more information, please check out the {API page on SEOmoz}[http://www.seomoz.org/api] and the {SEOmoz API Wiki}[http://apiwiki.seomoz.org]
|
4
|
+
|
5
|
+
== Usage
|
6
|
+
|
7
|
+
First you must secure an accessID and secret key from SEOmoz. See the {API page}[http://www.seomoz.org/api] for details. Once you have your credentials, create an instance of your client:
|
8
|
+
|
9
|
+
client = Linkscape::Client.new(:accessID => "my_access_id", :secret => "$secretp@$$w0rd")
|
10
|
+
|
11
|
+
Then you should be able to make one of the predefined API calls
|
12
|
+
|
13
|
+
response = client.urlMetrics("http://gemcutter.org", :cols => :all)
|
14
|
+
|
15
|
+
At this point <tt>response</tt> should be a Linkscape::Response object. You can get at its data by calling
|
16
|
+
|
17
|
+
response.data
|
18
|
+
|
19
|
+
This returns a Linkscape::Response::ResponseData object, which is the container class for the response data.
|
20
|
+
|
21
|
+
For the urlMetrics and mozrank call, you can request specific data points, like:
|
22
|
+
|
23
|
+
response.data[:url]
|
24
|
+
response.data[:external_links]
|
25
|
+
response.data[:page_authority]
|
26
|
+
|
27
|
+
For all other calls, such as anchor text, top pages and link data sets, <tt>data</tt> is an array of ResponseData objects, with each one keyed on their respective types. For example:
|
28
|
+
|
29
|
+
response.data.first[:source][:mozrank] # :source just for topPages and link calls
|
30
|
+
response.data.first[:target][:page_authority] # :target just for link calls
|
31
|
+
response.data.first[:link][:text] # :link just for link calls
|
32
|
+
response.data.first[:anchor][:text] # :anchor just for link anchorMetrics calls
|
33
|
+
|
34
|
+
== Available API Calls
|
35
|
+
|
36
|
+
The Linkscape::Client object can make the following API calls:
|
37
|
+
|
38
|
+
=== mozRank
|
39
|
+
|
40
|
+
Returns the mozRank of the supplied URI.
|
41
|
+
|
42
|
+
client.mozRank(uri)
|
43
|
+
|
44
|
+
=== urlMetrics
|
45
|
+
|
46
|
+
Returns metrics about the supplied URI.
|
47
|
+
|
48
|
+
client.urlMetrics(uri, :cols => :all)
|
49
|
+
|
50
|
+
This call accepts the following options:
|
51
|
+
|
52
|
+
* <tt>:cols</tt> - An array of columns (see below) to return in the response, or the <tt>:all</tt> keyword, which returns all columns.
|
53
|
+
|
54
|
+
=== topLinks
|
55
|
+
|
56
|
+
Returns a list of topLinks to the supplied URI and :type.
|
57
|
+
|
58
|
+
client.topLinks(uri, :page, :urlcols => :all, :linkcols => :all, :limit => 3))
|
59
|
+
|
60
|
+
The 2nd parameter of this method, <tt>:type</tt> can be either <tt>:page</tt>, <tt>:subdomain</tt> <tt>:domain</tt>. It specifies the type of links you are requesting (links to the supplied URI, Root Domain or the Subdomain of the supplied URI).
|
61
|
+
|
62
|
+
This call accepts the following options:
|
63
|
+
|
64
|
+
* <tt>:sourcecols</tt> - An array of data columns (see below) that should be returned for the link source.
|
65
|
+
* <tt>:targetcols</tt> - An array of data columns (see below) that should be returned for the link target.
|
66
|
+
* <tt>:linkcols</tt> - An array of data columns (see below) that should be returned for the link itself.
|
67
|
+
* <tt>:limit</tt> - The # of links (limit) you would like to return.
|
68
|
+
* <tt>:offset</tt> = The number of records to offset before returning the 1st record of results.
|
69
|
+
|
70
|
+
=== allLinks
|
71
|
+
|
72
|
+
Returns all the links to a specific URI and :type.
|
73
|
+
|
74
|
+
client.allLinks(uri, :page, :urlcols => [:title, :url, :page_authority, :domain_authority], :linkcols => :all, :filters => :external, :limit => 3)
|
75
|
+
|
76
|
+
The 2nd parameter of this method, <tt>:type</tt> can be either <tt>:page</tt>, <tt>:subdomain</tt> <tt>:domain</tt>. It specifies the type of links you are requesting (links to the supplied URI, Root Domain or the Subdomain of the supplied URI).
|
77
|
+
|
78
|
+
This call accepts the following options:
|
79
|
+
|
80
|
+
* <tt>:sourcecols</tt> - An array of data columns (see below) that should be returned for the link source.
|
81
|
+
* <tt>:targetcols</tt> - An array of data columns (see below) that should be returned for the link target.
|
82
|
+
* <tt>:linkcols</tt> - An array of data columns (see below) that should be returned for the link itself.
|
83
|
+
* <tt>:filters</tt> - A String, Array or Symbol of filters (see below) that should be applied to the list of links. NOTE: Multiple filters may be combined, i.e. <tt>:filters => [:internal,:follow,:redir301]</tt>.
|
84
|
+
* <tt>:limit</tt> - The # of links (limit) you would like to return.
|
85
|
+
* <tt>:offset</tt> = The number of records to offset before returning the 1st record of results.
|
86
|
+
* <tt>:scope</tt> - A symbol representing the 'scope' of the links (see below).
|
87
|
+
|
88
|
+
=== topPages
|
89
|
+
|
90
|
+
Returns a list of the top pages on the URI in question
|
91
|
+
|
92
|
+
client.topPages(uri, :page, :cols => :all, :limit => 3)
|
93
|
+
|
94
|
+
The 2nd parameter of this method, <tt>:type</tt> can be either <tt>:page</tt>, <tt>:subdomain</tt> <tt>:domain</tt>. It specifies the type of top pages you are requesting (top pages on the supplied URI, Root Domain or the Subdomain of the supplied URI).
|
95
|
+
|
96
|
+
This call accepts the following options:
|
97
|
+
|
98
|
+
* <tt>:cols</tt> - An array of data columns (see below) that should be returned for the link source.
|
99
|
+
* <tt>:limit</tt> - The # of links (limit) you would like to return.
|
100
|
+
* <tt>:offset</tt> = The number of records to offset before returning the 1st record of results.
|
101
|
+
|
102
|
+
=== anchorMetrics
|
103
|
+
|
104
|
+
Returns anchor text metrics about the URI in question
|
105
|
+
|
106
|
+
client.anchorMetrics(uri, :cols => :all, :scope => "page_to_domain", :filters => :external, :sort => :domains_linking_page, :limit => 3, :scope => :phrase_to_page)
|
107
|
+
|
108
|
+
This call accepts the following options:
|
109
|
+
|
110
|
+
* <tt>:cols</tt> - An array of data columns (see below) that should be returned.
|
111
|
+
* <tt>:scope</tt> - A symbol representing the 'scope' of the anchor text data (see below).
|
112
|
+
* <tt>:sort</tt> - A symbol representing the 'sort order' of the anchor text data (see below).
|
113
|
+
* <tt>:filters</tt> - A symbol representing the 'filter' of the anchor text data (see below). NOTE: Only <tt>:external</tt> or <tt>internal</tt> filters may be used, separately.
|
114
|
+
* <tt>:limit</tt> - The # of links (limit) you would like to return.
|
115
|
+
* <tt>:offset</tt> = The number of records to offset before returning the 1st record of results.
|
116
|
+
|
117
|
+
== Requesting Data on Multiple URLs
|
118
|
+
|
119
|
+
For the API calls which support it, you may request data on multiple URLs by passing an
|
120
|
+
array of URLs to the API call.
|
121
|
+
|
122
|
+
urls = ["http://www.seomoz.org/blog/21-tactics-to-increase-blog-traffic", "http://www.seomoz.org/tools"]
|
123
|
+
response = client.urlMetrics(urls, :cols => :all)
|
124
|
+
response.data.first[:url]
|
125
|
+
>> "http://www.seomoz.org/blog/21-tactics-to-increase-blog-traffic"
|
126
|
+
response.data.first[:external_links]
|
127
|
+
>> 436
|
128
|
+
|
129
|
+
== Available Response Columns
|
130
|
+
|
131
|
+
Depending on the type of data point return, you may access certain data points inside the ResponseData object.
|
132
|
+
|
133
|
+
=== Source/Target/URL Metrics
|
134
|
+
|
135
|
+
* <tt>:status</tt>
|
136
|
+
* <tt>:fq_domain_mozrank</tt>
|
137
|
+
* <tt>:pl_domain_links</tt>
|
138
|
+
* <tt>:pl_domain_external_links</tt>
|
139
|
+
* <tt>:fq_domain</tt>
|
140
|
+
* <tt>:fq_domain_moztrust</tt>
|
141
|
+
* <tt>:fq_domain_pl_domains_linking</tt>
|
142
|
+
* <tt>:pl_domain</tt>
|
143
|
+
* <tt>:url</tt>
|
144
|
+
* <tt>:page_authority_raw</tt>
|
145
|
+
* <tt>:pl_domain_external_mozrank_sum_raw</tt>
|
146
|
+
* <tt>:links</tt>
|
147
|
+
* <tt>:external_mozrank</tt>
|
148
|
+
* <tt>:pl_domain_mozrank</tt>
|
149
|
+
* <tt>:juice_links</tt>
|
150
|
+
* <tt>:title</tt>
|
151
|
+
* <tt>:fq_domains_linking</tt>
|
152
|
+
* <tt>:page_authority</tt>
|
153
|
+
* <tt>:fq_domain_external_mozrank_sum_raw</tt>
|
154
|
+
* <tt>:pl_domain_moztrust</tt>
|
155
|
+
* <tt>:fq_domain_external_links</tt>
|
156
|
+
* <tt>:domain_authority_raw</tt>
|
157
|
+
* <tt>:canonical_url</tt>
|
158
|
+
* <tt>:pl_domain_mozrank_sum_raw</tt>
|
159
|
+
* <tt>:fq_domain_links</tt>
|
160
|
+
* <tt>:all</tt>
|
161
|
+
* <tt>:mozrank</tt>
|
162
|
+
* <tt>:pl_domain_pl_domains_linking</tt>
|
163
|
+
* <tt>:external_links</tt>
|
164
|
+
* <tt>:fq_domain_fq_domains_linking</tt>
|
165
|
+
* <tt>:pl_domains_linking</tt>
|
166
|
+
* <tt>:domain_authority</tt>
|
167
|
+
* <tt>:fq_domain_mozrank_sum_raw</tt>
|
168
|
+
* <tt>:moztrust</tt>
|
169
|
+
|
170
|
+
=== Link Metrics
|
171
|
+
|
172
|
+
* <tt>:text</tt>
|
173
|
+
* <tt>:mozrank</tt> (passed)
|
174
|
+
|
175
|
+
=== Anchor Text Metrics
|
176
|
+
|
177
|
+
* <tt>:flags</tt>
|
178
|
+
* <tt>:internal_mozrank</tt>
|
179
|
+
* <tt>:internal_pages_linking</tt>
|
180
|
+
* <tt>:external_subdomains_linking</tt>
|
181
|
+
* <tt>:external_mozrank</tt>
|
182
|
+
* <tt>:text</tt>
|
183
|
+
* <tt>:internal_subdomains_linking</tt>
|
184
|
+
* <tt>:external_domains_linking</tt>
|
185
|
+
* <tt>:all</tt>
|
186
|
+
* <tt>:record_id</tt>
|
187
|
+
* <tt>:external_pages_linking</tt>
|
188
|
+
|
189
|
+
== Available Filters
|
190
|
+
|
191
|
+
Links may be filtered by any of the following. Anchor text may o
|
192
|
+
|
193
|
+
* <tt>:internal</tt>
|
194
|
+
* <tt>:external</tt>
|
195
|
+
* <tt>:redir301</tt>
|
196
|
+
* <tt>:follow</tt>
|
197
|
+
* <tt>:nofollow</tt>
|
198
|
+
|
199
|
+
== Anchor Text Scope
|
200
|
+
|
201
|
+
When requesting anchor text data, the following scopes may be used.
|
202
|
+
|
203
|
+
* <tt>:phrase_to_page</tt>
|
204
|
+
* <tt>:phrase_to_subdomain</tt>
|
205
|
+
* <tt>:phrase_to_domain</tt>
|
206
|
+
* <tt>:term_to_page</tt>
|
207
|
+
* <tt>:term_to_subdomain</tt>
|
208
|
+
* <tt>:term_to_domain</tt>
|
209
|
+
|
210
|
+
== Link Scope
|
211
|
+
|
212
|
+
When requesting links, the following scope may be used
|
213
|
+
|
214
|
+
* <tt>:page_to_page</tt>
|
215
|
+
* <tt>:page_to_subdomain</tt>
|
216
|
+
* <tt>:page_to_domain</tt>
|
217
|
+
* <tt>:domain_to_page</tt>
|
218
|
+
* <tt>:domain_to_subdomain</tt>
|
219
|
+
* <tt>:domain_to_domain</tt>
|
220
|
+
|
221
|
+
== Sort Orders
|
222
|
+
|
223
|
+
When sorting links, the following sort orders are available.
|
224
|
+
|
225
|
+
* <tt>:page_athority</tt>
|
226
|
+
* <tt>:domain_authority</tt>
|
227
|
+
* <tt>:domains_linking_page</tt>
|
228
|
+
* <tt>:domains_linking_domain</tt>
|
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "linkscape"
|
8
|
+
gem.summary = %Q{Provides an interface to the SEOmoz API}
|
9
|
+
gem.description = %Q{Provides an interface to SEOmoz's suite of APIs, including the free and site intelligence APIs.}
|
10
|
+
gem.email = %q{api@seomoz.org}
|
11
|
+
gem.homepage = "http://www.seomoz.org/api"
|
12
|
+
gem.authors = ["Marty Smyth", "Jeff Pollard"]
|
13
|
+
gem.add_dependency "ruby-hmac", ">= 0"
|
14
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
|
+
end
|
16
|
+
Jeweler::GemcutterTasks.new
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'rake/testtask'
|
22
|
+
Rake::TestTask.new(:test) do |test|
|
23
|
+
test.libs << 'lib' << 'test'
|
24
|
+
test.pattern = 'test/**/test_*.rb'
|
25
|
+
test.verbose = true
|
26
|
+
end
|
27
|
+
|
28
|
+
begin
|
29
|
+
require 'rcov/rcovtask'
|
30
|
+
Rcov::RcovTask.new do |test|
|
31
|
+
test.libs << 'test'
|
32
|
+
test.pattern = 'test/**/test_*.rb'
|
33
|
+
test.verbose = true
|
34
|
+
end
|
35
|
+
rescue LoadError
|
36
|
+
task :rcov do
|
37
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
task :test => :check_dependencies
|
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 = "linkscape #{version}"
|
51
|
+
rdoc.rdoc_files.include('README*')
|
52
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
53
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.6
|
data/lib/hash-ext.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# Copied from ActiveSupport, included if we don't already have it
|
2
|
+
|
3
|
+
class Hash
|
4
|
+
# Return a new hash with all keys converted to symbols.
|
5
|
+
def symbolize_keys
|
6
|
+
inject({}) do |options, (key, value)|
|
7
|
+
options[(key.downcase.to_sym rescue key) || key] = value
|
8
|
+
options
|
9
|
+
end
|
10
|
+
end
|
11
|
+
def symbolize_keys!
|
12
|
+
self.replace(self.symbolize_keys)
|
13
|
+
end
|
14
|
+
# Return a new hash with all keys converted to strings.
|
15
|
+
def stringify_keys
|
16
|
+
inject({}) do |options, (key, value)|
|
17
|
+
options[(key.to_s rescue key) || key] = value
|
18
|
+
options
|
19
|
+
end
|
20
|
+
end
|
21
|
+
def stringify_keys!
|
22
|
+
self.replace(self.stringify_keys)
|
23
|
+
end
|
24
|
+
end
|
data/lib/linkscape.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'cgi'
|
3
|
+
require 'base64'
|
4
|
+
require 'hmac-sha1'
|
5
|
+
|
6
|
+
directory = File.expand_path(File.dirname(__FILE__))
|
7
|
+
|
8
|
+
require File.join(directory, 'hash-ext') unless Hash.method_defined?(:symbolize_keys)
|
9
|
+
require File.join(directory, 'string-ext')
|
10
|
+
|
11
|
+
require File.join(directory, 'linkscape', 'signer')
|
12
|
+
require File.join(directory, 'linkscape', 'constants')
|
13
|
+
|
14
|
+
require File.join(directory, 'linkscape', 'client')
|
15
|
+
require File.join(directory, 'linkscape', 'request')
|
16
|
+
require File.join(directory, 'linkscape', 'response')
|
17
|
+
require File.join(directory, 'linkscape', 'errors')
|
@@ -0,0 +1,210 @@
|
|
1
|
+
module Linkscape
|
2
|
+
class Client
|
3
|
+
def initialize(*args)
|
4
|
+
options = Hash === args.last ? args.pop : {}
|
5
|
+
@accessID = args.first ? args.shift : (options[:id] || options[:ID] || options[:accessID])
|
6
|
+
@secretKey = args.first ? args.shift : (options[:secret] || options[:secretKey] || options[:key])
|
7
|
+
|
8
|
+
@options = {
|
9
|
+
:apiHost => 'lsapi.seomoz.com',
|
10
|
+
:apiRoot => 'linkscape',
|
11
|
+
:accessID => @accessID,
|
12
|
+
:secretKey => @secretKey
|
13
|
+
}.merge(options)
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
def mozRank(*args)
|
19
|
+
options = Hash === args.last ? args.pop.symbolize_keys : {}
|
20
|
+
url = args.first ? args.shift : options[:url]
|
21
|
+
|
22
|
+
raise MissingArgument, "mozRank requires a url." unless url
|
23
|
+
|
24
|
+
options[:url] = url
|
25
|
+
options[:api] = 'mozrank'
|
26
|
+
|
27
|
+
Linkscape::Request.run(@options.merge(options))
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
def urlMetrics(*args)
|
32
|
+
options = Hash === args.last ? args.pop.symbolize_keys : {}
|
33
|
+
url = args.first ? args.shift : options[:url]
|
34
|
+
|
35
|
+
raise MissingArgument, "urlMetrics requires a url." unless url
|
36
|
+
|
37
|
+
options[:url] = url
|
38
|
+
options[:api] = 'url-metrics'
|
39
|
+
|
40
|
+
options[:query] = {
|
41
|
+
'Cols' => translateBitfield(options[:cols], options[:columns], options[:fields], :type => :url)
|
42
|
+
}
|
43
|
+
|
44
|
+
raise MissingArgument, "urlMetrics requires a list of columns to return." unless options[:query]['Cols'].nonzero?
|
45
|
+
|
46
|
+
Linkscape::Request.run(@options.merge(options))
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
def topLinks(*args)
|
51
|
+
options = Hash === args.last ? args.pop.symbolize_keys : {}
|
52
|
+
url = args.first ? args.shift : options[:url]
|
53
|
+
whichSet = (args.first ? args.shift : (options[:set] || 'none')).to_sym
|
54
|
+
|
55
|
+
raise MissingArgument, "topLinks requires a set (:page, :subdomain, :domain) and a url." unless whichSet and url
|
56
|
+
|
57
|
+
options[:url] = url
|
58
|
+
|
59
|
+
options[:api] = {
|
60
|
+
:page => 'page-linklist',
|
61
|
+
:subdomain => 'subdomain-linklist',
|
62
|
+
:domain => 'rootdomain-linklist',
|
63
|
+
}[whichSet]
|
64
|
+
|
65
|
+
raise InvalidArgument, "topLinks set must be one of :page, :subdomain, :domain" unless options[:api]
|
66
|
+
|
67
|
+
options[:query] = {
|
68
|
+
'SourceCols' => translateBitfield(options[:sourcecols], options[:sourcecolumns], options[:urlcols], :type => :url),
|
69
|
+
'TargetCols' => translateBitfield(options[:targetcols], options[:targetcolumns], options[:urlcols], :type => :url),
|
70
|
+
'LinkCols' => translateBitfield(options[:cols], options[:columns], options[:linkcols], :type => :link),
|
71
|
+
}
|
72
|
+
|
73
|
+
raise MissingArgument, "topLinks requires a list of columns for Source, Target, and/or Link." unless options[:query]['SourceCols'].nonzero? or options[:query]['TargetCols'].nonzero? or options[:query]['LinkCols'].nonzero?
|
74
|
+
|
75
|
+
Linkscape::Request.run(@options.merge(options))
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
def allLinks(*args)
|
80
|
+
options = Hash === args.last ? args.pop.symbolize_keys : {}
|
81
|
+
url = args.first ? args.shift : options[:url]
|
82
|
+
scope = args.length >= 2 ? "#{args.shift}_to_#{args.shift}" : (args.first ? args.shift : options[:scope])
|
83
|
+
sortOrder = (args.first ? args.shift : (options[:sort] || options[:sortOrder] || 'domains_linking_page')).to_sym
|
84
|
+
filters = args.first ? args.shift : (options[:filters] || options[:filter] || '')
|
85
|
+
|
86
|
+
raise MissingArgument, "allLinks requires a scope ([page, domain] to [page, subdomain, domain]) and a url." unless scope and url
|
87
|
+
raise InvalidArgument, "allLinks scope must be valid ([page, domain] to [page, subdomain, domain])" unless scope.to_s =~ /^(page|domain)_to_(page|subdomain|domain)$/
|
88
|
+
raise InvalidArgument, "allLinks sort order must be valid (domain_authority, page_authority, domains_linking_page, domains_linking_domain)" unless [:domain_authority, :page_authority, :domains_linking_page, :domains_linking_domain].include? sortOrder
|
89
|
+
|
90
|
+
if String === filters
|
91
|
+
filters = filters.downcase.split(/[,\s\+]+/).sort.collect(&:to_sym)
|
92
|
+
elsif Hash === filters
|
93
|
+
filters = filters.keys.collect{|k|k.to_s.downcase}.sort.collect(&:to_sym)
|
94
|
+
elsif Array === filters
|
95
|
+
filters = filters.collect{|k|k.to_s.downcase}.sort.collect(&:to_sym)
|
96
|
+
elsif Symbol === filters
|
97
|
+
filters = [filters]
|
98
|
+
else
|
99
|
+
raise InvalidArgument, "allLinks filters must be a string, hash, or array"
|
100
|
+
end
|
101
|
+
raise InvalidArgument, "allLinks filters must be from the set (:internal, :external, :redir301, :follow, :nofollow)" unless (filters - [:internal, :external, :redir301, :follow, :nofollow]).empty?
|
102
|
+
|
103
|
+
options[:url] = url
|
104
|
+
options[:api] = 'links'
|
105
|
+
|
106
|
+
options[:query] = {
|
107
|
+
'SourceCols' => translateBitfield(options[:sourcecols], options[:sourcecolumns], options[:urlcols], :type => :url),
|
108
|
+
'TargetCols' => translateBitfield(options[:targetcols], options[:targetcolumns], options[:urlcols], :type => :url),
|
109
|
+
'LinkCols' => translateBitfield(options[:cols], options[:columns], options[:linkcols], :type => :link),
|
110
|
+
'Scope' => scope,
|
111
|
+
'Filter' => filters.join(' ').gsub(/redir/, ''),
|
112
|
+
'Sort' => sortOrder.to_s,
|
113
|
+
}
|
114
|
+
|
115
|
+
raise MissingArgument, "allLinks requires a list of columns for Source, Target, and/or Link." unless options[:query]['SourceCols'].nonzero? or options[:query]['TargetCols'].nonzero? or options[:query]['LinkCols'].nonzero?
|
116
|
+
|
117
|
+
Linkscape::Request.run(@options.merge(options))
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
def topPages(*args)
|
122
|
+
options = Hash === args.last ? args.pop.symbolize_keys : {}
|
123
|
+
url = args.first ? args.shift : options[:url]
|
124
|
+
|
125
|
+
options[:url] = url
|
126
|
+
options[:api] = 'top-pages'
|
127
|
+
|
128
|
+
options[:query] = {
|
129
|
+
'Cols' => translateBitfield(options[:cols], options[:columns], options[:sourcecols], options[:sourcecolumns], options[:urlcols], :type => :url),
|
130
|
+
}
|
131
|
+
|
132
|
+
raise MissingArgument, "topPages requires a list of columns to return." unless options[:query]['Cols'].nonzero?
|
133
|
+
|
134
|
+
Linkscape::Request.run(@options.merge(options))
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
def anchorMetrics(*args)
|
139
|
+
options = Hash === args.last ? args.pop.symbolize_keys : {}
|
140
|
+
url = args.first ? args.shift : options[:url]
|
141
|
+
scope = args.length >= 2 ? "#{args.shift}_to_#{args.shift}" : (args.first ? args.shift : options[:scope])
|
142
|
+
filters = args.first ? args.shift : (options[:filters] || options[:filter] || '')
|
143
|
+
sortOrder = (args.first ? args.shift : (options[:sort] || options[:sortOrder] || 'domains_linking_page')).to_sym
|
144
|
+
|
145
|
+
raise InvalidArgument, "anchorMetrics scope must be valid ([phrase, term] to [page, subdomain, domain])" unless scope.to_s =~ /^(phrase|term)_to_(page|subdomain|domain)$/
|
146
|
+
raise InvalidArgument, "anchorMetrics sort order must be valid (domain_authority, page_authority, domains_linking_page, domains_linking_domain)" unless [:domain_authority, :page_authority, :domains_linking_page, :domains_linking_domain].include? sortOrder
|
147
|
+
|
148
|
+
if String === filters
|
149
|
+
filters = filters.downcase.split(/[,\s\+]+/).sort.collect(&:to_sym)
|
150
|
+
elsif Hash === filters
|
151
|
+
filters = filters.keys.collect{|k|k.to_s.downcase}.sort.collect(&:to_sym)
|
152
|
+
elsif Array === filters
|
153
|
+
filters = filters.collect{|k|k.to_s.downcase}.sort.collect(&:to_sym)
|
154
|
+
elsif Symbol === filters
|
155
|
+
filters = [filters]
|
156
|
+
else
|
157
|
+
raise InvalidArgument, "anchorMetrics filters must be a string, hash, or array"
|
158
|
+
end
|
159
|
+
raise InvalidArgument, "anchorMetrics filters must be from the set (:internal, :external)" unless (filters - [:internal, :external]).empty?
|
160
|
+
|
161
|
+
options[:url] = url
|
162
|
+
options[:api] = 'anchor-text'
|
163
|
+
|
164
|
+
options[:query] = {
|
165
|
+
'Cols' => translateBitfield(options[:cols], options[:columns], options[:anchorcols], :type => :anchors),
|
166
|
+
'Scope' => scope,
|
167
|
+
'Filter' => filters.join(' ').gsub(/redir/, ''),
|
168
|
+
'Sort' => sortOrder.to_s,
|
169
|
+
}
|
170
|
+
|
171
|
+
# raise MissingArgument, "anchorMetrics requires a list of columns to return." unless options[:query]['Cols'].nonzero?
|
172
|
+
|
173
|
+
Linkscape::Request.run(@options.merge(options))
|
174
|
+
end
|
175
|
+
|
176
|
+
|
177
|
+
|
178
|
+
|
179
|
+
def inspect
|
180
|
+
%Q[#<#{self.class}:#{"0x%x" % self.object_id} api="#{@options[:apiHost]}/#{@options[:apiRoot]}" accessID="#{@options[:accessID]}">]
|
181
|
+
end
|
182
|
+
|
183
|
+
private
|
184
|
+
|
185
|
+
def translateBitfield *columns
|
186
|
+
options = Hash === columns.last ? columns.pop : {}
|
187
|
+
|
188
|
+
columns.flatten!
|
189
|
+
columns.compact!
|
190
|
+
|
191
|
+
bits = columns.inject(0) do |bitfield, key|
|
192
|
+
requestBit =
|
193
|
+
if options[:type] == :url
|
194
|
+
Linkscape::Constants::URLMetrics::RequestBits[key]
|
195
|
+
elsif options[:type] == :link
|
196
|
+
Linkscape::Constants::LinkMetrics::RequestBits[key]
|
197
|
+
elsif options[:type] == :anchors
|
198
|
+
Linkscape::Constants::AnchorMetrics::RequestBits[key]
|
199
|
+
else
|
200
|
+
Linkscape::Constants::URLMetrics::RequestBits[key] || Linkscape::Constants::LinkMetrics::RequestBits[key]
|
201
|
+
end
|
202
|
+
raise InvalidArgument, "Unknown #{options[:type]} flag '#{key.inspect}'".gsub(/ +/, ' ') unless requestBit
|
203
|
+
bitfield |= requestBit[:flag]
|
204
|
+
end
|
205
|
+
|
206
|
+
bits
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
210
|
+
end
|