class RDoc::Generator::Shomen

Shomen Adaptor for RDoc utilizes the rdoc tool to parse ruby source code to build a Shomen documenation file.

RDoc is almost entirely a free-form documentation system, so it is not possible for Shomen to fully harness all the details it can support from the RDoc documentation, such as method argument descriptions.

Constants

DESCRIPTION

Attributes

options[R]

User options from the command line.

path_base[R]

Current pathname.

path_output[R]

The output path.

Public Class Methods

for(options) click to toggle source

Standard generator factory method.

options - Generator options.

Returns new RDoc::Generator::Shomen instance.

# File lib/rdoc/generator/shomen.rb, line 35
def self.for(options)
  new(options)
end
new(options) click to toggle source

Initialize new generator.

options - Generator options.

Returns new RDoc::Generator::Shomen instance.

# File lib/rdoc/generator/shomen.rb, line 191
def initialize(options)
  @options = options
  #@options.diagram = false  # why?

  @path_base   = Pathname.pwd.expand_path

  # TODO: This is probably not needed any more.
  @path_output = Pathname.new(@options.op_dir).expand_path(@path_base)
end
setup_options(options) click to toggle source
# File lib/rdoc/generator/shomen.rb, line 43
def self.setup_options(options)
  options.source = false
  options.yaml   = false

  opt = options.option_parser

  opt.separator nil
  opt.separator "Shomen generator options:"
  opt.separator nil
  opt.on("--yaml", "Generate YAML document instead of JSON.") do |value|
    options.yaml = true
  end
  opt.separator nil
  opt.on("--source", "Include full source code for scripts.") do |value|
    options.github = true
  end
end

Public Instance Methods

attributes_all() click to toggle source

List of all attributes in all classes and modules.

# File lib/rdoc/generator/shomen.rb, line 104
def attributes_all
  @attributes_all ||= classes.map{ |m| m.attributes }.flatten.sort
end
class_dir() click to toggle source

RDoc needs this to function.

# File lib/rdoc/generator/shomen.rb, line 119
def class_dir ; nil ; end
classes() click to toggle source

In the world of the RDoc Generators classes is the same as all_classes_and_modules. Well, except that its sorted too. For classes sans modules, see types.

# File lib/rdoc/generator/shomen.rb, line 70
def classes
  @classes ||= RDoc::TopLevel.all_classes_and_modules.sort
end
classes_toplevel() click to toggle source

Only toplevel classes and modules.

# File lib/rdoc/generator/shomen.rb, line 75
def classes_toplevel
  @classes_toplevel ||= classes.select {|klass| !(RDoc::ClassModule === klass.parent) }
end
constants_all() click to toggle source
# File lib/rdoc/generator/shomen.rb, line 109
def constants_all
  @constants_all ||= classes.map{ |c| c.constants }.flatten
end
file_dir() click to toggle source

RDoc needs this to function.

# File lib/rdoc/generator/shomen.rb, line 122
def file_dir  ; nil ; end
files() click to toggle source
# File lib/rdoc/generator/shomen.rb, line 80
def files
  @files ||= (
    @files_rdoc.select{ |f| f.parser != RDoc::Parser::Simple }
  )
end
files_hash() click to toggle source
# File lib/rdoc/generator/shomen.rb, line 94
def files_hash
  @files ||= RDoc::TopLevel.files_hash
end
files_toplevel() click to toggle source

List of toplevel files. RDoc supplies this via the generate method.

# File lib/rdoc/generator/shomen.rb, line 87
def files_toplevel
  @files_toplevel ||= (
    @files_rdoc.select{ |f| f.parser == RDoc::Parser::Simple }
  )
end
generate(files) click to toggle source

Build the initial indices and output objects based on an array of top level objects containing the extracted information.

files - Files to document.

Returns nothing.

# File lib/rdoc/generator/shomen.rb, line 135
def generate(files)
  @files_rdoc = files.sort

  @table = {}

  generate_metadata
  generate_constants
  generate_classes
  #generate_attributes
  generate_methods
  generate_documents
  generate_scripts   # must be last b/c it depends on the others

  # TODO: method accessor fields need to be handled

  # THINK: Internal referencing model, YAML and JSYNC ?
  #ref_table = reference_table(@table)

  if options.yaml
    out = @table.to_yaml
  else
    out = JSON.generate(@table)
  end

  if options.op_dir == '-'
    puts out
  else
    File.open(output_file, 'w') do |f|
      f << out
    end unless $dryrun
  end

