Register now and start sharing your code snippets.
-->

How to create a custom RDoc generator

Ruby posted about 1 year ago by christian

This is a work in progress… Heavily influenced by RAnnotate. Copy this to rdoc/generators:

   1  module Generators
   2  
   3    class CUSTOMGenerator                 
   4  
   5      TYPE        = {:file => 1, :class => 2, :module => 3 }
   6      VISIBILITY  = {:public => 1, :private => 2, :protected => 3 }
   7          
   8      def CUSTOMGenerator.for(options)
   9        new(options)
  10      end
  11          
  12      def initialize(options) #:not-new:
  13        @options = options
  14        
  15        # set up a hash to keep track of all the classes/modules we have processed
  16        @already_processed = {}
  17        
  18        # set up a hash to keep track of all of the objects to be output
  19        @output = {
  20          :files => [], 
  21          :classes => [], 
  22          :modules => [], 
  23          :attributes => [], 
  24          :methods => [], 
  25          :aliases => [], 
  26          :constants => [], 
  27          :requires => [], 
  28          :includes => []
  29        }
  30      end
  31  
  32      # Rdoc passes in TopLevel objects from the code_objects.rb tree (all files)
  33      def generate(files)                             
  34        # Each object passed in is a file, process it
  35        files.each { |file| process_file(file) }
  36      end
  37  
  38      private
  39  
  40      # process a file from the code_object.rb tree
  41      def process_file(file)
  42        @output[:files].push(file)
  43  
  44        puts "#{file.comment}"
  45            
  46        # Process all of the objects that this file contains
  47        file.method_list.each { |child| process_method(child) }
  48        file.aliases.each { |child| process_alias(child) }
  49        file.constants.each { |child| process_constant(child) }
  50        file.requires.each { |child| process_require(child) }
  51        file.includes.each { |child| process_include(child) }
  52        file.attributes.each { |child| process_attribute(child) }   
  53      
  54        # Recursively process contained subclasses and modules 
  55        file.each_classmodule do |child| 
  56            process_class_or_module(child)      
  57        end   
  58      end
  59      
  60      # Process classes and modiles   
  61      def process_class_or_module(obj)
  62        obj.is_module? ? type = :modules : type = :classes
  63   
  64        # One important note about the code_objects.rb structure. A class or module
  65        # definition can be spread a cross many files in Ruby so code_objects.rb handles
  66        # this by keeping only *one* reference to each class or module that has a definition
  67        # at the root level of a file (ie. not contained in another class or module).
  68        # This means that when we are processing files we may run into the same class/module
  69        # twice. So we need to keep track of what classes/modules we have
  70        # already seen and make sure we don't create two INSERT statements for the same
  71        # object.
  72        if(!@already_processed.has_key?(obj.full_name)) then      
  73          @output[type].push(obj)
  74          @already_processed[obj.full_name] = true
  75            
  76          # Process all of the objects that this class or module contains
  77          obj.method_list.each { |child| process_method(child) }
  78          obj.aliases.each { |child| process_alias(child) }
  79          obj.constants.each { |child| process_constant(child) }
  80          obj.requires.each { |child| process_require(child) }
  81          obj.includes.each { |child| process_include(child) }
  82          obj.attributes.each { |child| process_attribute(child) }   
  83        end
  84        
  85        id = @already_processed[obj.full_name]
  86        # Recursively process contained subclasses and modules 
  87        obj.each_classmodule do |child| 
  88        	process_class_or_module(child) 
  89        end
  90      end       
  91      
  92      def process_method(obj)  
  93        obj.source_code = get_source_code(obj)
  94         puts "#{obj.name}#{obj.param_seq}"
  95         puts "#{obj.source_code}" # Source code, unformatted
  96  
  97         puts "====================================="
  98  
  99        @output[:methods].push(obj)                                
 100      end
 101      
 102      def process_alias(obj)
 103        @output[:aliases].push(obj)  
 104      end
 105      
 106      def process_constant(obj)
 107        @output[:constants].push(obj)    
 108      end
 109      
 110      def process_attribute(obj)
 111        @output[:attributes].push(obj)     
 112      end
 113      
 114      def process_require(obj)
 115        @output[:requires].push(obj) 
 116      end
 117      
 118      def process_include(obj) 
 119        @output[:includes].push(obj)     
 120      end   
 121      
 122                 
 123      # get the source code
 124      def get_source_code(method)
 125        src = ""
 126  	  if(ts = method.token_stream)    
 127  	    ts.each do |t|
 128  	    next unless t    			
 129  	      src << t.text
 130  	    end
 131        end
 132        return src
 133      end
 134           
 135    end
 136  
 137  
 138     # dynamically add a source code attribute to the base oject of code_objects.rb
 139     class RDoc::AnyMethod
 140       attr_accessor :source_code	  
 141     end
 142  
 143  end
 144  
 145  

Tagged rdoc, ruby, generator