Commit 0bd5ca9f authored by Rene Saarsoo's avatar Rene Saarsoo
Browse files

Class for creating namespace-tree-structure.

JsDuck::Tree creates from doc-object list a tree structure consisting
of Ruby Hashes and Arrays.  Another method in jsduck.rb converts this
to JSON and outputs.

For all this added a unit test, but with it's single test it's mostly
just a sanity check.

The previous debug output was removed to separate method.
parent 6a6aa26c
Loading
Loading
Loading
Loading
+22 −10
Original line number Diff line number Diff line
@@ -4,6 +4,8 @@ require 'jsduck/lexer'
require 'jsduck/parser'
require 'jsduck/doc_parser'
require 'jsduck/merger'
require 'jsduck/tree'
require 'json'

require 'pp'

@@ -40,11 +42,9 @@ module JsDuck
    end
    docs
  end
end


if __FILE__ == $0 then
  JsDuck.parse_files(ARGV).each do |doc|
  def JsDuck.print_debug(docs)
    docs.each do |doc|
      puts (doc[:name] || "?") + ":"
      if doc[:tagname] == :class
        [:cfg, :property, :method, :event].each do |key|
@@ -56,3 +56,15 @@ if __FILE__ == $0 then
    end
  end

  # Given array of doc-objects, generates namespace tree that can be
  # later printed out in JSON.
  def JsDuck.print_tree(docs)
    puts "Docs.classData = " + JSON.generate( Tree.new.create(docs) ) + ";"
  end
end


if __FILE__ == $0 then
  JsDuck.print_tree( JsDuck.parse_files(ARGV) )
end

lib/jsduck/tree.rb

0 → 100644
+115 −0
Original line number Diff line number Diff line

module JsDuck

  # Creator of package-tree in the format expected by the
  # documentation browser UI.
  #
  # See unit test for example output.
  #
  class Tree
    def initialize
      @root = {
        :id => "apidocs",
        :iconCls => "icon-docs",
        :text => "API Documentation",
        :singleClickExpand => true,
        :children => []
      }
      @packages = {"" => @root}
    end

    # Given list of class documentation objects returns a
    # tree-structure that can be turned into JSON that's needed by
    # documentation browser interface.
    def create(docs)
      docs.each {|cls| add_class(cls) }
      sort_tree(@root)
      @root
    end

    # Sorts all child nodes, and recursively all child packages.
    def sort_tree(node)
      node[:children].sort! {|a,b| compare(a, b) }
      node[:children].find_all {|c| c[:cls] == "package" }.each {|c| sort_tree(c) }
    end

    # Comparson method that sorts package nodes before class nodes.
    def compare(a, b)
      if a[:cls] == b[:cls]
        a[:text] <=> b[:text]
      else
        a[:cls] == "package" ? -1 : 1
      end
    end

    # When package for the class exists, add class node to that
    # package; otherwise create the package first.
    def add_class(cls)
      parent_name = package_name(cls[:name])
      parent = @packages[parent_name] || add_package(parent_name)
      parent[:children] << class_node(cls)
    end

    # When parent package exists, add new package node into it, also
    # record the package into @packages hash for quick lookups;
    # otherwise create the parent package first.
    #
    # Note that the root package always exists, so we can safely
    # recurse knowing we will eventually stop.
    def add_package(name)
      parent_name = package_name(name)
      parent = @packages[parent_name] || add_package(parent_name)
      package = package_node(name)
      parent[:children] << package
      @packages[name] = package
      package
    end

    # Given full class name, returns package name
    #
    # For example:
    #     My.package.Class => My.package
    #     My.package => My
    #     My => ""
    def package_name(name)
      parts = name.split(/\./)
      parts.slice(0, parts.length - 1).join(".")
    end

    # Given full class name, returns class name
    #
    # For example:
    #     My.package.Class => Class
    #     My.package => package
    #     My => My
    def class_name(name)
      name.split(/\./).last
    end

    # Given full doc object for class creates class node
    def class_node(cls)
      return {
        :href => "output/#{cls[:name]}.html",
        :text => class_name(cls[:name]),
        :id => cls[:name],
        :isClass => true,
        :iconCls => cls[:singleton] ? "icon-static" : "icon-cls",
        :cls => "cls",
        :leaf => true
      }
    end

    # Given full package name like my.package creates package node
    def package_node(name)
      return {
        :id => "pkg-#{name}",
        :text => class_name(name),
        :iconCls => "icon-pkg",
        :cls => "package",
        :singleClickExpand => true,
        :children => []
      }
    end
  end

end

test/tc_tree.rb

0 → 100644
+49 −0
Original line number Diff line number Diff line
require "jsduck/tree"
require "test/unit"

class TestTree < Test::Unit::TestCase

  def test_create
    output = JsDuck::Tree.new.create([
      {:tagname => :class, :name => "SamplePackage.SampleClass"},
      {:tagname => :class, :name => "SamplePackage.Singleton", :singleton => true},
    ])
    assert_equal({
      :id => "apidocs",
      :iconCls => "icon-docs",
      :text => "API Documentation",
      :singleClickExpand => true,
      :children => [
        {
          :id => "pkg-SamplePackage",
          :text => "SamplePackage",
          :iconCls => "icon-pkg",
          :cls => "package",
          :singleClickExpand => true,
          :children => [
            {
              :href => "output/SamplePackage.SampleClass.html",
              :text => "SampleClass",
              :id => "SamplePackage.SampleClass",
              :isClass => true,
              :iconCls => "icon-cls",
              :cls => "cls",
              :leaf => true
            },
            {
              :href => "output/SamplePackage.Singleton.html",
              :text => "Singleton",
              :id => "SamplePackage.Singleton",
              :isClass => true,
              :iconCls => "icon-static",
              :cls => "cls",
              :leaf => true
            }
          ]
        }
      ]
    }, output)
  end

end