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.
- data/ChangeLog +194 -0
- data/LICENSE +27 -0
- data/README +100 -0
- data/Rakefile +238 -0
- data/Rakefile.local +4 -0
- data/lib/darkfish-rdoc.rb +26 -0
- data/lib/rdoc/generator/darkfish.rb +456 -0
- data/rake/dependencies.rb +62 -0
- data/rake/helpers.rb +350 -0
- data/rake/manual.rb +384 -0
- data/rake/packaging.rb +112 -0
- data/rake/publishing.rb +301 -0
- data/rake/rdoc.rb +30 -0
- data/rake/style.rb +62 -0
- data/rake/svn.rb +441 -0
- data/rake/testing.rb +191 -0
- data/rake/verifytask.rb +64 -0
- metadata +86 -0
data/Rakefile.local
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# Bootstrap file for the Darkfish RDoc generator -- since RDoc doesn't
|
2
|
+
# know about gems, this file is necessary to get things loaded before RDoc
|
3
|
+
# checks to see what generators it knows about.
|
4
|
+
|
5
|
+
gem 'rdoc', '>= 2.0.0'
|
6
|
+
|
7
|
+
require 'pathname'
|
8
|
+
require 'rdoc/rdoc' unless defined?( RDoc ) && defined?( RDoc::RDoc )
|
9
|
+
|
10
|
+
### Version for the Rakefile (update this in rdoc/generator/darkfish.rb too)
|
11
|
+
# VERSION = '1.1.1'
|
12
|
+
|
13
|
+
begin
|
14
|
+
generator_dir = Pathname.new( __FILE__ ).dirname + 'rdoc/generator'
|
15
|
+
|
16
|
+
# Add the darkfish generator to the ones RDoc knows about
|
17
|
+
generator = RDoc::RDoc::Generator.new(
|
18
|
+
generator_dir + 'darkfish.rb',
|
19
|
+
:Darkfish,
|
20
|
+
'darkfish'
|
21
|
+
)
|
22
|
+
|
23
|
+
$stderr.puts "Adding 'darkfish' as an RDoc generator type" if $DEBUG
|
24
|
+
RDoc::RDoc::GENERATORS[ 'darkfish' ] = generator
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,456 @@
|
|
1
|
+
#!ruby
|
2
|
+
#
|
3
|
+
# Darkfish RDoc HTML Generator
|
4
|
+
# $Id: darkfish.rb 18 2008-08-07 06:56:40Z deveiant $
|
5
|
+
#
|
6
|
+
# Author: Michael Granger <ged@FaerieMUD.org>
|
7
|
+
#
|
8
|
+
# == License
|
9
|
+
#
|
10
|
+
# Copyright (c) 2007, 2008, The FaerieMUD Consortium
|
11
|
+
# All rights reserved.
|
12
|
+
#
|
13
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
14
|
+
# of this software and associated documentation files (the "Software"), to deal
|
15
|
+
# in the Software without restriction, including without limitation the rights
|
16
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
17
|
+
# copies of the Software, and to permit persons to whom the Software is
|
18
|
+
# furnished to do so, subject to the following conditions:
|
19
|
+
#
|
20
|
+
# The above copyright notice and this permission notice shall be included in
|
21
|
+
# all copies or substantial portions of the Software.
|
22
|
+
#
|
23
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
24
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
25
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
26
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
27
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
28
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
29
|
+
# THE SOFTWARE.
|
30
|
+
#
|
31
|
+
|
32
|
+
|
33
|
+
require 'rubygems'
|
34
|
+
gem 'rdoc', '>= 2.0.0'
|
35
|
+
|
36
|
+
require 'pp'
|
37
|
+
require 'pathname'
|
38
|
+
require 'fileutils'
|
39
|
+
require 'erb'
|
40
|
+
require 'yaml'
|
41
|
+
|
42
|
+
require 'rdoc/rdoc'
|
43
|
+
require 'rdoc/generator/xml'
|
44
|
+
require 'rdoc/generator/html'
|
45
|
+
|
46
|
+
### A erb-based RDoc HTML generator
|
47
|
+
class RDoc::Generator::Darkfish < RDoc::Generator::XML
|
48
|
+
include ERB::Util
|
49
|
+
|
50
|
+
# Subversion rev
|
51
|
+
SVNRev = %$Rev: 18 $
|
52
|
+
|
53
|
+
# Subversion ID
|
54
|
+
SVNId = %$Id: darkfish.rb 18 2008-08-07 06:56:40Z deveiant $
|
55
|
+
|
56
|
+
# Path to this file's parent directory. Used to find templates and other
|
57
|
+
# resources.
|
58
|
+
GENERATOR_DIR = Pathname.new( __FILE__ ).expand_path.dirname
|
59
|
+
|
60
|
+
# Darkfish Version (update this in )
|
61
|
+
VERSION = '1.1.1'
|
62
|
+
|
63
|
+
|
64
|
+
### Standard generator factory method
|
65
|
+
def self::for( options )
|
66
|
+
new( options )
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
### Initialize a few instance variables before we start
|
71
|
+
def initialize( *args )
|
72
|
+
@template = nil
|
73
|
+
@template_dir = GENERATOR_DIR + 'template/darkfish'
|
74
|
+
|
75
|
+
@files = []
|
76
|
+
@classes = []
|
77
|
+
@hyperlinks = {}
|
78
|
+
|
79
|
+
@basedir = Pathname.pwd.expand_path
|
80
|
+
|
81
|
+
super
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
### Output progress information if debugging is enabled
|
86
|
+
def debug_msg( *msg )
|
87
|
+
return unless $DEBUG
|
88
|
+
$stderr.puts( *msg )
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
### Create the directories the generated docs will live in if
|
93
|
+
### they don't already exist.
|
94
|
+
def gen_sub_directories
|
95
|
+
@outputdir.mkpath
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
### Copy over the stylesheet into the appropriate place in the
|
100
|
+
### output directory.
|
101
|
+
def write_style_sheet
|
102
|
+
debug_msg "Copying over static files"
|
103
|
+
staticfiles = %w[rdoc.css js images]
|
104
|
+
staticfiles.each do |path|
|
105
|
+
FileUtils.cp_r( @template_dir + path, '.', :verbose => $DEBUG )
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
|
111
|
+
### Build the initial indices and output objects
|
112
|
+
### based on an array of TopLevel objects containing
|
113
|
+
### the extracted information.
|
114
|
+
def generate( toplevels )
|
115
|
+
@outputdir = Pathname.new( @options.op_dir ).expand_path( @basedir )
|
116
|
+
@files, @classes = RDoc::Generator::Context.build_indicies( toplevels, @options )
|
117
|
+
|
118
|
+
# Now actually write the output
|
119
|
+
generate_xhtml( @options, @files, @classes )
|
120
|
+
|
121
|
+
rescue StandardError => err
|
122
|
+
debug_msg "%s: %s\n %s" % [ err.class.name, err.message, err.backtrace.join("\n ") ]
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
### No-opped
|
127
|
+
def load_html_template # :nodoc:
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
### Generate output
|
132
|
+
def generate_xhtml( options, files, classes )
|
133
|
+
files = gen_into( @files )
|
134
|
+
classes = gen_into( @classes )
|
135
|
+
|
136
|
+
# Make a hash of class info keyed by class name
|
137
|
+
classes_by_classname = classes.inject({}) {|hash, classinfo|
|
138
|
+
hash[ classinfo['full_name'] ] = classinfo
|
139
|
+
hash[ classinfo['full_name'] ][:outfile] =
|
140
|
+
classinfo['full_name'].gsub( /::/, '/' ) + '.html'
|
141
|
+
hash
|
142
|
+
}
|
143
|
+
|
144
|
+
# Make a hash of
|
145
|
+
files_by_path = files.inject({}) {|hash, fileinfo|
|
146
|
+
hash[ fileinfo['full_path'] ] = fileinfo
|
147
|
+
hash[ fileinfo['full_path'] ][:outfile] =
|
148
|
+
fileinfo['full_path'] + '.html'
|
149
|
+
hash
|
150
|
+
}
|
151
|
+
|
152
|
+
self.write_style_sheet
|
153
|
+
self.generate_index( options, files_by_path, classes_by_classname )
|
154
|
+
self.generate_class_files( options, files_by_path, classes_by_classname )
|
155
|
+
self.generate_file_files( options, files_by_path, classes_by_classname )
|
156
|
+
end
|
157
|
+
|
158
|
+
|
159
|
+
|
160
|
+
#########
|
161
|
+
protected
|
162
|
+
#########
|
163
|
+
|
164
|
+
### Return a list of the documented modules sorted by salience first, then by name.
|
165
|
+
def get_sorted_module_list( classes )
|
166
|
+
nscounts = classes.keys.inject({}) do |counthash, name|
|
167
|
+
toplevel = name.gsub( /::.*/, '' )
|
168
|
+
counthash[toplevel] ||= 0
|
169
|
+
counthash[toplevel] += 1
|
170
|
+
|
171
|
+
counthash
|
172
|
+
end
|
173
|
+
|
174
|
+
# Sort based on how often the toplevel namespace occurs, and then on the name
|
175
|
+
# of the module -- this works for projects that put their stuff into a
|
176
|
+
# namespace, of course, but doesn't hurt if they don't.
|
177
|
+
return classes.keys.sort_by do |name|
|
178
|
+
toplevel = name.gsub( /::.*/, '' )
|
179
|
+
[
|
180
|
+
nscounts[ toplevel ] * -1,
|
181
|
+
name
|
182
|
+
]
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
|
187
|
+
### Generate an index page which lists all the classes which
|
188
|
+
### are documented.
|
189
|
+
def generate_index( options, files, classes )
|
190
|
+
debug_msg "Rendering the index page..."
|
191
|
+
|
192
|
+
templatefile = @template_dir + 'index.rhtml'
|
193
|
+
template_src = templatefile.read
|
194
|
+
template = ERB.new( template_src, nil, '<>' )
|
195
|
+
template.filename = templatefile.to_s
|
196
|
+
context = binding()
|
197
|
+
|
198
|
+
modsort = self.get_sorted_module_list( classes )
|
199
|
+
output = nil
|
200
|
+
begin
|
201
|
+
output = template.result( context )
|
202
|
+
rescue NoMethodError => err
|
203
|
+
raise "Error while evaluating %s: %s (at %p)" % [
|
204
|
+
templatefile,
|
205
|
+
err.message,
|
206
|
+
eval( "_erbout[-50,50]", context )
|
207
|
+
]
|
208
|
+
end
|
209
|
+
|
210
|
+
outfile = @basedir + @options.op_dir + 'index.html'
|
211
|
+
unless $dryrun
|
212
|
+
debug_msg "Outputting to %s" % [outfile.expand_path]
|
213
|
+
outfile.open( 'w', 0644 ) do |fh|
|
214
|
+
fh.print( output )
|
215
|
+
end
|
216
|
+
else
|
217
|
+
debug_msg "Would have output to %s" % [outfile.expand_path]
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
|
222
|
+
|
223
|
+
### Generate a documentation file for each class present in the
|
224
|
+
### given hash of +classes+.
|
225
|
+
def generate_class_files( options, files, classes )
|
226
|
+
debug_msg "Generating class documentation in #@outputdir"
|
227
|
+
templatefile = @template_dir + 'classpage.rhtml'
|
228
|
+
outputdir = @outputdir
|
229
|
+
|
230
|
+
modsort = self.get_sorted_module_list( classes )
|
231
|
+
|
232
|
+
classes.sort_by {|k,v| k }.each do |classname, classinfo|
|
233
|
+
debug_msg " working on %s (%s)" % [ classname, classinfo[:outfile] ]
|
234
|
+
outfile = outputdir + classinfo[:outfile]
|
235
|
+
rel_prefix = outputdir.relative_path_from( outfile.dirname )
|
236
|
+
svninfo = self.get_svninfo( classinfo )
|
237
|
+
|
238
|
+
self.render_template( templatefile, binding(), outfile )
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
|
243
|
+
### Generate a documentation file for each file present in the
|
244
|
+
### given hash of +files+.
|
245
|
+
def generate_file_files( options, files, classes )
|
246
|
+
debug_msg "Generating file documentation in #@outputdir"
|
247
|
+
templatefile = @template_dir + 'filepage.rhtml'
|
248
|
+
|
249
|
+
files.sort_by {|k,v| k }.each do |path, fileinfo|
|
250
|
+
outfile = @outputdir + fileinfo[:outfile]
|
251
|
+
debug_msg " working on %s (%s)" % [ path, outfile ]
|
252
|
+
rel_prefix = @outputdir.relative_path_from( outfile.dirname )
|
253
|
+
context = binding()
|
254
|
+
|
255
|
+
debug_msg " rending #{outfile}"
|
256
|
+
self.render_template( templatefile, binding(), outfile )
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
|
261
|
+
### Return a string describing the amount of time in the given number of
|
262
|
+
### seconds in terms a human can understand easily.
|
263
|
+
def time_delta_string( seconds )
|
264
|
+
return 'less than a minute' if seconds < 1.minute
|
265
|
+
return (seconds / 1.minute).to_s + ' minute' + (seconds/60 == 1 ? '' : 's') if seconds < 50.minutes
|
266
|
+
return 'about one hour' if seconds < 90.minutes
|
267
|
+
return (seconds / 1.hour).to_s + ' hours' if seconds < 18.hours
|
268
|
+
return 'one day' if seconds < 1.day
|
269
|
+
return 'about one day' if seconds < 2.days
|
270
|
+
return (seconds / 1.day).to_s + ' days' if seconds < 1.week
|
271
|
+
return 'about one week' if seconds < 2.week
|
272
|
+
return (seconds / 1.week).to_s + ' weeks' if seconds < 3.months
|
273
|
+
return (seconds / 1.month).to_s + ' months' if seconds < 1.year
|
274
|
+
return (seconds / 1.year).to_s + ' years'
|
275
|
+
end
|
276
|
+
|
277
|
+
|
278
|
+
# %q$Id: darkfish.rb 18 2008-08-07 06:56:40Z deveiant $"
|
279
|
+
SVNID_PATTERN = /
|
280
|
+
\$Id:\s
|
281
|
+
(\S+)\s # filename
|
282
|
+
(\d+)\s # rev
|
283
|
+
(\d{4}-\d{2}-\d{2})\s # Date (YYYY-MM-DD)
|
284
|
+
(\d{2}:\d{2}:\d{2}Z)\s # Time (HH:MM:SSZ)
|
285
|
+
(\w+)\s # committer
|
286
|
+
\$$
|
287
|
+
/x
|
288
|
+
|
289
|
+
### Try to extract Subversion information out of the first constant whose value looks like
|
290
|
+
### a subversion Id tag. If no matching constant is found, and empty hash is returned.
|
291
|
+
def get_svninfo( classinfo )
|
292
|
+
return {} unless classinfo['sections']
|
293
|
+
constants = classinfo['sections'].first['constants'] or return {}
|
294
|
+
|
295
|
+
constants.find {|c| c['value'] =~ SVNID_PATTERN } or return {}
|
296
|
+
|
297
|
+
filename, rev, date, time, committer = $~.captures
|
298
|
+
commitdate = Time.parse( date + ' ' + time )
|
299
|
+
|
300
|
+
return {
|
301
|
+
:filename => filename,
|
302
|
+
:rev => Integer( rev ),
|
303
|
+
:commitdate => commitdate,
|
304
|
+
:commitdelta => time_delta_string( Time.now.to_i - commitdate.to_i ),
|
305
|
+
:committer => committer,
|
306
|
+
}
|
307
|
+
end
|
308
|
+
|
309
|
+
|
310
|
+
### Load and render the erb template in the given +templatefile+ within the specified
|
311
|
+
### +context+ (a Binding object) and write it out to +outfile+. Both +templatefile+ and
|
312
|
+
### +outfile+ should be Pathname-like objects.
|
313
|
+
def render_template( templatefile, context, outfile )
|
314
|
+
template_src = templatefile.read
|
315
|
+
template = ERB.new( template_src, nil, '<>' )
|
316
|
+
template.filename = templatefile.to_s
|
317
|
+
|
318
|
+
output = begin
|
319
|
+
template.result( context )
|
320
|
+
rescue NoMethodError => err
|
321
|
+
raise "Error while evaluating %s: %s (at %p)" % [
|
322
|
+
templatefile.to_s,
|
323
|
+
err.message,
|
324
|
+
eval( "_erbout[-50,50]", context )
|
325
|
+
]
|
326
|
+
end
|
327
|
+
|
328
|
+
unless $dryrun
|
329
|
+
outfile.dirname.mkpath
|
330
|
+
outfile.open( 'w', 0644 ) do |ofh|
|
331
|
+
ofh.print( output )
|
332
|
+
end
|
333
|
+
else
|
334
|
+
debug_msg " would have written %d bytes to %s" %
|
335
|
+
[ output.length, outfile ]
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
end # Roc::Generator::Darkfish
|
340
|
+
|
341
|
+
# Silly alias for RDoc's silly upcased 'class_name'
|
342
|
+
RDoc::Generator::DARKFISH = RDoc::Generator::Darkfish
|
343
|
+
|
344
|
+
|
345
|
+
# :stopdoc:
|
346
|
+
|
347
|
+
### Time constants
|
348
|
+
module TimeConstantMethods # :nodoc:
|
349
|
+
|
350
|
+
### Number of seconds (returns receiver unmodified)
|
351
|
+
def seconds
|
352
|
+
return self
|
353
|
+
end
|
354
|
+
alias_method :second, :seconds
|
355
|
+
|
356
|
+
### Returns number of seconds in <receiver> minutes
|
357
|
+
def minutes
|
358
|
+
return self * 60
|
359
|
+
end
|
360
|
+
alias_method :minute, :minutes
|
361
|
+
|
362
|
+
### Returns the number of seconds in <receiver> hours
|
363
|
+
def hours
|
364
|
+
return self * 60.minutes
|
365
|
+
end
|
366
|
+
alias_method :hour, :hours
|
367
|
+
|
368
|
+
### Returns the number of seconds in <receiver> days
|
369
|
+
def days
|
370
|
+
return self * 24.hours
|
371
|
+
end
|
372
|
+
alias_method :day, :days
|
373
|
+
|
374
|
+
### Return the number of seconds in <receiver> weeks
|
375
|
+
def weeks
|
376
|
+
return self * 7.days
|
377
|
+
end
|
378
|
+
alias_method :week, :weeks
|
379
|
+
|
380
|
+
### Returns the number of seconds in <receiver> fortnights
|
381
|
+
def fortnights
|
382
|
+
return self * 2.weeks
|
383
|
+
end
|
384
|
+
alias_method :fortnight, :fortnights
|
385
|
+
|
386
|
+
### Returns the number of seconds in <receiver> months (approximate)
|
387
|
+
def months
|
388
|
+
return self * 30.days
|
389
|
+
end
|
390
|
+
alias_method :month, :months
|
391
|
+
|
392
|
+
### Returns the number of seconds in <receiver> years (approximate)
|
393
|
+
def years
|
394
|
+
return (self * 365.25.days).to_i
|
395
|
+
end
|
396
|
+
alias_method :year, :years
|
397
|
+
|
398
|
+
|
399
|
+
### Returns the Time <receiver> number of seconds before the
|
400
|
+
### specified +time+. E.g., 2.hours.before( header.expiration )
|
401
|
+
def before( time )
|
402
|
+
return time - self
|
403
|
+
end
|
404
|
+
|
405
|
+
|
406
|
+
### Returns the Time <receiver> number of seconds ago. (e.g.,
|
407
|
+
### expiration > 2.hours.ago )
|
408
|
+
def ago
|
409
|
+
return self.before( ::Time.now )
|
410
|
+
end
|
411
|
+
|
412
|
+
|
413
|
+
### Returns the Time <receiver> number of seconds after the given +time+.
|
414
|
+
### E.g., 10.minutes.after( header.expiration )
|
415
|
+
def after( time )
|
416
|
+
return time + self
|
417
|
+
end
|
418
|
+
|
419
|
+
# Reads best without arguments: 10.minutes.from_now
|
420
|
+
def from_now
|
421
|
+
return self.after( ::Time.now )
|
422
|
+
end
|
423
|
+
end # module TimeConstantMethods
|
424
|
+
|
425
|
+
|
426
|
+
# Extend Numeric with time constants
|
427
|
+
class Numeric # :nodoc:
|
428
|
+
include TimeConstantMethods
|
429
|
+
end
|
430
|
+
|
431
|
+
|
432
|
+
### Monkeypatch RDoc::Generator::Method so it works with line numbers
|
433
|
+
### turned on and $DEBUG = true. Also make it use a conditional instead
|
434
|
+
### of a side-effect to get the initial blank line.
|
435
|
+
class RDoc::Generator::Method # :nodoc:
|
436
|
+
def add_line_numbers(src)
|
437
|
+
if src =~ /\A.*, line (\d+)/
|
438
|
+
first = $1.to_i - 1
|
439
|
+
last = first + src.count("\n")
|
440
|
+
size = last.to_s.length
|
441
|
+
|
442
|
+
line = first
|
443
|
+
src.gsub!(/^/) do
|
444
|
+
if line == first
|
445
|
+
res = " " * ( size + 2 )
|
446
|
+
else
|
447
|
+
res = sprintf( "%#{size}d: ", line )
|
448
|
+
end
|
449
|
+
|
450
|
+
line += 1
|
451
|
+
res
|
452
|
+
end
|
453
|
+
end
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|