Customization

All of the following Guides require modifications of the default-templates. To start of with your own templates simply type:

docjs scaffold TEMPLATE_PATH

in your console and replace TEMPLATE_PATH with the path you wish the templates to be generated in. This command will copy the default templates to TEMPLATE_PATH, which results in a structure similiar to:

my_templates/
  generators/
  helpers/
  resources/
  tokens/
  types/
  views/
  application.rb

If you take a look at application.rb you will see, that all it does is to require the files, which are placed within the other template-folders.

generators/

Contains all custom generators, which are needed to create html or json-files from the internal data- structure. In section Modifying a Generator you can learn a bit more about the use of a generator and how to customize it.

Contents:

helpers/

Helpers are, like helpers in Rails, included into your generator and can be used there and in the views aswell. (See Section Helpers in Generator::Generator)

Contents:

resources/

Contains all static template resources like css, js and img files

Contents:

tokens/

Only consists of one file tokens.rb, which contains all the registered Tokens. You may find some more information about the tokens in the Usage-Guide.

Contents:

types/

Types are special tokens. They categorize the Comments respectively CodeObject's by subclassing them. Per default these are

Contents:

See Usage-Guide

views/

Contains the html.erb template-files and partials, which will be rendered by the Generators

Contents:

1. Example - Adding a simple token

You can extend Doc.js on different levels. The easiest way of customization is to add a simple token. Let's say we want to add a new innovative token @requires to specify dependencies to other objects.

/**
 * @function say_hello
 * @requires console.log
 */

If we start Doc.js without making any changes to the templates this documentation will result in an error like ERROR No Tokenhandler for: @requires. So let's register our tokenhandler to tokens/tokens.rb:

register :requires

Changing the area

The token will now be processed using the :text_only tokenhandler and will appear in the :body section of the template. Because requires may fit much better in the :sidebar section (it contains meta-information about the object) we place it there with

register :requires, :area => :sidebar

Now our token should be displayed nicely in the sidebar-area on the right side (if you are using the default template).

You can find more information about areas in the Helper-doc

Using another tokenhandler

While using our new token, we may notice, that it would be nice if we can specify some detailed information about the dependency like:

/**
 * @function say_hello
 * @requires console.log for printing the message to the console
 */

So we have to change the tokenhandler to :named because our tokenline now consists of a name and the description-content.

register :requires, :area => :sidebar, :handler => :named

The template, which renders the token now can access the name and the content of the token seperatly and displays them somehow like:

<h4>console.log</h4>
<p>for printing the message to the console</p>

Creating a custom template

We've come pretty far with just adding one line to our own templates. But now we want to use our own template for this token, because the title isn't automatically linked, if the required object exists in our documentation. Because this would be a cool features, let's implement it.

First of all we have to create our partial. Like all other templates, the token-partials are located under views/tokens. So we create a file called views/tokens/_requires.html.erb and fill in some content:

<section>
  <h3>Dependencies</h3>
  <ul>
    <% tokens.each do |token| %> 
      <li>
        <h4><%=link_to token.name %></h4>
        <%=to_html token.content %>
      </li>
    <% end %>
  </ul>
</section>

Because all tokens of one type are rendered as a collection by default, we have to iterate over our local-variable tokens. We use the link_to helper to create a link to the referenced dependency if it exists in our documentation. Additionally we use the to_html helper to convert the description (which could be written using markdown and contain links we need to generate) to html.

Last thing we have to do, is to make the renderer use our new template for @requires tokens

register :requires, :area => :sidebar, :handler => :named, :template => :requires

2. Example - Writing your own token-handler

There are a few handlers, which can be used to process the tokenlines. But sometimes one of the existing handlers isn't enough to process your token.

Let's say we need a tokenhandler to parse something like

/**
 * @special [String] my_name (default) description
 */

We could get pretty close using the :typed_with_name handler. But this handler doesn't recognizes defaults, so we need to write our own handler.

First of all we register our token in tokens/tokens.rb.

register :special do |tokenklass, content|
  # currently the textual content is only stored as `content`
  self.add_token token_klass.new(:content => stringcontent)
end

Next let's write a Regexp, that recognizes our parts. (Ok, that regular expression may not be the most beautiful one, but it may work)

