# = A better RDoc HTML template # # Code rewritten by: # Erik Hollensbe # # RubyGems integration properly done by: # James Tucker (aka raggi) # # Original Authors: # Mislav Marohnić # Tony Strauss (http://github.com/DesigningPatterns) # Michael Granger , who had maintained the original RDoc template require 'pathname' require 'haml' require 'sass' require 'rdoc/rdoc' require 'rdoc/generator' class RDoc::Generator::Hanna STYLE = 'styles.sass' LAYOUT = 'layout.haml' INDEX_PAGE = 'index.haml' CLASS_PAGE = 'page.haml' METHOD_LIST_PAGE = 'method_list.haml' FILE_PAGE = CLASS_PAGE SECTIONS_PAGE = 'sections.haml' FILE_INDEX = 'file_index.haml' CLASS_INDEX = 'class_index.haml' METHOD_INDEX = 'method_index.haml' CLASS_DIR = 'classes' FILE_DIR = 'files' INDEX_OUT = 'index.html' FILE_INDEX_OUT = 'fr_file_index.html' CLASS_INDEX_OUT = 'fr_class_index.html' METHOD_INDEX_OUT = 'fr_method_index.html' STYLE_OUT = File.join('css', 'style.css') DESCRIPTION = 'a HAML-based HTML generator that scales' # EPIC CUT AND PASTE TIEM NAO -- GG RDoc::RDoc.add_generator( self ) def self::for( options ) new( options ) end def initialize( options ) @options = options @templatedir = Pathname.new File.expand_path('../hanna-nouveau/template_files', __FILE__) @files = nil @classes = nil @methods = nil @attributes = nil @basedir = Pathname.pwd.expand_path end 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(&:method_list).flatten.sort @attributes = @classes.map(&:attributes).flatten.sort # Now actually write the output write_static_files generate_indexes generate_class_files generate_file_files rescue StandardError => err p [ err.class.name, err.message, err.backtrace.join("\n ") ] raise end def write_static_files css_dir = outjoin('css') unless File.directory?(css_dir) FileUtils.mkdir css_dir end File.open(File.join(css_dir, 'style.css'), 'w') { |f| f << Sass::Engine.new(File.read(templjoin(STYLE))).to_css } end # FIXME refactor def generate_indexes @main_page_uri = @files.find { |f| f.name == @options.main_page }.path rescue '' File.open(outjoin(INDEX_OUT), 'w') { |f| f << haml_file(templjoin(INDEX_PAGE)).to_html(binding) } generate_index(FILE_INDEX_OUT, FILE_INDEX, 'File', { :files => @files}) generate_index(CLASS_INDEX_OUT, CLASS_INDEX, 'Class', { :classes => @classes }) generate_index(METHOD_INDEX_OUT, METHOD_INDEX, 'Method', { :methods => @methods, :attributes => @attributes }) end def generate_index(outfile, templfile, index_name, values) values.merge!({ :stylesheet => STYLE_OUT, :list_title => "#{index_name} Index" }) index = haml_file(templjoin(templfile)) File.open(outjoin(outfile), 'w') do |f| f << with_layout(values) do index.to_html(binding, values) end end end def generate_file_files file_page = haml_file(templjoin(FILE_PAGE)) method_list_page = haml_file(templjoin(METHOD_LIST_PAGE)) # FIXME non-Ruby files @files.each do |file| path = Pathname.new(file.path) stylesheet = Pathname.new(STYLE_OUT).relative_path_from(path.dirname) values = { :file => file, :entry => file, :stylesheet => stylesheet, :classmod => nil, :title => file.base_name, :list_title => nil, :description => file.description } result = with_layout(values) do file_page.to_html(binding, :values => values) do method_list_page.to_html(binding, values) end end # FIXME XXX sanity check dir = path.dirname unless File.directory? dir FileUtils.mkdir_p dir end File.open(outjoin(file.path), 'w') { |f| f << result } end end def generate_class_files class_page = haml_file(templjoin(CLASS_PAGE)) method_list_page = haml_file(templjoin(METHOD_LIST_PAGE)) sections_page = haml_file(templjoin(SECTIONS_PAGE)) # FIXME refactor @classes.each do |klass| outfile = classfile(klass) stylesheet = Pathname.new(STYLE_OUT).relative_path_from(outfile.dirname) values = { :file => klass.path, :entry => klass, :stylesheet => stylesheet, :classmod => klass.type, :title => klass.full_name, :list_title => nil, :description => klass.description, :section => { # FIXME linkify :classlist => '
    ' + klass.classes_and_modules.inject('') { |x,y| x << '
  1. ' + y.name + '
  2. ' } + '
', :constants => klass.constants, :aliases => klass.method_list.select { |x| x.is_alias_for }.sort, :attributes => klass.attributes.sort, :method_list => klass.method_list.select { |x| !x.is_alias_for }.sort } } result = with_layout(values) do class_page.to_html(binding, :values => values) do method_list_page.to_html(binding, :values => values) + sections_page.to_html(binding, :values => values) end end # FIXME XXX sanity check dir = outfile.dirname unless File.directory? dir FileUtils.mkdir_p dir end File.open(outfile, 'w') { |f| f << result } end end def with_layout(values) layout = haml_file(templjoin(LAYOUT)) layout.to_html(binding, :values => values) { yield } end def sanitize_code_blocks(text) text.gsub(/
(.+?)<\/pre>/m) do
      code = $1.sub(/^\s*\n/, '')
      indent = code.gsub(/\n[ \t]*\n/, "\n").scan(/^ */).map{ |i| i.size }.min
      code.gsub!(/^#{' ' * indent}/, '') if indent > 0

        "
#{code}
" end end # probably should bring in nokogiri/libxml2 to do this right.. not sure if # it's worth it. def frame_link(content) content.gsub(%r!' out << link_to(text, classfile(klass)) end subentries = @classes.select { |x| x.full_name[/^#{klass.full_name}::/] } subentries.each { |x| namespaces[x.full_name] = true } out << "\n
    " + render_class_tree(subentries, klass) + "\n
" if klass.document_self out << '' end end out end end def build_javascript_search_index(entries) result = "var search_index = [\n" entries.each do |entry| method_name = entry.name module_name = entry.parent_name # FIXME link html = link_to_method(entry, [classfile(entry.parent), (entry.aref rescue "method-#{entry.html_name}")].join('#')) result << " { method: '#{method_name.downcase}', " + "module: '#{module_name.downcase}', " + "html: '#{html}' },\n" end result << "]" result end def link_to(text, url = nil, classname = nil) class_attr = classname ? ' class="%s"' % classname : '' if url %[
#{text}] elsif classname %[#{text}] else text end end # +method_text+ is in the form of "ago (ActiveSupport::TimeWithZone)". def link_to_method(entry, url = nil, classname = nil) method_name = entry.pretty_name rescue entry.name module_name = entry.parent_name rescue entry.name link_to %Q(#{h method_name} (#{h module_name})), url, classname end def classfile(klass) # FIXME sloooooooow Pathname.new(File.join(CLASS_DIR, klass.full_name.split('::')) + '.html') end def outjoin(name) File.join(@outputdir, name) end def templjoin(name) File.join(@templatedir, name) end def haml_file(file) Haml::Engine.new(File.read(file), :format => :html4) end end