class RDoc::Generator::Emerald

This is the main generator class that is instanciated by RDoc when you order it to use the emerald generator. It mainly works on ERB template files you can find in the data/templates directory, where each major component (read: classes and toplevel files) has a template file that is evaluated for it. The result is then injected into the layout template, data/templates/layout.html.erb, which is then evaluated as well.

About relative paths

As the output generated by RDoc is supposed to be viewable by both visiting the doc/ directory with a browser and providing the doc/ directory to an HTTP server, effectively making it the root directory, care has been taken to only use relative links in all the static HTML files. The key component for this to work is the root_path method, which is called to set the relative path to the root directory (i.e. the output directory). When called without an argument, root_path returns the value previously remembered (usually it contains a good number of ../ entries). This way, the root directory can be set whenever a new HTML file is going to be outputted and can then be referenced from the ERB template.

Darkfish compatibility

RDoc’s HTML formatter has a good number of helper methods that have a strong hint regarding “where what belongs”. By using these helper methods itself when creating cross-references, the HTML formatter enforces both the directory structure of the output directory and the anchor names used for references inside a single HTML file. The only way to circumvent this is to write another formatter, which I don’t intend to as the standard HTML formatter does a good job for HTML stuff. A nice side effect is that Emerald’s documentation is compatible with Darkfish’s one when it comes to links to specific elements. For example, you can create a link to a method called Foo::Bar#baz somewhere on the web, and if the destinatinon website chooses to switch from Darkfish output to Emerald (which I hope!), the link will continue to work.

Constants

DATA_DIR

Where to find the non-code stuff.

DESCRIPTION

Description displayed in RDoc’s help.

DummyStartPage

Minimal RDoc::Toplevel-alike object (i.e. it responds to the two methods required for rendering it, full_name and description) that is used to create the index page if none was set. I really recommend to manually set a useful index page!

LAYOUT_TEMPLATE

Main template used as the general layout.

ROOT_DIR

Root directory of this project.

TEMPLATES

Subtemplates injected into the main template.

VERSION

The version number.

Public Class Methods

new(store, options) click to toggle source

Instanciates this generator. Automatically called by RDoc.

Parameter

options

RDoc passed the current RDoc::Options instance.

# File lib/rdoc/generator/emerald.rb, line 128
def initialize(store, options)
  @store   = store
  @options = options
  @op_dir  = Pathname.pwd.expand_path + @options.op_dir
end
setup_options(options) click to toggle source

Add additional options to RDoc (see the RDoc::Generator::Emerald::Options module).

# File lib/rdoc/generator/emerald.rb, line 119
def self.setup_options(options)
  options.extend(RDoc::Generator::Emerald::Options)
end

Public Instance Methods

class_dir() click to toggle source

Darkfish returns nil, hence we do this as well.

# File lib/rdoc/generator/emerald.rb, line 169
def class_dir
  nil
end
debug(str) click to toggle source

Outputs a string on standard output, but only if RDoc was invoked with the --debug switch.

# File lib/rdoc/generator/emerald.rb, line 136
def debug(str)
  puts(str) if $DEBUG_RDOC
end
file_dir() click to toggle source

Darkfish returns nil, hence we do this as well.

# File lib/rdoc/generator/emerald.rb, line 164
def file_dir
  nil
end
generate() click to toggle source

Main hook method called by RDoc, triggers the generation process.

# File lib/rdoc/generator/emerald.rb, line 141
def generate
  debug "Sorting classes, modules, and methods..."
  @toplevels = @store.all_files
  @classes_and_modules = @store.all_classes_and_modules.sort_by{|klass| klass.full_name}
  @methods = @classes_and_modules.map{|mod| mod.method_list}.flatten.sort

  # Create the output directory
  mkdir @op_dir unless @op_dir.exist?

  copy_base_files
  evaluate_toplevels
  evaluate_classes_and_modules

  unless @options.main_page # If set, #evaluate_toplevels creates the index.html for us
    toplevel = DummyStartPage.new
    root_path "./" # This *is* in the toplevel
    File.open(@op_dir + "index.html", "w") do |file|
      file.write(render(:toplevel, binding))
    end
  end
end

Protected Instance Methods

rdocize_classmod(classmod) click to toggle source

Takes a RDoc::ClassModule and transforms it into a complete pathname relative to the output directory. Filename alterations done by RDoc’s crossref-HTML formatter are honoured. Note you have to prepend root_path to get a complete href.