#rescue StandardError => err
#  debug_msg "%s: %s\n  %s" % [ err.class.name, err.message, err.backtrace.join("\n  ") ]
#  raise err
end
methods_all() click to toggle source

List of all methods in all classes and modules.

# File lib/rdoc/generator/shomen.rb, line 99
def methods_all
  @methods_all ||= classes.map{ |m| m.method_list }.flatten.sort
end
output_file() click to toggle source
# File lib/rdoc/generator/shomen.rb, line 173
def output_file
  name = project_metadata['name']
  vers = project_metadata['version']

  if name && vers
    "#{name}-#{vers}.json"
  else
   'doc.json'
  end
end
shomen() click to toggle source

TODO: Rename ?

# File lib/rdoc/generator/shomen.rb, line 125
def shomen
  @table || {}
end

Protected Instance Methods

collect_attributes(class_module, singleton=false) click to toggle source
# File lib/rdoc/generator/shomen.rb, line 527
def collect_attributes(class_module, singleton=false)
  list = []
  class_module.attributes.each do |a|
    next if singleton ^ a.singleton
    #p a.rw
    #case a.rw
    #when :write, 'W'
    #  list << "#{method_name(a)}="
    #else
      list << method_name(a)
    #end
  end
  list.uniq
end
collect_methods(class_module, singleton=false) click to toggle source
# File lib/rdoc/generator/shomen.rb, line 517
def collect_methods(class_module, singleton=false)
  list = []
  class_module.method_list.each do |m|
    next if singleton ^ m.singleton
    list << method_name(m)
  end
  list.uniq
end
comment(rdoc_comment) click to toggle source
# File lib/rdoc/generator/shomen.rb, line 577
def comment(rdoc_comment)
  case rdoc_comment
  when String
    "" # something is wrong if this is a string
  else
    rdoc_comment.text
  end
end
complete_name(name, namespace) click to toggle source

Returns String of fully qualified name.

# File lib/rdoc/generator/shomen.rb, line 508
def complete_name(name, namespace)
  if name !~ /^#{namespace}/
    "#{namespace}::#{name}"
  else
    name
  end
end
debug_msg(msg) click to toggle source

Output progress information if rdoc debugging is enabled

# File lib/rdoc/generator/shomen.rb, line 566
def debug_msg(msg)
  return unless $DEBUG_RDOC
  case msg[-1,1]
    when '.' then tab = "= "
    when ':' then tab = "== "
    else          tab = "* "
  end
  $stderr.puts(tab + msg)
end
generate_classes() click to toggle source

Add classes (and modules) to table.

# File lib/rdoc/generator/shomen.rb, line 245
def generate_classes
  debug_msg "Generating class/module documentation:"

  classes.each do |rdoc_class|
    debug_msg "%s (%s)" % [ rdoc_class.full_name, rdoc_class.path ]

    if rdoc_class.type=='class'
      model = Shomen::Model::Class.new
    else
      model = Shomen::Model::Module.new
    end

    model.path             = rdoc_class.full_name
    model.name             = rdoc_class.name
    model.namespace        = rdoc_class.full_name.split('::')[0...-1].join('::')
    model.includes         = rdoc_class.includes.map{ |x| x.name }  # FIXME: How to "lookup" full name?
    model.extensions       = []                                     # TODO:  How to get extensions?
    model.comment          = comment(rdoc_class.comment)
    model.format           = 'rdoc'
    model.constants        = rdoc_class.constants.map{ |x| complete_name(x.name, rdoc_class.full_name) }
    model.modules          = rdoc_class.modules.map{ |x| complete_name(x.name, rdoc_class.full_name) }
    model.classes          = rdoc_class.classes.map{ |x| complete_name(x.name, rdoc_class.full_name) }
    model.methods          = rdoc_class.method_list.map{ |m| method_name(m) }.uniq
    model.accessors        = rdoc_class.attributes.map{ |a| method_name(a) }.uniq  #+ ":#{a.rw}" }.uniq
    model.files            = rdoc_class.in_files.map{ |x| "/#{x.full_name}" }

    if rdoc_class.type == 'class'
      # HACK: No idea why RDoc is returning some weird superclass:
      #   <RDoc::NormalClass:0xd924d4 class Object < BasicObject includes: []
      #     attributes: [] methods: [#<RDoc::AnyMethod:0xd92b8c Object#fileutils
      #     (public)>] aliases: []>
      # Maybe it has something to do with #fileutils?
      model.superclass = (
        case rdoc_class.superclass
        when nil
        when String
          rdoc_class.superclass
        else
          rdoc_class.superclass.full_name
        end
      )
    end

    @table[model.path] = model.to_h
  end