register :special do |token_klass, content|

  TYPED_NAMED_WITH_DEFAULTS = /\[([^\]\n]+)\]\s*([\w_.:]+)\s(?:\(([^\)]+)\))?\s*(.*)/

  types, name, default, content = TYPED_NAMED_WITH_DEFAULTS.match(content).captures
  token = token_klass.new(:name => name, :types => types.split(','), :content => content)
  token.define_singleton_method(:default) { default }

  self.add_token token
end

Because :default isn't a default property of token, we have to add it manually by defining a method which returns the value (define_singleton_method).

The last thing, we would have to do is to create a custom template and use it. See above for tipps how to achieve this. The template could make use of token.default to access the default value parsed by our cool token-handler.

3. Example - Creating the custom type class

If you want to create your own Domain specific language (DSL) it's often not enough to add new tokens and manipulate the templates. Sometimes you need to create your own custom types of objects. For example maybe you could need classes, packages or mixins. So in this example we will create a type called class.

The template-folder types/ contains 3 items by default:

types/
  function.rb
  object.rb
  prototype.rb

We will create a fourth one and call it class.rb, which at first only consists of

class CodeObject::Class < CodeObject::Base
end

Then we need to require it from application.rb

require_relative 'types/class.rb'

We get serious problems here, because in ruby we cannot redefine Class without getting into troubles. With nearly all other Type-Names everything wents fine, but for Class we need something different. Maybe we can simply call it Klass or ClassType to bypass any trouble. It's up to you.

class CodeObject::Klass < CodeObject::Base
end

Based on the name you choosed, we now have to register our token, to be a Type-Creating Token. Every Comment needs exactly one of these type-specificating tokens. With the following line (either in tokens.rb or in class.rb, after defining our type) we make our token work:

Token::Handler.register :class, :type => CodeObject::Klass

Using @class in a comment will create an instance of CodeObject::Klass and fill it with all details of that comment.

If we don't want our type-token to appear in any token-listing like :body or :sidebar we can specify :none for the rendering-area.

Token::Handler.register :class, :type => CodeObject::Klass, :area => :none

If we test our customized template with something like

/** 
 * @class Collection
 */
var Collection = function() { ... };
Collection.prototype = Array.prototype;

we may notice, that nothing is really different from using @object instead. That's only because our template is not yet specialised to work with classes.

Modifying a Generator

We learned how to create our own templates. But now we need to switch between different templates before rendering starts. To understand where rendering starts, we need to inspect the generators. Generators in Doc.js are pretty much like Controllers in Rails. They decide which template to use and where to save the result. So let's take a look at the generators/api_pages_generator.rb. We can see that every generator is able to choose which layout it want's to render the pages in.

We may notice, that the ApiPagesGenerator is nothing more than a switch for CodeObject-Types. By default it only differs between functions and those, that are not functions.

We can add some code, to use own templates for classes.

Dom.root.each_child do |node| 
  next if node.is_a? Dom::NoDoc 

  if node.is_a? CodeObject::Function
    render_function node
  elsif node.is_a? CodeObject::Klass
    render_class node
  else
    render_object node
  end
end

We invoke a method, which is not yet defined 'render_class'. This method get's the node as the only parameter and needs to create html-output from it.

def render_class(code_object)       

  Logger.info "Rendering Class: '#{code_object.name}'"

  # This is needed to eventually resolve relative links included in the documentation
  in_context code_object do

    # The instance-variables are also available in the template-views
    @class = code_object
    @methods = @object.children.values.select {|c| c.is_a? CodeObject::Function }

    # Here we call our new template, stored under `views/class/index.html.erb`
    render 'class/index', :to_file => path_to(code_object, :format => :html)
  end
end

The last thing (and granted, this may be the biggest part) is to create a template, which is optimzed regarding our new type class.

Combination with a custom handler

Often classes are used in context of the classical inheritance. Once class extends another. So we may add a new token @extends, which is a text-only token-handler, maybe with a custom template. (See first example above)

It would be much more interesting to add a documentation syntax like:

/**
 * @class Student < Person
 */

This is way easiert, than it may look like at first glance. We only have to write a custom handler:

Token::Handler.register :class, :type => CodeObject::Klass, :area => :none do |tokenklass, content|
  extends = /\s*[^\s]+\s*(?:<\s*([^\s]+))/.match(content)

  self.add_token Token::Token::ExtendsToken.new(:content => extends.captures.first) unless extends.nil?
end

What is this doing? We first match our token-content against a regular expression like (Word (< Word)?) the inner word is captured. If this matches we use the second part (after the <) to create an @extends token.