darkfish-rdoc 1.1.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.
@@ -0,0 +1,441 @@
1
+ #
2
+ # Subversion Rake Tasks
3
+ # $Id: svn.rb 22 2008-08-08 01:30:06Z deveiant $
4
+ #
5
+ # Authors:
6
+ # * Michael Granger <ged@FaerieMUD.org>
7
+ #
8
+
9
+
10
+ require 'pp'
11
+ require 'yaml'
12
+ require 'date'
13
+
14
+ # Strftime format for tags/releases
15
+ TAG_TIMESTAMP_FORMAT = '%Y%m%d-%H%M%S'
16
+ TAG_TIMESTAMP_PATTERN = /\d{4}\d{2}\d{2}-\d{6}/
17
+
18
+ RELEASE_VERSION_PATTERN = /\d+\.\d+\.\d+/
19
+
20
+ DEFAULT_EDITOR = 'vi'
21
+ DEFAULT_KEYWORDS = %w[Date Rev Author URL Id]
22
+ KEYWORDED_FILEDIRS = %w[applets bin etc lib misc]
23
+ KEYWORDED_FILEPATTERN = /^(?:Rakefile|.*\.(?:rb|js|html|template))$/i
24
+
25
+ COMMIT_MSG_FILE = 'commit-msg.txt'
26
+
27
+ SVN_TRUNK_DIR = 'trunk' unless defined?( SVN_TRUNK_DIR )
28
+ SVN_RELEASES_DIR = 'branches' unless defined?( SVN_RELEASES_DIR )
29
+ SVN_BRANCHES_DIR = 'branches' unless defined?( SVN_BRANCHES_DIR )
30
+ SVN_TAGS_DIR = 'tags' unless defined?( SVN_TAGS_DIR )
31
+
32
+ FILE_INDENT = " " * 12
33
+ LOG_INDENT = " " * 3
34
+
35
+
36
+
37
+ ###
38
+ ### Subversion-specific Helpers
39
+ ###
40
+
41
+ ### Return a new tag for the given time
42
+ def make_new_tag( time=Time.now )
43
+ return time.strftime( TAG_TIMESTAMP_FORMAT )
44
+ end
45
+
46
+
47
+ ### Get the subversion information for the current working directory as
48
+ ### a hash.
49
+ def get_svn_info( dir='.' )
50
+ return {} unless File.directory?( File.join(dir, '.svn') )
51
+ info = IO.read( '|-' ) or exec 'svn', 'info', dir
52
+ return YAML.load( info ) # 'svn info' outputs valid YAML! Yay!
53
+ end
54
+
55
+
56
+ ### Get a list of the objects registered with subversion under the specified directory and
57
+ ### return them as an Array of Pathame objects.
58
+ def get_svn_filelist( dir='.' )
59
+ list = IO.read( '|-' ) or exec 'svn', 'st', '-v', '--ignore-externals', dir
60
+
61
+ # Split into lines, filter out the unknowns, and grab the filenames as Pathnames
62
+ # :FIXME: This will break if we ever put in a file with spaces in its name. This
63
+ # will likely be the least of our worries if we do so, however, so it's not worth
64
+ # the additional complexity to make it handle that case. If we do need that, there's
65
+ # always the --xml output for 'svn st'...
66
+ return list.split( $/ ).
67
+ reject {|line| line =~ /^\?/ }.
68
+ collect {|fn| Pathname(fn[/\S+$/]) }
69
+ end
70
+
71
+ ### Return the URL to the repository root for the specified +dir+.
72
+ def get_svn_repo_root( dir='.' )
73
+ info = get_svn_info( dir )
74
+ return info['Repository Root']
75
+ end
76
+
77
+
78
+ ### Return the Subversion URL to the given +dir+.
79
+ def get_svn_url( dir='.' )
80
+ info = get_svn_info( dir )
81
+ return info['URL']
82
+ end
83
+
84
+
85
+ ### Return the path of the specified +dir+ under the svn root of the
86
+ ### checkout.
87
+ def get_svn_path( dir='.' )
88
+ root = get_svn_repo_root( dir )
89
+ url = get_svn_url( dir )
90
+
91
+ return url.sub( root + '/', '' )
92
+ end
93
+
94
+
95
+ ### Return the keywords for the specified array of +files+ as a Hash keyed by filename.
96
+ def get_svn_keyword_map( files )
97
+ cmd = ['svn', 'pg', 'svn:keywords', *files]
98
+
99
+ # trace "Executing: svn pg svn:keywords " + files.join(' ')
100
+ output = IO.read( '|-' ) or exec( 'svn', 'pg', 'svn:keywords', *files )
101
+
102
+ kwmap = {}
103
+ output.split( "\n" ).each do |line|
104
+ next if line !~ /\s+-\s+/
105
+ path, keywords = line.split( /\s+-\s+/, 2 )
106
+ kwmap[ path ] = keywords.split
107
+ end
108
+
109
+ return kwmap
110
+ end
111
+
112
+
113
+ ### Return the latest revision number of the specified +dir+ as an Integer.
114
+ def get_svn_rev( dir='.' )
115
+ info = get_svn_info( dir )
116
+ return info['Revision']
117
+ end
118
+
119
+
120
+ ### Return the latest revision number of the specified +dir+ as an Integer.
121
+ def get_last_changed_rev( dir='.' )
122
+ info = get_svn_info( dir )
123
+ return info['Last Changed Rev']
124
+ end
125
+
126
+
127
+ ### Return a list of the entries at the specified Subversion url. If
128
+ ### no +url+ is specified, it will default to the list in the URL
129
+ ### corresponding to the current working directory.
130
+ def svn_ls( url=nil )
131
+ url ||= get_svn_url()
132
+ list = IO.read( '|-' ) or exec 'svn', 'ls', url
133
+
134
+ trace 'svn ls of %s: %p' % [url, list] if $trace
135
+
136
+ return [] if list.nil? || list.empty?
137
+ return list.split( $INPUT_RECORD_SEPARATOR )
138
+ end
139
+
140
+
141
+ ### Return the URL of the latest timestamp in the tags directory.
142
+ def get_latest_svn_timestamp_tag
143
+ rooturl = get_svn_repo_root()
144
+ tagsurl = rooturl + "/#{SVN_TAGS_DIR}"
145
+
146
+ tags = svn_ls( tagsurl ).grep( TAG_TIMESTAMP_PATTERN ).sort
147
+ return nil if tags.nil? || tags.empty?
148
+ return tagsurl + '/' + tags.last
149
+ end
150
+
151
+
152
+ ### Get a subversion diff of the specified targets and return it. If no targets are
153
+ ### specified, the current directory will be diffed instead.
154
+ def get_svn_diff( *targets )
155
+ targets << BASEDIR if targets.empty?
156
+ trace "Getting svn diff for targets: %p" % [targets]
157
+ log = IO.read( '|-' ) or exec 'svn', 'diff', *(targets.flatten)
158
+
159
+ return log
160
+ end
161
+
162
+
163
+ ### Return the URL of the latest timestamp in the tags directory.
164
+ def get_latest_release_tag
165
+ rooturl = get_svn_repo_root()
166
+ releaseurl = rooturl + "/#{SVN_RELEASES_DIR}"
167
+
168
+ tags = svn_ls( releaseurl ).grep( RELEASE_VERSION_PATTERN ).sort_by do |tag|
169
+ tag[RELEASE_VERSION_PATTERN].split('.').collect {|i| Integer(i) }
170
+ end
171
+ return nil if tags.empty?
172
+
173
+ return releaseurl + '/' + tags.last
174
+ end
175
+
176
+
177
+ ### Extract a diff from the specified subversion working +dir+ and return it.
178
+ def make_svn_commit_log( dir='.' )
179
+ diff = IO.read( '|-' ) or exec 'svn', 'diff'
180
+ fail "No differences." if diff.empty?
181
+
182
+ return diff
183
+ end
184
+
185
+
186
+ ### Extract the svn log from the specified subversion working +dir+,
187
+ ### starting from rev +start+ and ending with rev +finish+, and return it.
188
+ def make_svn_log( dir='.', start='PREV', finish='HEAD' )
189
+ trace "svn log -r#{start}:#{finish} #{dir}"
190
+ log = IO.read( '|-' ) or exec 'svn', 'log', "-r#{start}:#{finish}", dir
191
+ fail "No log between #{start} and #{finish}." if log.empty?
192
+
193
+ return log
194
+ end
195
+
196
+
197
+ ### Extract the verbose XML svn log from the specified subversion working +dir+,
198
+ ### starting from rev +start+ and ending with rev +finish+, and return it.
199
+ def make_xml_svn_log( dir='.', start='PREV', finish='HEAD' )
200
+ trace "svn log --xml --verbose -r#{start}:#{finish} #{dir}"
201
+ log = IO.read( '|-' ) or exec 'svn', 'log', '--verbose', '--xml', "-r#{start}:#{finish}", dir
202
+ fail "No log between #{start} and #{finish}." if log.empty?
203
+
204
+ return log
205
+ end
206
+
207
+
208
+ ### Create a changelog from the subversion log of the specified +dir+ and return it.
209
+ def make_svn_changelog( dir='.' )
210
+ require 'xml/libxml'
211
+
212
+ changelog = ''
213
+ path_prefix = '/' + get_svn_path( dir ) + '/'
214
+
215
+ xmllog = make_xml_svn_log( dir, 0 )
216
+
217
+ parser = XML::Parser.string( xmllog )
218
+ root = parser.parse.root
219
+ root.find( '//log/logentry' ).to_a.reverse.each do |entry|
220
+ trace "Making a changelog entry for r%s" % [ entry['revision'] ]
221
+
222
+ added = []
223
+ deleted = []
224
+ changed = []
225
+
226
+ entry.find( 'paths/path').each do |path|
227
+ pathname = path.content
228
+ pathname.sub!( path_prefix , '' ) if pathname.count('/') > 1
229
+
230
+ case path['action']
231
+ when 'A', 'R'
232
+ if path['copyfrom-path']
233
+ verb = path['action'] == 'A' ? 'renamed' : 'copied'
234
+ added << "%s\n#{FILE_INDENT}-> #{verb} from %s@r%s" % [
235
+ pathname,
236
+ path['copyfrom-path'],
237
+ path['copyfrom-rev'],
238
+ ]
239
+ else
240
+ added << "%s (new)" % [ pathname ]
241
+ end
242
+
243
+ when 'M'
244
+ changed << pathname
245
+
246
+ when 'D'
247
+ deleted << pathname
248
+
249
+ else
250
+ log "Unknown action %p in rev %d" % [ path['action'], entry['revision'] ]
251
+ end
252
+
253
+ end
254
+
255
+ date = Time.parse( entry.find_first('date').content )
256
+ author = entry.find_first( 'author' ).content
257
+ msg = entry.find_first( 'msg' ).content
258
+ rev = entry['revision']
259
+
260
+ changelog << "-- #{date.rfc2822} by #{author} (r#{rev}) -----\n"
261
+ changelog << " Added: " << humanize_file_list(added) << "\n" unless added.empty?
262
+ changelog << " Changed: " << humanize_file_list(changed) << "\n" unless changed.empty?
263
+ changelog << " Deleted: " << humanize_file_list(deleted) << "\n" unless deleted.empty?
264
+ changelog << "\n"
265
+
266
+ indent = msg[/^(\s*)/] + LOG_INDENT
267
+
268
+ changelog << indent << msg.strip.gsub(/\n\s*/m, "\n#{indent}")
269
+ changelog << "\n\n\n"
270
+ end
271
+
272
+ return changelog
273
+ end
274
+
275
+
276
+ ### Returns a human-scannable file list by joining and truncating the list if it's too long.
277
+ def humanize_file_list( list )
278
+ listtext = list[0..5].join( "\n#{FILE_INDENT}" )
279
+ if list.length > 5
280
+ listtext << " (and %d other/s)" % [ list.length - 5 ]
281
+ end
282
+
283
+ return listtext
284
+ end
285
+
286
+
287
+
288
+ ###
289
+ ### Tasks
290
+ ###
291
+
292
+ desc "Subversion tasks"
293
+ namespace :svn do
294
+
295
+ desc "Copy the HEAD revision of the current #{SVN_TRUNK_DIR}/ to #{SVN_TAGS_DIR}/ with a " +
296
+ "current timestamp."
297
+ task :tag do
298
+ svninfo = get_svn_info()
299
+ tag = make_new_tag()
300
+ svntrunk = svninfo['Repository Root'] + "/#{SVN_TRUNK_DIR}"
301
+ svntagdir = svninfo['Repository Root'] + "/#{SVN_TAGS_DIR}"
302
+ svntag = svntagdir + '/' + tag
303
+
304
+ desc = "Tagging trunk as #{svntag}"
305
+ ask_for_confirmation( desc ) do
306
+ msg = prompt_with_default( "Commit log: ", "Tagging for code push" )
307
+ run 'svn', 'cp', '-m', msg, svntrunk, svntag
308
+ end
309
+ end
310
+
311
+
312
+ desc "Copy the most recent tag to #{SVN_RELEASES_DIR}/#{PKG_VERSION}"
313
+ task :release do
314
+ last_tag = get_latest_svn_timestamp_tag()
315
+ svninfo = get_svn_info()
316
+ svntrunk = svninfo['Repository Root'] + "/#{SVN_TRUNK_DIR}"
317
+ svnrel = svninfo['Repository Root'] + "/#{SVN_RELEASES_DIR}"
318
+ release = PKG_VERSION
319
+ svnrelease = svnrel + '/' + release
320
+
321
+ releases = svn_ls( svnrel ).collect {|name| name.sub(%r{/$}, '') }
322
+ trace "Releases: %p" % [releases]
323
+ if releases.include?( release )
324
+ error "Version #{release} already has a branch (#{svnrelease}). Did you mean " +
325
+ "to increment the version in #{VERSION_FILE}?"
326
+ fail
327
+ else
328
+ trace "No #{release} version currently exists"
329
+ end
330
+
331
+ desc = "Tagging trunk as #{svnrelease}..."
332
+ ask_for_confirmation( desc ) do
333
+ msg = prompt_with_default( "Commit log: ", "Branching for release" )
334
+ run 'svn', 'cp', '-m', msg, svntrunk, svnrelease
335
+ end
336
+ end
337
+
338
+ ### Task for debugging the #get_target_args helper
339
+ task :show_targets do
340
+ $stdout.puts "Targets from ARGV (%p): %p" % [ARGV, get_target_args()]
341
+ end
342
+
343
+
344
+ desc "Generate a commit log"
345
+ task :commitlog => [COMMIT_MSG_FILE]
346
+
347
+ desc "Show the (pre-edited) commit log for the current directory"
348
+ task :show_commitlog do
349
+ puts make_svn_commit_log()
350
+ end
351
+
352
+
353
+ file COMMIT_MSG_FILE do
354
+ diff = make_svn_commit_log()
355
+
356
+ File.open( COMMIT_MSG_FILE, File::WRONLY|File::EXCL|File::CREAT ) do |fh|
357
+ fh.print( diff )
358
+ end
359
+
360
+ editor = ENV['EDITOR'] || ENV['VISUAL'] || DEFAULT_EDITOR
361
+ system editor, COMMIT_MSG_FILE
362
+ unless $?.success?
363
+ fail "Editor exited uncleanly."
364
+ end
365
+ end
366
+
367
+
368
+ desc "Update from Subversion"
369
+ task :update do
370
+ run 'svn', 'up', '--ignore-externals'
371
+ end
372
+
373
+
374
+ desc "Check in all the changes in your current working copy"
375
+ task :checkin => ['svn:update', 'coverage:verify', 'svn:fix_keywords', COMMIT_MSG_FILE] do
376
+ targets = get_target_args()
377
+ $deferr.puts '---', File.read( COMMIT_MSG_FILE ), '---'
378
+ ask_for_confirmation( "Continue with checkin?" ) do
379
+ run 'svn', 'ci', '-F', COMMIT_MSG_FILE, targets
380
+ rm_f COMMIT_MSG_FILE
381
+ end
382
+ end
383
+ task :commit => :checkin
384
+ task :ci => :checkin
385
+
386
+
387
+ task :clean do
388
+ rm_f COMMIT_MSG_FILE
389
+ end
390
+
391
+
392
+ desc "Check and fix any missing keywords for any files in the project which need them"
393
+ task :fix_keywords do
394
+ log "Checking subversion keywords..."
395
+ paths = get_svn_filelist( BASEDIR ).
396
+ select {|path| path.file? && path.to_s =~ KEYWORDED_FILEPATTERN }
397
+
398
+ trace "Looking at %d paths for keywords:\n %p" % [paths.length, paths]
399
+ kwmap = get_svn_keyword_map( paths )
400
+
401
+ buf = ''
402
+ PP.pp( kwmap, buf, 132 )
403
+ trace "keyword map is: %s" % [buf]
404
+
405
+ files_needing_fixups = paths.find_all do |path|
406
+ (kwmap[path.to_s] & DEFAULT_KEYWORDS) != DEFAULT_KEYWORDS
407
+ end
408
+
409
+ unless files_needing_fixups.empty?
410
+ $deferr.puts "Files needing keyword fixes: ",
411
+ files_needing_fixups.collect {|f|
412
+ " %s: %s" % [f, kwmap[f] ? kwmap[f].join(' ') : "(no keywords)"]
413
+ }
414
+ ask_for_confirmation( "Will add default keywords to these files." ) do
415
+ run 'svn', 'ps', 'svn:keywords', DEFAULT_KEYWORDS.join(' '), *files_needing_fixups
416
+ end
417
+ else
418
+ log "Keywords are all up to date."
419
+ end
420
+ end
421
+
422
+
423
+ task :debug_helpers do
424
+ methods = [
425
+ :make_new_tag,
426
+ :get_svn_info,
427
+ :get_svn_repo_root,
428
+ :get_svn_url,
429
+ :get_svn_path,
430
+ :svn_ls,
431
+ :get_latest_svn_timestamp_tag,
432
+ ]
433
+ maxlen = methods.collect {|sym| sym.to_s.length }.max
434
+
435
+ methods.each do |meth|
436
+ res = send( meth )
437
+ puts "%*s => %p" % [ maxlen, colorize(meth.to_s, :cyan), res ]
438
+ end
439
+ end
440
+ end
441
+
@@ -0,0 +1,191 @@
1
+ #
2
+ # Rake tasklib for testing tasks
3
+ # $Id: testing.rb 14 2008-07-23 23:16:30Z deveiant $
4
+ #
5
+ # Authors:
6
+ # * Michael Granger <ged@FaerieMUD.org>
7
+ #
8
+
9
+ COVERAGE_MINIMUM = 85.0 unless defined?( COVERAGE_MINIMUM )
10
+ SPEC_FILES = [] unless defined?( SPEC_FILES )
11
+ TEST_FILES = [] unless defined?( TEST_FILES )
12
+
13
+ COMMON_SPEC_OPTS = ['-c', '-f', 's'] unless defined?( COMMON_SPEC_OPTS )
14
+
15
+ COVERAGE_TARGETDIR = BASEDIR + 'coverage' unless defined?( COVERAGE_TARGETDIR )
16
+ RCOV_EXCLUDES = 'spec,tests,/Library/Ruby,/var/lib,/usr/local/lib' unless
17
+ defined?( RCOV_EXCLUDES )
18
+
19
+
20
+ desc "Run all defined tests"
21
+ task :test do
22
+ unless SPEC_FILES.empty?
23
+ log "Running specs"
24
+ Rake::Task['spec:quiet'].invoke
25
+ end
26
+
27
+ unless TEST_FILES.empty?
28
+ log "Running unit tests"
29
+ Rake::Task[:unittests].invoke
30
+ end
31
+ end
32
+
33
+
34
+ ### RSpec specifications
35
+ begin
36
+ gem 'rspec', '>= 1.1.3'
37
+ require 'spec/rake/spectask'
38
+
39
+ ### Task: spec
40
+ Spec::Rake::SpecTask.new( :spec ) do |task|
41
+ task.spec_files = SPEC_FILES
42
+ task.libs += [LIBDIR]
43
+ task.spec_opts = COMMON_SPEC_OPTS
44
+ end
45
+
46
+
47
+ namespace :spec do
48
+ desc "Run rspec every time there's a change to one of the files"
49
+ task :autotest do
50
+ require 'autotest/rspec'
51
+
52
+ autotester = Autotest::Rspec.new
53
+ autotester.exceptions = %r{\.svn|\.skel}
54
+ autotester.run
55
+ end
56
+
57
+
58
+ desc "Generate quiet output"
59
+ Spec::Rake::SpecTask.new( :quiet ) do |task|
60
+ task.spec_files = SPEC_FILES
61
+ task.spec_opts = ['-f', 'p', '-D']
62
+ end
63
+
64
+ desc "Generate HTML output for a spec run"
65
+ Spec::Rake::SpecTask.new( :html ) do |task|
66
+ task.spec_files = SPEC_FILES
67
+ task.spec_opts = ['-f','h', '-D']
68
+ end
69
+
70
+ desc "Generate plain-text output for a CruiseControl.rb build"
71
+ Spec::Rake::SpecTask.new( :text ) do |task|
72
+ task.spec_files = SPEC_FILES
73
+ task.spec_opts = ['-f','p']
74
+ end
75
+ end
76
+ rescue LoadError => err
77
+ task :no_rspec do
78
+ $stderr.puts "Specification tasks not defined: %s" % [ err.message ]
79
+ end
80
+
81
+ task :spec => :no_rspec
82
+ namespace :spec do
83
+ task :autotest => :no_rspec
84
+ task :quiet => :no_rspec
85
+ task :html => :no_rspec
86
+ task :text => :no_rspec
87
+ end
88
+ end
89
+
90
+
91
+ ### Test::Unit tests
92
+ begin
93
+ require 'rake/testtask'
94
+
95
+ Rake::TestTask.new( :unittests ) do |task|
96
+ task.libs += [LIBDIR]
97
+ task.test_files = TEST_FILES
98
+ task.verbose = true
99
+ end
100
+
101
+ rescue LoadError => err
102
+ task :no_test do
103
+ $stderr.puts "Test tasks not defined: %s" % [ err.message ]
104
+ end
105
+
106
+ task :unittests => :no_rspec
107
+ end
108
+
109
+
110
+ ### RCov (via RSpec) tasks
111
+ begin
112
+ gem 'rcov'
113
+ gem 'rspec', '>= 1.1.3'
114
+
115
+ ### Task: coverage (via RCov)
116
+ ### Task: rcov
117
+ desc "Build test coverage reports"
118
+ unless SPEC_FILES.empty?
119
+ Spec::Rake::SpecTask.new( :coverage ) do |task|
120
+ task.spec_files = SPEC_FILES
121
+ task.libs += [LIBDIR]
122
+ task.spec_opts = ['-f', 'p', '-b']
123
+ task.rcov_opts = RCOV_OPTS
124
+ task.rcov = true
125
+ end
126
+ end
127
+ unless TEST_FILES.empty?
128
+ require 'rcov/rcovtask'
129
+
130
+ Rcov::RcovTask.new do |task|
131
+ task.libs += [LIBDIR]
132
+ task.test_files = TEST_FILES
133
+ task.verbose = true
134
+ task.rcov_opts = RCOV_OPTS
135
+ end
136
+ end
137
+
138
+
139
+ task :rcov => [:coverage] do; end
140
+
141
+ ### Other coverage tasks
142
+ namespace :coverage do
143
+ desc "Generate a detailed text coverage report"
144
+ Spec::Rake::SpecTask.new( :text ) do |task|
145
+ task.spec_files = SPEC_FILES
146
+ task.rcov_opts = RCOV_OPTS + ['--text-report']
147
+ task.rcov = true
148
+ end
149
+
150
+ desc "Show differences in coverage from last run"
151
+ Spec::Rake::SpecTask.new( :diff ) do |task|
152
+ task.spec_files = SPEC_FILES
153
+ task.rcov_opts = ['--text-coverage-diff']
154
+ task.rcov = true
155
+ end
156
+
157
+ ### Task: verify coverage
158
+ desc "Build coverage statistics"
159
+ VerifyTask.new( :verify => :rcov ) do |task|
160
+ task.threshold = COVERAGE_MINIMUM
161
+ end
162
+
163
+ desc "Run RCov in 'spec-only' mode to check coverage from specs"
164
+ Spec::Rake::SpecTask.new( :speconly ) do |task|
165
+ task.spec_files = SPEC_FILES
166
+ task.rcov_opts = ['--exclude', RCOV_EXCLUDES, '--text-report', '--save']
167
+ task.rcov = true
168
+ end
169
+ end
170
+
171
+ task :clobber_coverage do
172
+ rmtree( COVERAGE_TARGETDIR )
173
+ end
174
+
175
+ rescue LoadError => err
176
+ task :no_rcov do
177
+ $stderr.puts "Coverage tasks not defined: RSpec+RCov tasklib not available: %s" %
178
+ [ err.message ]
179
+ end
180
+
181
+ task :coverage => :no_rcov
182
+ task :clobber_coverage
183
+ task :rcov => :no_rcov
184
+ namespace :coverage do
185
+ task :text => :no_rcov
186
+ task :diff => :no_rcov
187
+ end
188
+ task :verify => :no_rcov
189
+ end
190
+
191
+