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
User options from the command line.
Current pathname.
The output path.
Public Class Methods
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
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
# 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
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
RDoc
needs this to function.
# File lib/rdoc/generator/shomen.rb, line 119 def class_dir ; nil ; end
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
# File lib/rdoc/generator/shomen.rb, line 109 def constants_all @constants_all ||= classes.map{ |c| c.constants }.flatten end
RDoc
needs this to function.
# File lib/rdoc/generator/shomen.rb, line 122 def file_dir ; nil ; end
# File lib/rdoc/generator/shomen.rb, line 80 def files @files ||= ( @files_rdoc.select{ |f| f.parser != RDoc::Parser::Simple } ) end
# File lib/rdoc/generator/shomen.rb, line 94 def files_hash @files ||= RDoc::TopLevel.files_hash end
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
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
# 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
TODO: Rename ?
# File lib/rdoc/generator/shomen.rb, line 125 def shomen @table || {} end
Protected Instance Methods
# 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
# 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
# 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
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
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
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
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 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
# File lib/rdoc/generator/shomen.rb, line 222 def generate_metadata @table['(metadata)'] = project_metadata.to_h end
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 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
# 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
# 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
# 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
# File lib/rdoc/generator/shomen.rb, line 217 def project_metadata @project_metadata ||= Shomen::Metadata.new end
Private Instance Methods
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