Skip to content

Custom member types

Rene Saarsoo edited this page Feb 22, 2013 · 7 revisions

There are two broad categories of member types we can create:

  • :method_like - such members have parameters and possibly a return value. Things like methods and events.

  • :property_like - such members have a type and possibly some kind of (default) value. Things like properties and configs.

Method-like members are way easier to implement, so we start with them.

Method-like members

Lets implement a @hook tag to describe a special class of functions that will provide a way to hook into the internals of a class and extend it. For example:

/**
 * @hook before
 * Runs before the component is initialized.
 * @param {Object} self The component itself.
 * @param {Object} cfg Config options passed in constructor.
 */

An implementation for this is quite simple:

require "jsduck/tag/tag"

class Hook < JsDuck::Tag::Tag
  def initialize
    @tagname = :hook
    @pattern = "hook"
    @member_type = {
      :name => :hook,
      :category => :method_like,
      :title => "Hooks",
      :position => MEMBER_POS_CFG - 0.1,
    }
  end

  def parse_doc(scanner)
    return {
      :tagname => :hook,
      :name => scanner.ident,
    }
  end

  def process_doc(context, hook_tags, position)
    context[:name] = hook_tags[0][:name]
  end

end

The thing that turns this tag into a new member type is the setting of @member_type variable. There we give a :name to our new type, specify our chosen :category, and set a few rendering options.

:position defines the ordering of member sections in the final page. Here we position hooks at the very top - before configs. The title is shown at the top of each members section and is also used as a title for toolbar button.

parse_doc and process_doc are fairly trivial. We parse the name of the hook using builtin scanner.ident method, and assign the value to :name field in context hash. The :name field is a special field that every member type must have - it gets further processed internally for JSDuck.

The result will look like this:

Screenshot of custom hook member type

Property-like members

Now let's tackle a property-like member type. We'll define a @constant tag which indeed will work just like a @property, but with the semantic difference of documenting unchangable values. For example:

/**
 * @constant {Number} [GRAVITY=9.80665]
 * Acceleration of objects in Earth's gravitational field.
 */

That's really exactly the same syntax that builtin @cfg and @property use. Here's our implementation:

require "jsduck/tag/tag"

class Constant < JsDuck::Tag::Tag
  def initialize
    @tagname = :constant
    @pattern = "constant"
    @member_type = {
      :name => :constant,
      :category => :property_like,
      :title => "Constants",
      :position => MEMBER_POS_CFG - 0.1,
    }
  end

  def parse_doc(scanner)
    scanner.standard_tag({:tagname => :constant, :type => true, :name => true})
  end

  def process_doc(h, tags, pos)
    p = tags[0]
    h[:name] = p[:name]
    h[:type] = p[:type]
    h[:default] = p[:default]
  end

end

Now, instead of implementing all the complex parsing logic for extracting the type, name and value, we simply reuse the builtin mechanism form parsing such tags by calling the scanner.standard_tag method, which will parse the type and name parts of our tag and return the following hash:

{
  :tagname => :constant,
  :type => "Number",
  :name => "GRAVITY",
  :default => "9.80665"
}

In process_doc we assign those values separately to the context hash and JSDuck will already know how to treat them.

The result looks like this:

Screenshot of custom constant member type

Clone this wiki locally