summaryrefslogtreecommitdiff
path: root/lib/rdoc/generator/darkfish.rb
diff options
context:
space:
mode:
authordrbrain <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2010-12-20 03:22:49 +0000
committerdrbrain <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2010-12-20 03:22:49 +0000
commit2ef9c50c6e405717d06362787c4549ca4f1c6485 (patch)
treeee99486567461dd5796f3d6edcc9e204187f2666 /lib/rdoc/generator/darkfish.rb
parentd7effd506f5b91a636f2e6452ef1946b923007c7 (diff)
Import RDoc 3
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@30249 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib/rdoc/generator/darkfish.rb')
-rw-r--r--lib/rdoc/generator/darkfish.rb658
1 files changed, 284 insertions, 374 deletions
diff --git a/lib/rdoc/generator/darkfish.rb b/lib/rdoc/generator/darkfish.rb
index f64641873e..e5a6e57424 100644
--- a/lib/rdoc/generator/darkfish.rb
+++ b/lib/rdoc/generator/darkfish.rb
@@ -1,15 +1,12 @@
# -*- mode: ruby; ruby-indent-level: 2; tab-width: 2 -*-
-# vim: noet ts=2 sts=8 sw=2
require 'pathname'
require 'fileutils'
-require 'erb'
+require 'rdoc/erbio'
require 'rdoc/generator/markup'
-$DARKFISH_DRYRUN = false # TODO make me non-global
-
-#
+##
# Darkfish RDoc HTML Generator
#
# $Id: darkfish.rb 52 2009-01-07 02:08:11Z deveiant $
@@ -52,401 +49,314 @@ $DARKFISH_DRYRUN = false # TODO make me non-global
#
class RDoc::Generator::Darkfish
- RDoc::RDoc.add_generator( self )
+ RDoc::RDoc.add_generator self
+
+ include ERB::Util
+
+ # Path to this file's parent directory. Used to find templates and other
+ # resources.
+
+ GENERATOR_DIR = File.join 'rdoc', 'generator'
+
+ ##
+ # Release Version
+
+ VERSION = '2'
+
+ ##
+ # Initialize a few instance variables before we start
- include ERB::Util
+ def initialize options
+ @options = options
- # Subversion rev
- SVNRev = %$Rev: 52 $
+ @template_dir = Pathname.new options.template_dir
+ @template_cache = {}
- # Subversion ID
- SVNId = %$Id: darkfish.rb 52 2009-01-07 02:08:11Z deveiant $
+ @files = nil
+ @classes = nil
- # Path to this file's parent directory. Used to find templates and other
- # resources.
- GENERATOR_DIR = File.join 'rdoc', 'generator'
+ @basedir = Pathname.pwd.expand_path
+ end
- # Release Version
- VERSION = '1.1.6'
+ ##
+ # The output directory
- # Directory where generated classes live relative to the root
- CLASS_DIR = nil
+ attr_reader :outputdir
- # Directory where generated files live relative to the root
- FILE_DIR = nil
+ ##
+ # Output progress information if debugging is enabled
+ def debug_msg *msg
+ return unless $DEBUG_RDOC
+ $stderr.puts(*msg)
+ end
- #################################################################
- ### C L A S S M E T H O D S
- #################################################################
+ ##
+ # Directory where generated class HTML files live relative to the output
+ # dir.
- ### Standard generator factory method
- def self::for( options )
- new( options )
- end
+ def class_dir
+ nil
+ end
+ ##
+ # Directory where generated class HTML files live relative to the output
+ # dir.
- #################################################################
- ### I N S T A N C E M E T H O D S
- #################################################################
+ def file_dir
+ nil
+ end
- ### Initialize a few instance variables before we start
- def initialize( options )
- @options = options
+ ##
+ # Create the directories the generated docs will live in if they don't
+ # already exist.
- template = @options.template || 'darkfish'
+ def gen_sub_directories
+ @outputdir.mkpath
+ end
- template_dir = $LOAD_PATH.map do |path|
- File.join File.expand_path(path), GENERATOR_DIR, 'template', template
- end.find do |dir|
- File.directory? dir
- end
+ ##
+ # Copy over the stylesheet into the appropriate place in the output
+ # directory.
- raise RDoc::Error, "could not find template #{template.inspect}" unless
- template_dir
+ def write_style_sheet
+ debug_msg "Copying static files"
+ options = { :verbose => $DEBUG_RDOC, :noop => @options.dry_run }
- @template_dir = Pathname.new File.expand_path(template_dir)
+ FileUtils.cp @template_dir + 'rdoc.css', '.', options
- @files = nil
- @classes = nil
+ Dir[(@template_dir + "{js,images}/**/*").to_s].each do |path|
+ next if File.directory? path
+ next if File.basename(path) =~ /^\./
- @basedir = Pathname.pwd.expand_path
- end
+ dst = Pathname.new(path).relative_path_from @template_dir
- ######
- public
- ######
+ # I suck at glob
+ dst_dir = dst.dirname
+ FileUtils.mkdir_p dst_dir, options unless File.exist? dst_dir
- # The output directory
- attr_reader :outputdir
+ FileUtils.cp @template_dir + path, dst, options
+ end
+ end
+ ##
+ # Build the initial indices and output objects based on an array of TopLevel
+ # objects containing the extracted information.
- ### Output progress information if debugging is enabled
- def debug_msg( *msg )
- return unless $DEBUG_RDOC
- $stderr.puts( *msg )
- end
+ def generate top_levels
+ @outputdir = Pathname.new(@options.op_dir).expand_path(@basedir)
- def class_dir
- CLASS_DIR
- end
+ @files = top_levels.sort
+ @classes = RDoc::TopLevel.all_classes_and_modules.sort
+ @methods = @classes.map { |m| m.method_list }.flatten.sort
+ @modsort = get_sorted_module_list(@classes)
- def file_dir
- FILE_DIR
- end
+ # Now actually write the output
+ write_style_sheet
+ generate_index
+ generate_class_files
+ generate_file_files
- ### Create the directories the generated docs will live in if
- ### they don't already exist.
- def gen_sub_directories
- @outputdir.mkpath
- end
+ rescue StandardError => err
+ debug_msg "%s: %s\n %s" % [
+ err.class.name, err.message, err.backtrace.join("\n ")
+ ]
+
+ raise
+ end
+
+ protected
+
+ ##
+ # Return a list of the documented modules sorted by salience first, then
+ # by name.
+
+ def get_sorted_module_list(classes)
+ nscounts = classes.inject({}) do |counthash, klass|
+ top_level = klass.full_name.gsub(/::.*/, '')
+ counthash[top_level] ||= 0
+ counthash[top_level] += 1
+
+ counthash
+ end
+
+ # Sort based on how often the top level namespace occurs, and then on the
+ # name of the module -- this works for projects that put their stuff into
+ # a namespace, of course, but doesn't hurt if they don't.
+ classes.sort_by do |klass|
+ top_level = klass.full_name.gsub( /::.*/, '' )
+ [nscounts[top_level] * -1, klass.full_name]
+ end.select do |klass|
+ klass.document_self
+ end
+ end
+
+ ##
+ # Generate an index page which lists all the classes which are documented.
+
+ def generate_index
+ template_file = @template_dir + 'index.rhtml'
+ return unless template_file.exist?
+
+ debug_msg "Rendering the index page..."
+
+ out_file = @basedir + @options.op_dir + 'index.html'
+
+ render_template template_file, out_file do |io| binding end
+ end
+
+ ##
+ # Generate a documentation file for each class
+
+ def generate_class_files
+ template_file = @template_dir + 'classpage.rhtml'
+ return unless template_file.exist?
+ debug_msg "Generating class documentation in #@outputdir"
+
+ @classes.each do |klass|
+ debug_msg " working on %s (%s)" % [klass.full_name, klass.path]
+ out_file = @outputdir + klass.path
+ # suppress 1.9.3 warning
+ rel_prefix = rel_prefix = @outputdir.relative_path_from(out_file.dirname)
+ svninfo = svninfo = self.get_svninfo(klass)
+
+ debug_msg " rendering #{out_file}"
+ render_template template_file, out_file do |io| binding end
+ end
+ end
+
+ ##
+ # Generate a documentation file for each file
+
+ def generate_file_files
+ template_file = @template_dir + 'filepage.rhtml'
+ return unless template_file.exist?
+ debug_msg "Generating file documentation in #@outputdir"
+
+ @files.each do |file|
+ out_file = @outputdir + file.path
+ debug_msg " working on %s (%s)" % [ file.full_name, out_file ]
+ # suppress 1.9.3 warning
+ rel_prefix = rel_prefix = @outputdir.relative_path_from(out_file.dirname)
+
+ debug_msg " rendering #{out_file}"
+ render_template template_file, out_file do |io| binding end
+ end
+ end
+
+ ##
+ # Return a string describing the amount of time in the given number of
+ # seconds in terms a human can understand easily.
+
+ def time_delta_string seconds
+ return 'less than a minute' if seconds < 60
+ return "#{seconds / 60} minute#{seconds / 60 == 1 ? '' : 's'}" if
+ seconds < 3000 # 50 minutes
+ return 'about one hour' if seconds < 5400 # 90 minutes
+ return "#{seconds / 3600} hours" if seconds < 64800 # 18 hours
+ return 'one day' if seconds < 86400 # 1 day
+ return 'about one day' if seconds < 172800 # 2 days
+ return "#{seconds / 86400} days" if seconds < 604800 # 1 week
+ return 'about one week' if seconds < 1209600 # 2 week
+ return "#{seconds / 604800} weeks" if seconds < 7257600 # 3 months
+ return "#{seconds / 2419200} months" if seconds < 31536000 # 1 year
+ return "#{seconds / 31536000} years"
+ end
+
+ # %q$Id: darkfish.rb 52 2009-01-07 02:08:11Z deveiant $"
+ SVNID_PATTERN = /
+ \$Id:\s
+ (\S+)\s # filename
+ (\d+)\s # rev
+ (\d{4}-\d{2}-\d{2})\s # Date (YYYY-MM-DD)
+ (\d{2}:\d{2}:\d{2}Z)\s # Time (HH:MM:SSZ)
+ (\w+)\s # committer
+ \$$
+ /x
+
+ ##
+ # Try to extract Subversion information out of the first constant whose
+ # value looks like a subversion Id tag. If no matching constant is found,
+ # and empty hash is returned.
+
+ def get_svninfo klass
+ constants = klass.constants or return {}
+
+ constants.find { |c| c.value =~ SVNID_PATTERN } or return {}
+
+ filename, rev, date, time, committer = $~.captures
+ commitdate = Time.parse "#{date} #{time}"
+
+ return {
+ :filename => filename,
+ :rev => Integer(rev),
+ :commitdate => commitdate,
+ :commitdelta => time_delta_string(Time.now - commitdate),
+ :committer => committer,
+ }
+ end
+
+ ##
+ # Load and render the erb template in the given +template_file+ and write
+ # it out to +out_file+.
+ #
+ # Both +template_file+ and +out_file+ should be Pathname-like objects.
+ #
+ # An io will be yielded which must be captured by binding in the caller.
+
+ def render_template template_file, out_file # :yield: io
+ template = template_for template_file
+
+ unless @options.dry_run then
+ debug_msg "Outputting to %s" % [out_file.expand_path]
+
+ out_file.dirname.mkpath
+ out_file.open 'w', 0644 do |io|
+ io.set_encoding @options.encoding if Object.const_defined? :Encoding
+
+ context = yield io
+
+ template_result template, context, template_file
+ end
+ else
+ context = yield nil
+
+ output = template_result template, context, template_file
+
+ debug_msg " would have written %d characters to %s" % [
+ output.length, out_file.expand_path
+ ]
+ end
+ end
+
+ ##
+ # Creates the result for +template+ with +context+. If an error is raised a
+ # Pathname +template_file+ will indicate the file where the error occurred.
+
+ def template_result template, context, template_file
+ template.filename = template_file.to_s
+ template.result context
+ rescue NoMethodError => e
+ raise RDoc::Error, "Error while evaluating %s: %s" % [
+ template_file.expand_path,
+ e.message,
+ ], e.backtrace
+ end
+
+ ##
+ # Retrieves a cache template for +file+, if present, or fills the cache.
+
+ def template_for file
+ template = @template_cache[file]
+
+ return template if template
+
+ klass = @options.dry_run ? ERB : RDoc::ERBIO
+
+ template = klass.new file.read, nil, '<>'
+ @template_cache[file] = template
+ template
+ end
- ### Copy over the stylesheet into the appropriate place in the output
- ### directory.
- def write_style_sheet
- debug_msg "Copying static files"
- options = { :verbose => $DEBUG_RDOC, :noop => $DARKFISH_DRYRUN }
-
- FileUtils.cp @template_dir + 'rdoc.css', '.', options
-
- Dir[(@template_dir + "{js,images}/**/*").to_s].each do |path|
- next if File.directory? path
- next if path =~ /#{File::SEPARATOR}\./
-
- dst = Pathname.new(path).relative_path_from @template_dir
-
- # I suck at glob
- dst_dir = dst.dirname
- FileUtils.mkdir_p dst_dir, options unless File.exist? dst_dir
-
- FileUtils.cp @template_dir + path, dst, options
- end
- end
-
- ### Build the initial indices and output objects
- ### based on an array of TopLevel objects containing
- ### the extracted information.
- def generate( top_levels )
- @outputdir = Pathname.new( @options.op_dir ).expand_path( @basedir )
-
- @files = top_levels.sort
- @classes = RDoc::TopLevel.all_classes_and_modules.sort
- @methods = @classes.map { |m| m.method_list }.flatten.sort
- @modsort = get_sorted_module_list( @classes )
-
- # Now actually write the output
- write_style_sheet
- generate_index
- generate_class_files
- generate_file_files
-
- rescue StandardError => err
- debug_msg "%s: %s\n %s" % [ err.class.name, err.message, err.backtrace.join("\n ") ]
- raise
- end
-
- #########
- protected
- #########
-
- ### Return a list of the documented modules sorted by salience first, then
- ### by name.
- def get_sorted_module_list( classes )
- nscounts = classes.inject({}) do |counthash, klass|
- top_level = klass.full_name.gsub( /::.*/, '' )
- counthash[top_level] ||= 0
- counthash[top_level] += 1
-
- counthash
- end
-
- # Sort based on how often the top level namespace occurs, and then on the
- # name of the module -- this works for projects that put their stuff into
- # a namespace, of course, but doesn't hurt if they don't.
- classes.sort_by do |klass|
- top_level = klass.full_name.gsub( /::.*/, '' )
- [
- nscounts[ top_level ] * -1,
- klass.full_name
- ]
- end.select do |klass|
- klass.document_self
- end
- end
-
- ### Generate an index page which lists all the classes which
- ### are documented.
- def generate_index
- template_file = @template_dir + 'index.rhtml'
- return unless template_file.exist?
-
- debug_msg "Rendering the index page..."
-
- template_src = template_file.read
- template = ERB.new( template_src, nil, '<>' )
- template.filename = template_file.to_s
- context = binding()
-
- output = nil
-
- begin
- output = template.result( context )
- rescue NoMethodError => err
- raise RDoc::Error, "Error while evaluating %s: %s (at %p)" % [
- template_file,
- err.message,
- eval( "_erbout[-50,50]", context )
- ], err.backtrace
- end
-
- outfile = @basedir + @options.op_dir + 'index.html'
- unless $DARKFISH_DRYRUN
- debug_msg "Outputting to %s" % [outfile.expand_path]
- outfile.open( 'w', 0644 ) do |fh|
- fh.print( output )
- end
- else
- debug_msg "Would have output to %s" % [outfile.expand_path]
- end
- end
-
- ### Generate a documentation file for each class
- def generate_class_files
- template_file = @template_dir + 'classpage.rhtml'
- return unless template_file.exist?
- debug_msg "Generating class documentation in #@outputdir"
-
- @classes.each do |klass|
- debug_msg " working on %s (%s)" % [ klass.full_name, klass.path ]
- outfile = @outputdir + klass.path
- rel_prefix = @outputdir.relative_path_from( outfile.dirname )
- svninfo = self.get_svninfo( klass )
-
- debug_msg " rendering #{outfile}"
- self.render_template( template_file, binding(), outfile )
- end
- end
-
- ### Generate a documentation file for each file
- def generate_file_files
- template_file = @template_dir + 'filepage.rhtml'
- return unless template_file.exist?
- debug_msg "Generating file documentation in #@outputdir"
-
- @files.each do |file|
- outfile = @outputdir + file.path
- debug_msg " working on %s (%s)" % [ file.full_name, outfile ]
- rel_prefix = @outputdir.relative_path_from( outfile.dirname )
-
- debug_msg " rendering #{outfile}"
- self.render_template( template_file, binding(), outfile )
- end
- end
-
-
- ### Return a string describing the amount of time in the given number of
- ### seconds in terms a human can understand easily.
- def time_delta_string( seconds )
- return 'less than a minute' if seconds < 1.minute
- return (seconds / 1.minute).to_s + ' minute' + (seconds/60 == 1 ? '' : 's') if seconds < 50.minutes
- return 'about one hour' if seconds < 90.minutes
- return (seconds / 1.hour).to_s + ' hours' if seconds < 18.hours
- return 'one day' if seconds < 1.day
- return 'about one day' if seconds < 2.days
- return (seconds / 1.day).to_s + ' days' if seconds < 1.week
- return 'about one week' if seconds < 2.week
- return (seconds / 1.week).to_s + ' weeks' if seconds < 3.months
- return (seconds / 1.month).to_s + ' months' if seconds < 1.year
- return (seconds / 1.year).to_s + ' years'
- end
-
-
- # %q$Id: darkfish.rb 52 2009-01-07 02:08:11Z deveiant $"
- SVNID_PATTERN = /
- \$Id:\s
- (\S+)\s # filename
- (\d+)\s # rev
- (\d{4}-\d{2}-\d{2})\s # Date (YYYY-MM-DD)
- (\d{2}:\d{2}:\d{2}Z)\s # Time (HH:MM:SSZ)
- (\w+)\s # committer
- \$$
- /x
-
- ### Try to extract Subversion information out of the first constant whose value looks like
- ### a subversion Id tag. If no matching constant is found, and empty hash is returned.
- def get_svninfo( klass )
- constants = klass.constants or return {}
-
- constants.find {|c| c.value =~ SVNID_PATTERN } or return {}
-
- filename, rev, date, time, committer = $~.captures
- commitdate = Time.parse( date + ' ' + time )
-
- return {
- :filename => filename,
- :rev => Integer( rev ),
- :commitdate => commitdate,
- :commitdelta => time_delta_string( Time.now.to_i - commitdate.to_i ),
- :committer => committer,
- }
- end
-
-
- ### Load and render the erb template in the given +template_file+ within the
- ### specified +context+ (a Binding object) and write it out to +outfile+.
- ### Both +template_file+ and +outfile+ should be Pathname-like objects.
-
- def render_template( template_file, context, outfile )
- template_src = template_file.read
- template = ERB.new( template_src, nil, '<>' )
- template.filename = template_file.to_s
-
- output = begin
- template.result( context )
- rescue NoMethodError => err
- raise RDoc::Error, "Error while evaluating %s: %s (at %p)" % [
- template_file.to_s,
- err.message,
- eval( "_erbout[-50,50]", context )
- ], err.backtrace
- end
-
- unless $DARKFISH_DRYRUN
- outfile.dirname.mkpath
- outfile.open( 'w', 0644 ) do |ofh|
- ofh.print( output )
- end
- else
- debug_msg " would have written %d bytes to %s" %
- [ output.length, outfile ]
- end
- end
-
-end # Roc::Generator::Darkfish
-
-# :stopdoc:
-
-### Time constants
-module TimeConstantMethods # :nodoc:
-
- ### Number of seconds (returns receiver unmodified)
- def seconds
- return self
- end
- alias_method :second, :seconds
-
- ### Returns number of seconds in <receiver> minutes
- def minutes
- return self * 60
- end
- alias_method :minute, :minutes
-
- ### Returns the number of seconds in <receiver> hours
- def hours
- return self * 60.minutes
- end
- alias_method :hour, :hours
-
- ### Returns the number of seconds in <receiver> days
- def days
- return self * 24.hours
- end
- alias_method :day, :days
-
- ### Return the number of seconds in <receiver> weeks
- def weeks
- return self * 7.days
- end
- alias_method :week, :weeks
-
- ### Returns the number of seconds in <receiver> fortnights
- def fortnights
- return self * 2.weeks
- end
- alias_method :fortnight, :fortnights
-
- ### Returns the number of seconds in <receiver> months (approximate)
- def months
- return self * 30.days
- end
- alias_method :month, :months
-
- ### Returns the number of seconds in <receiver> years (approximate)
- def years
- return (self * 365.25.days).to_i
- end
- alias_method :year, :years
-
-
- ### Returns the Time <receiver> number of seconds before the
- ### specified +time+. E.g., 2.hours.before( header.expiration )
- def before( time )
- return time - self
- end
-
-
- ### Returns the Time <receiver> number of seconds ago. (e.g.,
- ### expiration > 2.hours.ago )
- def ago
- return self.before( ::Time.now )
- end
-
-
- ### Returns the Time <receiver> number of seconds after the given +time+.
- ### E.g., 10.minutes.after( header.expiration )
- def after( time )
- return time + self
- end
-
- # Reads best without arguments: 10.minutes.from_now
- def from_now
- return self.after( ::Time.now )
- end
-end # module TimeConstantMethods
-
-
-# Extend Numeric with time constants
-class Numeric # :nodoc:
- include TimeConstantMethods
end