Skip to content
Rene Saarsoo edited this page Aug 5, 2013 · 6 revisions

Lets implement a @license tag to describe how we license our software. It should take as a parameter the name of the license:

/**
 * @class My.Class
 * An example class.
 * @license GNU General Public License v3
 * @license MIT License
 */

And here's an implementation for that:

require "jsduck/tag/tag"

class License < JsDuck::Tag::Tag
  def initialize
    @tagname = :license
    @pattern = "license"
    @html_position = POS_DOC + 0.1
    @repeatable = true
  end

  def parse_doc(scanner, position)
    text = scanner.match(/.*$/)
    return { :tagname => :license, :text => text }
  end

  def process_doc(context, license_tags, position)
    context[:license] = license_tags.map {|tag| tag[:text] }
  end

  def to_html(context)
    licenses = context[:license].map {|license| "<b>#{license}</b>" }.join(" and ")
    <<-EOHTML
      <p>This software is licensed under: #{licenses}.</p>
    EOHTML
  end
end

There's quite a lot of stuff in here, so lets go over it all step-by-step.

First thing to note is that this time we inherit our tag class from JsDuck::Tag::Tag. In previous chapter we extended BooleanTag, which in turn extended the JsDuck::Tag::Tag and performed a simple parsing and processing behind the scenes. This time around we implement our own parse_doc and process_doc methods.

We also define @repeatable = true to allow multiple @license tags to be used inside a single doc-comment.

Parsing

  def parse_doc(scanner, position)
    text = scanner.match(/.*$/)
    return { :tagname => :license, :text => text }
  end

parse_doc gets called every time the parser encounters our @license tag inside a doc-comment. JSDuck parses the name of the tag by itself and then passes control over to parse_doc to parse anything after the tag name, passing in an instance of JsDuck::Doc::Scanner on which we call the match method with a regex to extract all the text following the tag up to the end of line.

For our purposes we have now done enough of parsing and successfully extracted the name of the license. (See More about parsing for a deeper look at the Scanner object.)

At this point we need to return a hash with our extracted data. The hash must contain a :tagname field which must match up with the @tagname variable we defined in initialize. Anything else inside this hash is our custom data we store there for later processing - which in our case is just the name of the license.

The second position parameter contains file name and line number, which can be used for error reporting.

Processing

  def process_doc(context, license_tags, position)
    context[:license] = license_tags.map {|tag| tag[:text] }
  end

The hashes returned by parse_doc within one doc-comment get grouped together by the :tagname we specified and passed to process_doc. In our example case the value of license_tags parameter will be the following:

[
    {:tagname => :license, :text => "GNU General Public License"},
    {:tagname => :license, :text => "MIT License"},
]

The task of the process_doc method is to do any additional processing on this data and save it into the context hash (usually under the same key as our :tagname). In our case we just extract the names of our licenses.

The third position parameter contains file name and line number, which can be used for error reporting.

HTML output

  def to_html(context)
    licenses = context[:license].map {|license| "<b>#{license}</b>" }.join(" and ")
    <<-EOHTML
      <p>This software is licensed under: #{licenses}.</p>
    EOHTML
  end

This time inside to_html we make use of the context parameter, which is the same context that we modified in process_doc. So we just take the value we saved under :license key and turn it into HTML.

The result of this all will look as follows:

Screenshot of @license tag rendering