end
generate_constants() click to toggle source

Add constants to table.

# File lib/rdoc/generator/shomen.rb, line 227
def generate_constants
  debug_msg "Generating constant documentation:"
  constants_all.each do |rdoc|
    model = Shomen::Model::Constant.new

    model.path      = rdoc.parent.full_name + '::' + rdoc.name
    model.name      = rdoc.name
    model.namespace = rdoc.parent.full_name
    model.comment   = comment(rdoc.comment)
    model.format    = 'rdoc'
    model.value     = rdoc.value
    model.files     = ["/#{rdoc.file.full_name}"]

    @table[model.path] = model.to_h
  end
end
generate_documents() click to toggle source

Generate entries for information files, e.g. ‘README.rdoc`.

# File lib/rdoc/generator/shomen.rb, line 431
def generate_documents
  files_toplevel.each do |rdoc_document|
    absolute_path = File.join(path_base, rdoc_document.full_name)

    model = Shomen::Model::Document.new

    model.path     = rdoc_document.full_name
    model.name     = File.basename(absolute_path)
    model.created  = File.mtime(absolute_path)
    model.modified = File.mtime(absolute_path)
    model.text     = File.read(absolute_path) #comment(file.comment)
    model.format   = mime_type(absolute_path)

    @table['/'+model.path] = model.to_h
  end
end
generate_metadata() click to toggle source
# File lib/rdoc/generator/shomen.rb, line 222
def generate_metadata
  @table['(metadata)'] = project_metadata.to_h
end
generate_methods() click to toggle source

Transform RDoc methods to Shomen model and add to table.

# File lib/rdoc/generator/shomen.rb, line 295
def generate_methods
  debug_msg "Generating method documentation:"

  list = methods_all + attributes_all

  list.each do |rdoc_method|
    #debug_msg "%s" % [rdoc_method.full_name]

    #full_name  = method_name(m)
    #'prettyname'   => m.pretty_name,
    #'type'         => m.type, # class or instance

    model = Shomen::Model::Method.new

    model.path        = method_name(rdoc_method)
    model.name        = rdoc_method.name
    model.namespace   = rdoc_method.parent_name
    model.comment     = comment(rdoc_method.comment)
    model.format      = 'rdoc'
    model.aliases     = rdoc_method.aliases.map{ |a| method_name(a) }
    model.alias_for   = method_name(rdoc_method.is_alias_for)
    model.singleton   = rdoc_method.singleton

    model.declarations << rdoc_method.type.to_s #singleton ? 'class' : 'instance'
    model.declarations << rdoc_method.visibility.to_s

    model.interfaces = []
    if rdoc_method.call_seq
      rdoc_method.call_seq.split("\n").each do |cs|
        cs = cs.to_s.strip
        model.interfaces << parse_interface(cs) unless cs == ''
      end
    end
    model.interfaces << parse_interface("#{rdoc_method.name}#{rdoc_method.params}")

    model.returns    = []  # RDoc doesn't support specifying return values
    model.file       = '/'+rdoc_method.source_code_location.first
    model.line       = rdoc_method.source_code_location.last.to_i
    model.source     = rdoc_method.source_code_raw

    if rdoc_method.respond_to?(:c_function)
      model.language = rdoc_method.c_function ? 'c' : 'ruby'
    else
      model.language = 'ruby'
    end

    @table[model.path] = model.to_h
  end
end
generate_scripts() click to toggle source

Generate script entries.

# File lib/rdoc/generator/shomen.rb, line 451
def generate_scripts
  #debug_msg "Generating file documentation in #{path_output_relative}:"
  #templatefile = self.path_template + 'file.rhtml'

  files.each do |rdoc_file|
    debug_msg "%s" % [rdoc_file.full_name]

    absolute_path = File.join(path_base, rdoc_file.full_name)
    #rel_prefix  = self.path_output.relative_path_from(outfile.dirname)

    model = Shomen::Model::Script.new

    model.path      = rdoc_file.full_name
    model.name      = File.basename(rdoc_file.full_name)
    model.created   = File.ctime(absolute_path)
    model.modified  = File.mtime(absolute_path)

    if options.source
      model.source   = File.read(absolute_path) #comment(file.comment)
      model.language = mime_type(absolute_path)
    end

    webcvs = options.webcvs || project_metadata['webcvs']
    if webcvs
      model.uri      = File.join(webcvs, model.path)  # TODO: use open-uri ?
      model.language = mime_type(absolute_path)
    end

    #model.header   =
    #model.footer   =
    model.requires  = rdoc_file.requires.map{ |r| r.name }
    model.constants = rdoc_file.constants.map{ |c| c.full_name }

    # note that this utilizes the table we are building
    # so it needs to be the last thing done.
    @table.each do |k, h|
      case h['!']
      when 'module'
        model.modules ||= []
        model.modules << k if h['files'].include?(rdoc_file.full_name)
      when 'class'
        model.classes ||= []
        model.classes << k if h['files'].include?(rdoc_file.full_name)
      when 'method'
        model.methods ||= []
        model.methods << k if h['file'] == rdoc_file.full_name
      when 'class-method'
        model.class_methods ||= []
        model.class_methods << k if h['file'] == rdoc_file.full_name
      end
    end

    @table['/'+model.path] = model.to_h
  end
end
method_name(method) click to toggle source
# File lib/rdoc/generator/shomen.rb, line 543
def method_name(method)
  return nil if method.nil?
  if method.singleton
    i = method.full_name.rindex('::')     
    method.full_name[0...i] + '.' + method.full_name[i+2..-1]
  else
    method.full_name
  end
end
mime_type(path) click to toggle source
# File lib/rdoc/generator/shomen.rb, line 554
def mime_type(path)
  case File.extname(path)
  when '.rb', '.rbx' then 'text/ruby'
  when '.c' then 'text/c-source'
  when '.rdoc' then 'text/rdoc'
  when '.md', '.markdown' then 'text/markdown'
  else 'text/plain'
  end
end
path_output_relative(path=nil) click to toggle source
# File lib/rdoc/generator/shomen.rb, line 208
def path_output_relative(path=nil)
  if path
    path.to_s.sub(path_base.to_s+'/', '')
  else
    @path_output_relative ||= path_output.to_s.sub(path_base.to_s+'/', '')
  end
end
project_metadata() click to toggle source
# File lib/rdoc/generator/shomen.rb, line 217
def project_metadata
  @project_metadata ||= Shomen::Metadata.new
end

Private Instance Methods

parse_interface(interface) click to toggle source

Parse method interface.

TODO: remove any trailing comment too

# File lib/rdoc/generator/shomen.rb, line 400
def parse_interface(interface)
  args, block = [], {}

  interface, returns = interface.split(/[=-]\>/)
  interface = interface.strip
  if i = interface.index(/\)\s*\{/)
    block['image'] = interface[i+1..-1].strip
    interface = interface[0..i].strip
  end

  arguments = interface.strip.sub(/^.*?\(/,'').chomp(')')
  arguments = arguments.split(/\s*\,\s*/)
  arguments.each do |a|
    if a.start_with?('&')
      block['name'] = a
    else
      n,v = a.split('=')
      args << (v ? {'name'=>n,'default'=>v} : {'name'=>n})
    end
  end

  result = {}
  result['signature'] = interface
  result['arguments'] = args
  result['block']     = block unless block.empty?
  result['returns']   = returns.strip if returns
  return result
end