module RDoc::PuppetParserCore
Functionality common to both our RDoc version 1 and 2 parsers.
Constants
- SITE
Public Class Methods
# File lib/puppet/util/rdoc/parser/puppet_parser_core.rb 7 def self.included(base) 8 base.class_eval do 9 attr_accessor :input_file_name, :top_level 10 11 # parser registration into RDoc 12 parse_files_matching(/\.(rb)$/) 13 end 14 end
called with the top level file
# File lib/puppet/util/rdoc/parser/puppet_parser_core.rb 17 def initialize(top_level, file_name, body, options, stats) 18 @options = options 19 @stats = stats 20 @input_file_name = file_name 21 @top_level = top_level 22 @top_level.extend(RDoc::PuppetTopLevel) 23 @progress = $stderr unless options.quiet 24 end
Public Instance Methods
New instance of the appropriate PreProcess for our RDoc version.
# File lib/puppet/util/rdoc/parser/puppet_parser_core.rb 215 def create_rdoc_preprocess 216 raise(NotImplementedError, "This method must be overwritten for whichever version of RDoc this parser is working with") 217 end
Due to a bug in RDoc, we need to roll our own find_module_named The issue is that RDoc tries harder by asking the parent for a class/module of the name. But by doing so, it can mistakenly use a module of same name but from which we are not descendant.
# File lib/puppet/util/rdoc/parser/puppet_parser_core.rb 37 def find_object_named(container, name) 38 return container if container.name == name 39 container.each_classmodule do |m| 40 return m if m.name == name 41 end 42 nil 43 end
walk down the namespace and lookup/create container as needed
# File lib/puppet/util/rdoc/parser/puppet_parser_core.rb 46 def get_class_or_module(container, name) 47 48 # class ::A -> A is in the top level 49 if name =~ /^::/ 50 container = @top_level 51 end 52 53 names = name.split('::') 54 55 final_name = names.pop 56 names.each do |n| 57 prev_container = container 58 container = find_object_named(container, n) 59 container ||= prev_container.add_class(RDoc::PuppetClass, n, nil) 60 end 61 [container, final_name] 62 end
look_for_directives_in scans the current comment for RDoc directives
# File lib/puppet/util/rdoc/parser/puppet_parser_core.rb 220 def look_for_directives_in(context, comment) 221 preprocess = create_rdoc_preprocess 222 223 preprocess.handle(comment) do |directive, param| 224 case directive 225 when "stopdoc" 226 context.stop_doc 227 "" 228 when "startdoc" 229 context.start_doc 230 context.force_documentation = true 231 "" 232 when "enddoc" 233 #context.done_documenting = true 234 #"" 235 throw :enddoc 236 when "main" 237 options = Options.instance 238 options.main_page = param 239 "" 240 when "title" 241 options = Options.instance 242 options.title = param 243 "" 244 when "section" 245 context.set_current_section(param, comment) 246 comment.replace("") # 1.8 doesn't support #clear 247 break 248 else 249 warn "Unrecognized directive '#{directive}'" 250 break 251 end 252 end 253 remove_private_comments(comment) 254 end
this is a poor man custom fact parser :-)
# File lib/puppet/util/rdoc/parser/puppet_parser_core.rb 149 def parse_fact(container) 150 comments = "" 151 current_fact = nil 152 parsed_facts = [] 153 File.open(@input_file_name) do |of| 154 of.each do |line| 155 # fetch comments 156 if line =~ /^[ \t]*# ?(.*)$/ 157 comments += $1 + "\n" 158 elsif line =~ /^[ \t]*(Facter.add|Puppet\.runtime\[:facter\].add)\(['"](.*?)['"]\)/ 159 current_fact = RDoc::Fact.new($1,{}) 160 look_for_directives_in(container, comments) unless comments.empty? 161 current_fact.comment = comments 162 parsed_facts << current_fact 163 comments = "" 164 Puppet.debug "rdoc: found custom fact #{current_fact.name}" 165 elsif line =~ /^[ \t]*confine[ \t]*:(.*?)[ \t]*=>[ \t]*(.*)$/ 166 current_fact.confine = { :type => $1, :value => $2 } unless current_fact.nil? 167 else # unknown line type 168 comments ="" 169 end 170 end 171 end 172 parsed_facts.each do |f| 173 container.add_fact(f) 174 f.record_location(@top_level) 175 end 176 end
create documentation for plugins
# File lib/puppet/util/rdoc/parser/puppet_parser_core.rb 139 def parse_plugins(container) 140 Puppet.debug "rdoc: scanning plugin or fact" 141 if @input_file_name =~ /\/facter\/[^\/]+\.rb$/ 142 parse_fact(container) 143 else 144 parse_puppet_plugin(container) 145 end 146 end
this is a poor man puppet plugin parser :-) it doesn't extract doc nor desc :-(
# File lib/puppet/util/rdoc/parser/puppet_parser_core.rb 180 def parse_puppet_plugin(container) 181 comments = "" 182 current_plugin = nil 183 184 File.open(@input_file_name) do |of| 185 of.each do |line| 186 # fetch comments 187 if line =~ /^[ \t]*# ?(.*)$/ 188 comments += $1 + "\n" 189 elsif line =~ /^[ \t]*(?:Puppet::Parser::Functions::)?newfunction[ \t]*\([ \t]*:(.*?)[ \t]*,[ \t]*:type[ \t]*=>[ \t]*(:rvalue|:lvalue)/ 190 current_plugin = RDoc::Plugin.new($1, "function") 191 look_for_directives_in(container, comments) unless comments.empty? 192 current_plugin.comment = comments 193 current_plugin.record_location(@top_level) 194 container.add_plugin(current_plugin) 195 comments = "" 196 Puppet.debug "rdoc: found new function plugins #{current_plugin.name}" 197 elsif line =~ /^[ \t]*Puppet::Type.newtype[ \t]*\([ \t]*:(.*?)\)/ 198 current_plugin = RDoc::Plugin.new($1, "type") 199 look_for_directives_in(container, comments) unless comments.empty? 200 current_plugin.comment = comments 201 current_plugin.record_location(@top_level) 202 container.add_plugin(current_plugin) 203 comments = "" 204 Puppet.debug "rdoc: found new type plugins #{current_plugin.name}" 205 elsif line =~ /module Puppet::Parser::Functions/ 206 # skip 207 else # unknown line type 208 comments ="" 209 end 210 end 211 end 212 end
# File lib/puppet/util/rdoc/parser/puppet_parser_core.rb 256 def remove_private_comments(comment) 257 comment.gsub!(/^#--.*?^#\+\+/m, '') 258 comment.sub!(/^#--.*/m, '') 259 end
main entry point
# File lib/puppet/util/rdoc/parser/puppet_parser_core.rb 27 def scan 28 environment = Puppet.lookup(:current_environment) 29 scan_top_level(@top_level, environment) 30 @top_level 31 end
create documentation for the top level container
# File lib/puppet/util/rdoc/parser/puppet_parser_core.rb 103 def scan_top_level(container, environment) 104 # use the module README as documentation for the module 105 comment = "" 106 %w{README README.rdoc}.each do |rfile| 107 readme = File.join(File.dirname(File.dirname(@input_file_name)), rfile) 108 # module README should be UTF-8, not default system encoding 109 comment = File.open(readme,"r:UTF-8") { |f| f.read } if FileTest.readable?(readme) 110 end 111 look_for_directives_in(container, comment) unless comment.empty? 112 113 # infer module name from directory 114 name = split_module(@input_file_name, environment) 115 if name.nil? 116 # skip .pp files that are not in manifests directories as we can't guarantee they're part 117 # of a module or the global configuration. 118 # PUP-3638, keeping this while it should have no effect since no .pp files are now processed 119 container.document_self = false 120 return 121 end 122 123 Puppet.debug "rdoc: scanning for #{name}" 124 125 container.module_name = name 126 container.global=true if name == SITE 127 128 container, name = get_class_or_module(container,name) 129 mod = container.add_module(RDoc::PuppetModule, name) 130 mod.record_location(@top_level) 131 mod.add_comment(comment, @top_level) 132 133 if @input_file_name =~ /\.rb$/ 134 parse_plugins(mod) 135 end 136 end
split_module tries to find if path belongs to the module path if it does, it returns the module name, otherwise if we are sure it is part of the global manifest path, “__site__” is returned. And finally if this path couldn't be mapped anywhere, nil is returned.
# File lib/puppet/util/rdoc/parser/puppet_parser_core.rb 68 def split_module(path, environment) 69 # find a module 70 fullpath = File.expand_path(path) 71 Puppet.debug "rdoc: testing #{fullpath}" 72 if fullpath =~ /(.*)\/([^\/]+)\/(?:manifests|plugins|lib)\/.+\.(rb)$/ 73 modpath = $1 74 name = $2 75 Puppet.debug "rdoc: module #{name} into #{modpath} ?" 76 environment.modulepath.each do |mp| 77 if File.identical?(modpath,mp) 78 Puppet.debug "rdoc: found module #{name}" 79 return name 80 end 81 end 82 end 83 if fullpath =~ /\.(rb)$/ 84 # there can be paths we don't want to scan under modules 85 # imagine a ruby or manifest that would be distributed as part as a module 86 # but we don't want those to be hosted under <site> 87 environment.modulepath.each do |mp| 88 # check that fullpath is a descendant of mp 89 dirname = fullpath 90 previous = dirname 91 while (dirname = File.dirname(previous)) != previous 92 previous = dirname 93 return nil if File.identical?(dirname,mp) 94 end 95 end 96 end 97 # we are under a global manifests 98 Puppet.debug "rdoc: global manifests" 99 SITE 100 end