# File lib/rdoc/generator/emerald.rb, line 222
def rdocize_classmod(classmod)
  Pathname.new("#{classmod.full_name.split("::").join("/")}.html")
end
rdocize_toplevel(toplevel) click to toggle source

Takes a RDoc::TopLevel and transforms it into a complete pathname relative to the output directory. Filename alterations done by RDoc’s crossref-HTML formatter are honoured. Note you have to prepend root_path to get a complete href.

# File lib/rdoc/generator/emerald.rb, line 214
def rdocize_toplevel(toplevel)
  Pathname.new("#{toplevel.relative_name.gsub(".", "_")}.html")
end
root_path(set_to = nil) click to toggle source

Set/get the root directory.

Parameter

set_to (nil)

If passed, this method sets the root directory rather than returning it.

Return value

The current relative path to the root directory.

Remarks

See the class’ introductory text for more information on this.

# File lib/rdoc/generator/emerald.rb, line 185
def root_path(set_to = nil)
  if set_to
    @root_path = Pathname.new(set_to)
  else
    @root_path ||= Pathname.new("./")
  end
end
title(set_to = nil) click to toggle source

Set/get the page title.

Parameter

set_to (nil)

If passed, this method sets the title rather than returning it.

Return value

The current page title.

Remarks

Works the same way as root_path.

# File lib/rdoc/generator/emerald.rb, line 202
def title(set_to = nil)
  if set_to
    @title = set_to
  else
    @title ||= ""
  end
end

Private Instance Methods

copy_base_files() click to toggle source
# File lib/rdoc/generator/emerald.rb, line 228
def copy_base_files
  debug "Copying base base files..."
  mkdir @op_dir + "stylesheets" unless File.directory?(@op_dir + "stylesheets")

  cp   Dir[DATA_DIR + "stylesheets" + "*.css"], @op_dir + "stylesheets"
  cp_r DATA_DIR + "javascripts", @op_dir
  cp_r DATA_DIR + "images",      @op_dir
end
evaluate_classes_and_modules() click to toggle source
# File lib/rdoc/generator/emerald.rb, line 268
def evaluate_classes_and_modules
  @classes_and_modules.each do |classmod|
    debug "Processing class/module #{classmod.full_name} (#{classmod.method_list.count} methods)..."

    path = @op_dir + rdocize_classmod(classmod)

    mkdir_p   path.parent unless path.parent.directory?
    title     classmod.full_name
    root_path "../" * (classmod.full_name.split("::").count - 1) # Last element is a file


    File.open(path, "w") do |file|
      debug "  => #{path}"
      file.write(render(:classmodule, binding))
    end
  end
end
evaluate_toplevels() click to toggle source
# File lib/rdoc/generator/emerald.rb, line 237
def evaluate_toplevels
  @toplevels.each do |toplevel|
    debug "Processing toplevel #{toplevel.name}..."

    root_path("../" * (toplevel.relative_name.split("/").count - 1)) # Last component is a filename
    title toplevel.relative_name

    # Create the path to the file if necessary
    path = @op_dir + rdocize_toplevel(toplevel)
    mkdir_p path.parent unless path.parent.exist?

    # Evaluate the actual file documentation
    File.open(path, "w") do |file|
      debug  "  => #{path}"
      file.write(render(:toplevel, binding))
    end

    # If this toplevel is supposed to be the main file,
    # copy it’s content to the index.html file.
    if toplevel.relative_name == @options.main_page
      debug "  => This is the main page. Writing index.html."

      root_path "./" # We *are* at the top here

      File.open(@op_dir + "index.html", "w") do |file|
        file.write(render(:toplevel, binding))
      end
    end
  end
end
render(template_name, context) click to toggle source

Renders the subtemplate template_name in the context of the given binding, then injects it into the main template (which is evaluated in the same context).

Returns the resulting string.

# File lib/rdoc/generator/emerald.rb, line 291
def render(template_name, context)
  render_into_layout{TEMPLATES[template_name].result(context)}
end
render_into_layout() click to toggle source

Renders into the main layout. The return value of the block passed to this method will be placed in the layout in place of the yield expression.

# File lib/rdoc/generator/emerald.rb, line 298
def render_into_layout
  LAYOUT_TEMPLATE.result(binding)
end