feedvalidator 0.1.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.
- data/CHANGELOG +2 -0
- data/README +71 -0
- data/install.rb +30 -0
- data/lib/feed_validator.rb +164 -0
- data/lib/feed_validator/assertions.rb +91 -0
- data/rakefile +109 -0
- data/test/feeds/weblog.rubyonrails.org_rss_2_0_articles.xml +154 -0
- data/test/feeds/www.w3.org_news.rss.xml +843 -0
- data/test/responses/success_with_warnings +40 -0
- data/test/unit/feed_validator_assertions_test.rb +48 -0
- data/test/unit/feed_validator_test.rb +63 -0
- metadata +54 -0
data/CHANGELOG
ADDED
data/README
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
= FeedValidator
|
2
|
+
|
3
|
+
FeedValidator is an interface to the {W3C Feed Validation online service}[http://validator.w3.org/feed/],
|
4
|
+
based on its SOAP 1.2 support.
|
5
|
+
|
6
|
+
It helps to find errors in RSS or ATOM feeds.
|
7
|
+
|
8
|
+
FeedValidator add a new assertion (<code>assert_valid_feed</code>) which you can use in Rails applications.
|
9
|
+
This assertion implements a cache to improve the performance of the tests and to not abuse of the
|
10
|
+
{W3C Feed Validation online service}[http://validator.w3.org/feed/]
|
11
|
+
|
12
|
+
|
13
|
+
== Examples
|
14
|
+
|
15
|
+
* For use FeedValidator just do this:
|
16
|
+
|
17
|
+
require 'feed_validator'
|
18
|
+
|
19
|
+
v = W3C::FeedValidator.new()
|
20
|
+
v.validate_url('http://www.w3.org/QA/news.rss')
|
21
|
+
puts v.to_s unless v.valid?
|
22
|
+
|
23
|
+
* In Rails you can use it in your functional tests, just modify your /test/test_helper.rb adding this line:
|
24
|
+
|
25
|
+
require 'feed_validator/assertions'
|
26
|
+
|
27
|
+
And now you can use in your functional test, in this way:
|
28
|
+
|
29
|
+
def test_bar_valid_feed
|
30
|
+
get :bar
|
31
|
+
assert_valid_feed
|
32
|
+
end
|
33
|
+
|
34
|
+
Or use the class-level method to quickly create validation tests for a bunch of actions at once:
|
35
|
+
|
36
|
+
assert_valid_feed :bar, :foo
|
37
|
+
|
38
|
+
|
39
|
+
== Download
|
40
|
+
|
41
|
+
The latest version of FeedValidator can be found at
|
42
|
+
|
43
|
+
* http://rubyforge.org/frs/?group_id=1533
|
44
|
+
|
45
|
+
Documentation can be found at
|
46
|
+
|
47
|
+
* http://feedvalidator.rubyforge.org
|
48
|
+
|
49
|
+
|
50
|
+
== Installation
|
51
|
+
|
52
|
+
You can install FeedValidator as a gem:
|
53
|
+
gem install feedvalidator
|
54
|
+
|
55
|
+
Or you can install it from the tarball or zip packages on the download page
|
56
|
+
and then extract it to your lib directory as you would with any other
|
57
|
+
Ruby library.
|
58
|
+
|
59
|
+
|
60
|
+
== License
|
61
|
+
|
62
|
+
FeedValidator is released under the MIT license.
|
63
|
+
|
64
|
+
|
65
|
+
== Support
|
66
|
+
|
67
|
+
You can find the Feed Validator RubyForge page at http://rubyforge.org/projects/feedvalidator.
|
68
|
+
|
69
|
+
Feel free to submit commits or feature requests.
|
70
|
+
|
71
|
+
For other information, feel free to contact mailto:edgar@lacaraoscura.com.
|
data/install.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'rbconfig'
|
2
|
+
require 'find'
|
3
|
+
require 'ftools'
|
4
|
+
|
5
|
+
include Config
|
6
|
+
|
7
|
+
# this was adapted from rdoc's install.rb by ways of Log4r
|
8
|
+
|
9
|
+
$sitedir = CONFIG["sitelibdir"]
|
10
|
+
unless $sitedir
|
11
|
+
version = CONFIG["MAJOR"] + "." + CONFIG["MINOR"]
|
12
|
+
$libdir = File.join(CONFIG["libdir"], "ruby", version)
|
13
|
+
$sitedir = $:.find {|x| x =~ /site_ruby/ }
|
14
|
+
if !$sitedir
|
15
|
+
$sitedir = File.join($libdir, "site_ruby")
|
16
|
+
elsif $sitedir !~ Regexp.quote(version)
|
17
|
+
$sitedir = File.join($sitedir, version)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# the acual gruntwork
|
22
|
+
Dir.chdir("lib")
|
23
|
+
|
24
|
+
Find.find("feed_validator", "feed_validator.rb") { |f|
|
25
|
+
if f[-3..-1] == ".rb"
|
26
|
+
File::install(f, File.join($sitedir, *f.split(/\//)), 0644, true)
|
27
|
+
else
|
28
|
+
File::makedirs(File.join($sitedir, *f.split(/\//)))
|
29
|
+
end
|
30
|
+
}
|
@@ -0,0 +1,164 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2006 Edgar Gonzalez <edgar@lacaraoscura.com>
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
#++
|
23
|
+
#
|
24
|
+
# Provide an interface to the {W3C Feed Validation online service}[http://validator.w3.org/feed/],
|
25
|
+
# based on its SOAP 1.2 support.
|
26
|
+
#
|
27
|
+
require 'net/http'
|
28
|
+
require 'cgi'
|
29
|
+
require 'rexml/document'
|
30
|
+
|
31
|
+
module W3C
|
32
|
+
|
33
|
+
# Implements an interface to the {W3C Feed Validation online service}[http://validator.w3.org/feed/],
|
34
|
+
# based on its SOAP 1.2 support.
|
35
|
+
#
|
36
|
+
# It helps to find errors in RSS or Atom feeds.
|
37
|
+
# ---
|
38
|
+
# Please remember that the {W3C Feed Validation service}[http://validator.w3.org/feed/] is a shared resource,
|
39
|
+
# so do not abuse it: you should make your scripts sleep between requests.
|
40
|
+
#
|
41
|
+
class FeedValidator
|
42
|
+
VERSION = "0.1.0"
|
43
|
+
|
44
|
+
# True if the w3c feed validation service not found errors in the feed.
|
45
|
+
#
|
46
|
+
attr_reader :valid
|
47
|
+
|
48
|
+
# The complete response (as Net::HTTPResponse object) sent it by the w3c feed validation service.
|
49
|
+
#
|
50
|
+
attr_reader :response
|
51
|
+
|
52
|
+
# Collection of _errors_ founded by the w3c feed validation service.
|
53
|
+
# Every error is a hash containing: <tt>:type</tt>, <tt>:line</tt>,
|
54
|
+
# <tt>:column</tt>, <tt>:text</tt>, <tt>:element</tt>
|
55
|
+
#
|
56
|
+
attr_reader :errors
|
57
|
+
|
58
|
+
# Collection of _warnings_ founded by the w3c feed validation service.
|
59
|
+
# Every error is a hash containing: <tt>:type</tt>, <tt>:line</tt>,
|
60
|
+
# <tt>:column</tt>, <tt>:text</tt>, <tt>:element</tt>
|
61
|
+
#
|
62
|
+
attr_reader :warnings
|
63
|
+
|
64
|
+
# Collection of _informations_ founded by the w3c feed validation service.
|
65
|
+
# Every error is a hash containing: <tt>:type</tt>, <tt>:line</tt>,
|
66
|
+
# <tt>:column</tt>, <tt>:text</tt>, <tt>:element</tt>
|
67
|
+
#
|
68
|
+
attr_reader :informations
|
69
|
+
|
70
|
+
# Initialize the feed validator object
|
71
|
+
#
|
72
|
+
def initialize
|
73
|
+
clear
|
74
|
+
end
|
75
|
+
|
76
|
+
# Validate the data provided.
|
77
|
+
# Returns a true value if the validation succeeded (regardless of whether the feed contains errors).
|
78
|
+
#
|
79
|
+
def validate_data(rawdata)
|
80
|
+
clear
|
81
|
+
params = "rawdata=#{CGI.escape(rawdata)}&manual=1&output=soap12"
|
82
|
+
begin
|
83
|
+
@response = Net::HTTP.start('validator.w3.org',80).post('/feed/check.cgi',params)
|
84
|
+
parse_response(@response.body)
|
85
|
+
return true
|
86
|
+
rescue
|
87
|
+
return false
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Validate the url provided.
|
92
|
+
# Returns a true value if the validation succeeded (regardless of whether the feed contains errors).
|
93
|
+
#
|
94
|
+
def validate_url(url)
|
95
|
+
clear
|
96
|
+
params = "url=#{CGI.escape(url)}&output=soap12"
|
97
|
+
begin
|
98
|
+
@response = Net::HTTP.get_response('validator.w3.org',"/feed/check.cgi?#{params}",80)
|
99
|
+
parse_response(@response.body)
|
100
|
+
return true
|
101
|
+
rescue
|
102
|
+
return false
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
alias :valid? :valid
|
107
|
+
|
108
|
+
def to_s
|
109
|
+
msg = "Vailidity: #{@valid}\n"
|
110
|
+
msg << "Errors count: #{@errors.size}\n"
|
111
|
+
@errors.each_with_index{ |item, i| msg << "(#{i+1}) type: #{item[:type]} | line: #{item[:line]} | column: #{item[:column]} | text: #{item[:text]},\n"}
|
112
|
+
msg << "Warnings count: #{@warnings.size}\n"
|
113
|
+
@warnings.each_with_index{ |item, i| msg << "(#{i+1}) type: #{item[:type]} | line: #{item[:line]} | column: #{item[:column]} | text: #{item[:text]},\n"}
|
114
|
+
msg << "Informations count: #{@informations.size}\n"
|
115
|
+
@informations.each_with_index{ |item, i| msg << "(#{i+1}) type: #{item[:type]} | line: #{item[:line]} | column: #{item[:column]} | text: #{item[:text]},\n"}
|
116
|
+
msg
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
def parse_response(response) #nodoc
|
122
|
+
xml = REXML::Document.new(response)
|
123
|
+
@valid = (/true/.match(xml.root.elements["env:Body/m:feedvalidationresponse/m:validity"].get_text.value))? true : false
|
124
|
+
unless @valid
|
125
|
+
xml.elements.each("env:Envelope/env:Body/m:feedvalidationresponse/m:errors/m:errorlist/error") do |error|
|
126
|
+
@errors << {
|
127
|
+
:type => error.elements["type"].get_text.value,
|
128
|
+
:line => error.elements["line"].get_text.value,
|
129
|
+
:column => error.elements["column"].get_text.value,
|
130
|
+
:text => error.elements["text"].get_text.value,
|
131
|
+
:element => error.elements["element"].get_text.value
|
132
|
+
}
|
133
|
+
end
|
134
|
+
end
|
135
|
+
xml.elements.each("env:Envelope/env:Body/m:feedvalidationresponse/m:warnings/m:warninglist/warning") do |warning|
|
136
|
+
@warnings << {
|
137
|
+
:type => warning.elements["type"].get_text.value,
|
138
|
+
:line => warning.elements["line"].get_text.value,
|
139
|
+
:column => warning.elements["column"].get_text.value,
|
140
|
+
:text => warning.elements["text"].get_text.value,
|
141
|
+
:element => warning.elements["element"].get_text.value
|
142
|
+
}
|
143
|
+
end
|
144
|
+
xml.elements.each("env:Envelope/env:Body/m:feedvalidationresponse/m:informations/m:infolist/information") do |info|
|
145
|
+
@informations << {
|
146
|
+
:type => info.elements["type"].get_text.value,
|
147
|
+
:line => info.elements["line"].get_text.value,
|
148
|
+
:column => info.elements["column"].get_text.value,
|
149
|
+
:text => info.elements["text"].get_text.value,
|
150
|
+
:element => info.elements["element"].get_text.value
|
151
|
+
}
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def clear #nodoc
|
156
|
+
@response = nil
|
157
|
+
@valid = false
|
158
|
+
@errors = []
|
159
|
+
@warnings = []
|
160
|
+
@informations = []
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2006 Edgar Gonzalez <edgar@lacaraoscura.com>
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
#++
|
23
|
+
|
24
|
+
require 'feed_validator'
|
25
|
+
require 'test/unit'
|
26
|
+
require 'tmpdir'
|
27
|
+
require 'md5'
|
28
|
+
|
29
|
+
module W3C
|
30
|
+
class FeedValidator
|
31
|
+
# Parse a response from the w3c feed validation service.
|
32
|
+
# Used by assert_valid_feed
|
33
|
+
def parse(response)
|
34
|
+
clear
|
35
|
+
if response.respond_to?(:body)
|
36
|
+
parse_response(response.body)
|
37
|
+
else
|
38
|
+
parse_response(response)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class Test::Unit::TestCase
|
45
|
+
|
46
|
+
# Assert that feed is valid according the {W3C Feed Validation online service}[http://validator.w3.org/feed/].
|
47
|
+
# By default, it validates the contents of @response.body, which is set after calling
|
48
|
+
# one of the get/post/etc helper methods in rails. You can also pass it a string to be validated.
|
49
|
+
# Validation errors, warnings and informations, if any, will be included in the output. The response from the validator
|
50
|
+
# service will be cached in the system temp directory to minimize duplicate calls.
|
51
|
+
#
|
52
|
+
# For example in Rails, if you have a FooController with an action Bar, put this in foo_controller_test.rb:
|
53
|
+
#
|
54
|
+
# def test_bar_valid_feed
|
55
|
+
# get :bar
|
56
|
+
# assert_valid_feed
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
def assert_valid_feed(fragment=@response.body)
|
60
|
+
v = W3C::FeedValidator.new()
|
61
|
+
filename = File.join Dir::tmpdir, 'feed.' + MD5.md5(fragment).to_s
|
62
|
+
begin
|
63
|
+
response = File.open filename do |f| Marshal.load(f) end
|
64
|
+
v.parse(response)
|
65
|
+
rescue
|
66
|
+
unless v.validate_data(fragment)
|
67
|
+
warn("Sorry! could not validate the feed.")
|
68
|
+
return assert(true,'')
|
69
|
+
end
|
70
|
+
File.open filename, 'w+' do |f| Marshal.dump v.response, f end
|
71
|
+
end
|
72
|
+
assert(v.valid?, v.valid? ? '' : v.to_s)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Class-level method to quickly create validation tests for a bunch of actions at once in Rails.
|
76
|
+
# For example, if you have a FooController with three actions, just add one line to foo_controller_test.rb:
|
77
|
+
#
|
78
|
+
# assert_valid_feed :bar, :baz, :qux
|
79
|
+
#
|
80
|
+
def self.assert_valid_feed(*actions)
|
81
|
+
actions.each do |action|
|
82
|
+
class_eval <<-EOF
|
83
|
+
def test_#{action}_valid_feed
|
84
|
+
get :#{action}
|
85
|
+
assert_valid_feed
|
86
|
+
end
|
87
|
+
EOF
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
data/rakefile
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/testtask'
|
4
|
+
require 'rake/rdoctask'
|
5
|
+
require 'rake/packagetask'
|
6
|
+
require 'rake/gempackagetask'
|
7
|
+
require 'rake/contrib/rubyforgepublisher'
|
8
|
+
|
9
|
+
PKG_NAME = 'feedvalidator'
|
10
|
+
PKG_VERSION = '0.1.0'
|
11
|
+
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
|
12
|
+
|
13
|
+
RELEASE_NAME = "REL #{PKG_VERSION}"
|
14
|
+
|
15
|
+
RUBY_FORGE_PROJECT = "feedvalidator"
|
16
|
+
RUBY_FORGE_USER = "edgar"
|
17
|
+
|
18
|
+
PKG_FILES = FileList[
|
19
|
+
"lib/**/*", "test/**/*", "examples/**/*", "doc/**/*", "[A-Z]*", "install.rb", "rakefile"
|
20
|
+
].exclude(/\b.svn\b|~$/)
|
21
|
+
|
22
|
+
desc "Default Task"
|
23
|
+
task :default => [ :test_all ]
|
24
|
+
|
25
|
+
# Run the unit tests
|
26
|
+
|
27
|
+
Rake::TestTask.new("test_all") { |t|
|
28
|
+
t.libs << "test"
|
29
|
+
t.pattern = 'test/**/*_test.rb'
|
30
|
+
t.verbose = true
|
31
|
+
}
|
32
|
+
|
33
|
+
# Generate the RDoc documentation
|
34
|
+
|
35
|
+
Rake::RDocTask.new { |rdoc|
|
36
|
+
rdoc.rdoc_dir = 'doc'
|
37
|
+
rdoc.title = "FeedValidator"
|
38
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
39
|
+
rdoc.template = "#{ENV['template']}.rb" if ENV['template']
|
40
|
+
rdoc.rdoc_files.include('README', 'CHANGELOG')
|
41
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
42
|
+
}
|
43
|
+
|
44
|
+
# Create compressed packages
|
45
|
+
|
46
|
+
dist_dirs = [ "lib", "test" ]
|
47
|
+
|
48
|
+
spec = Gem::Specification.new do |s|
|
49
|
+
s.name = PKG_NAME
|
50
|
+
s.version = PKG_VERSION
|
51
|
+
s.summary = "An interface to the W3C Feed Validation online service"
|
52
|
+
s.description = "Implements a simple system for generating UUIDs."
|
53
|
+
|
54
|
+
s.files = [ "rakefile", "install.rb", "README", "CHANGELOG" ]
|
55
|
+
dist_dirs.each do |dir|
|
56
|
+
s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if do |item|
|
57
|
+
item.include?( "\.svn" )
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
s.require_path = 'lib'
|
62
|
+
s.autorequire = 'feed_validator'
|
63
|
+
|
64
|
+
s.has_rdoc = true
|
65
|
+
s.extra_rdoc_files = %w( README )
|
66
|
+
s.rdoc_options.concat ['--main', 'README']
|
67
|
+
|
68
|
+
s.author = "Edgar Gonzalez"
|
69
|
+
s.email = "edgar@lacaraoscura.com"
|
70
|
+
s.homepage = "http://feedvalidator.rubyforge.org"
|
71
|
+
s.rubyforge_project = "feedvalidator"
|
72
|
+
end
|
73
|
+
|
74
|
+
Rake::GemPackageTask.new(spec) do |p|
|
75
|
+
p.gem_spec = spec
|
76
|
+
p.need_tar = true
|
77
|
+
p.need_zip = true
|
78
|
+
end
|
79
|
+
|
80
|
+
task :lines do
|
81
|
+
lines, codelines, total_lines, total_codelines = 0, 0, 0, 0
|
82
|
+
|
83
|
+
for file_name in FileList["lib/**/*.rb"]
|
84
|
+
f = File.open(file_name)
|
85
|
+
|
86
|
+
while line = f.gets
|
87
|
+
lines += 1
|
88
|
+
next if line =~ /^\s*$/
|
89
|
+
next if line =~ /^\s*#/
|
90
|
+
codelines += 1
|
91
|
+
end
|
92
|
+
puts "L: #{sprintf("%4d", lines)}, LOC #{sprintf("%4d", codelines)} | #{file_name}"
|
93
|
+
|
94
|
+
total_lines += lines
|
95
|
+
total_codelines += codelines
|
96
|
+
|
97
|
+
lines, codelines = 0, 0
|
98
|
+
end
|
99
|
+
|
100
|
+
puts "Total: Lines #{total_lines}, LOC #{total_codelines}"
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
# Publishing ------------------------------------------------------
|
105
|
+
|
106
|
+
desc "Publish the API documentation"
|
107
|
+
task :pdoc => [:rdoc] do
|
108
|
+
Rake::SshDirPublisher.new("edgar@rubyforge.org", "/var/www/gforge-projects/feedvalidator/", "doc").upload
|
109
|
+
end
|