# Handles `dispatch` calls within a future parser function declaration. For # now, it just treats any docstring as an `@overlaod` tag and attaches the # overload to the parent function. class PuppetX::PuppetLabs::Strings::YARD::Handlers::PuppetProviderHandler < YARD::Handlers::Ruby::Base include PuppetX::PuppetLabs::Strings::YARD::CodeObjects handles :command_call, :call process do @heredoc_helper = HereDocHelper.new # Puppet types always begin with: # Puppet::Types.newtype... # Therefore, we match the corresponding trees which look like this: # s(:call, # s(:const_path_ref, # s(:var_ref, s(:const, "Puppet", ...), ...), # s(:const, "Type", ...), # You think this is ugly? It's better than the alternative. return unless statement.children.length > 2 first = statement.children.first.first return unless (first.source == 'Puppet::Type') || (first.type == :var_ref && first.source == 'Type') && statement[2].source == 'provide' i = statement.index { |s| YARD::Parser::Ruby::AstNode === s && s.type == :ident && s.source == 'provide' } provider_name = statement[i+1].jump(:ident).source type_name = statement.jump(:symbol).first.source obj = ProviderObject.new(:root, "#{provider_name}_provider") docstring = nil features = [] commands = [] confines = {} defaults = {} do_block = statement.jump(:do_block) do_block.traverse do |node| if is_a_func_call_named?('desc', node) content = node.jump(:tstring_content) # if we found the string content extract its source if content != node # The docstring is either the source stripped of heredoc # annotations or the raw source. if @heredoc_helper.is_heredoc?(content.source) docstring = @heredoc_helper.process_heredoc content.source else docstring = content.source end end elsif is_a_func_call_named?('confine', node) node.traverse do |s| if s.type == :assoc k = s.first.jump(:ident).source v = s[1].first.source confines[k] = v end end elsif is_a_func_call_named?('has_feature', node) list = node.jump :list if list != nil && list != node features += list.map { |s| s.source if YARD::Parser::Ruby::AstNode === s }.compact end elsif is_a_func_call_named?('commands', node) assoc = node.jump(:assoc) if assoc && assoc != node ident = assoc.jump(:ident) if ident && ident != assoc commands << ident.source end end elsif is_a_func_call_named?('defaultfor', node) node.traverse do |s| if s.type == :assoc k = s.first.jump(:ident).source v = s[1].first.source defaults[k] = v end end end end obj.features = features obj.commands = commands obj.confines = confines obj.defaults = defaults obj.type_name = type_name obj.header_name = provider_name register_docstring(obj, docstring, nil) register obj end def is_a_func_call_named?(name, node) (node.type == :fcall || node.type == :command || node.type == :vcall) && node.children.first.source == name end end