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