require 'puppet' require 'puppet/pops' module PuppetStrings::Yard::Parsers::Puppet # Represents the base Puppet language statement. class Statement # The pattern for parsing docstring comments. COMMENT_REGEX = /^\s*#+\s?/ attr_reader :source attr_reader :file attr_reader :line attr_reader :docstring attr_reader :comments_range # Initializes the Puppet language statement. # @param object The Puppet parser model object for the statement. # @param [String] file The file name of the file containing the statement. def initialize(object, file) @file = file adapter = ::Puppet::Pops::Adapters::SourcePosAdapter.adapt(object) @source = adapter.extract_text @line = adapter.line @comments_range = nil end # Extracts the docstring for the statement given the source lines. # @param [Array] lines The source lines for the file containing the statement. # @return [void] def extract_docstring(lines) comment = [] (0..@line-2).reverse_each do |index| break unless index <= lines.count line = lines[index].strip count = line.size line.gsub!(COMMENT_REGEX, '') # Break out if nothing was removed (wasn't a comment line) break unless line.size < count comment << line end @comments_range = (@line - comment.size - 1..@line - 1) @docstring = YARD::Docstring.new(comment.reverse.join("\n")) end # Shows the first line context for the statement. # @return [String] Returns the first line context for the statement. def show "\t#{@line}: #{first_line}" end # Gets the full comments of the statement. # @return [String] Returns the full comments of the statement. def comments @docstring.all end # Determines if the comments have hash flag. # @return [Boolean] Returns true if the comments have a hash flag or false if not. def comments_hash_flag false end private def first_line @source.split(/\r?\n/).first.strip end end # Implements a parameterized statement (a statement that takes parameters). class ParameterizedStatement < Statement # Implements a parameter for a parameterized statement. class Parameter attr_reader :name attr_reader :type attr_reader :value # Initializes the parameter. # @param [Puppet::Pops::Model::Parameter] parameter The parameter model object. def initialize(parameter) @name = parameter.name # Take the exact text for the type expression if parameter.type_expr adapter = ::Puppet::Pops::Adapters::SourcePosAdapter.adapt(parameter.type_expr) @type = adapter.extract_text end # Take the exact text for the default value expression if parameter.value adapter = ::Puppet::Pops::Adapters::SourcePosAdapter.adapt(parameter.value) @value = adapter.extract_text end end end attr_reader :parameters # Initializes the parameterized statement. # @param object The Puppet parser model object that has parameters. # @param [String] file The file containing the statement. def initialize(object, file) super(object, file) @parameters = object.parameters.map { |parameter| Parameter.new(parameter) } end end # Implements the Puppet class statement. class ClassStatement < ParameterizedStatement attr_reader :name attr_reader :parent_class # Initializes the Puppet class statement. # @param [Puppet::Pops::Model::HostClassDefinition] object The model object for the class statement. # @param [String] file The file containing the statement. def initialize(object, file) super(object, file) @name = object.name @parent_class = object.parent_class end end # Implements the Puppet defined type statement. class DefinedTypeStatement < ParameterizedStatement attr_reader :name # Initializes the Puppet defined type statement. # @param [Puppet::Pops::Model::ResourceTypeDefinition] object The model object for the defined type statement. # @param [String] file The file containing the statement. def initialize(object, file) super(object, file) @name = object.name end end # Implements the Puppet function statement. class FunctionStatement < ParameterizedStatement attr_reader :name attr_reader :type # Initializes the Puppet function statement. # @param [Puppet::Pops::Model::FunctionDefinition] object The model object for the function statement. # @param [String] file The file containing the statement. def initialize(object, file) super(object, file) @name = object.name if object.respond_to? :return_type type = object.return_type if type adapter = ::Puppet::Pops::Adapters::SourcePosAdapter.adapt(type) @type = adapter.extract_text.gsub('>> ', '') end end end end # Implements the Puppet plan statement. class PlanStatement < ParameterizedStatement attr_reader :name # Initializes the Puppet plan statement. # @param [Puppet::Pops::Model::PlanDefinition] object The model object for the plan statement. # @param [String] file The file containing the statement. def initialize(object, file) super(object, file) @name = object.name end end # Implements the Puppet data type alias statement. class DataTypeAliasStatement < Statement attr_reader :name attr_reader :alias_of # Initializes the Puppet data type alias statement. # @param [Puppet::Pops::Model::TypeAlias] object The model object for the type statement. # @param [String] file The file containing the statement. def initialize(object, file) super(object, file) type_expr = object.type_expr case type_expr when Puppet::Pops::Model::AccessExpression # TODO: I don't like rebuilding the source from the AST, but AccessExpressions don't expose the original source @alias_of = ::Puppet::Pops::Adapters::SourcePosAdapter.adapt(type_expr.left_expr).extract_text + '[' @alias_of << type_expr.keys.map { |key| ::Puppet::Pops::Adapters::SourcePosAdapter.adapt(key).extract_text }.join(', ') @alias_of << ']' else adapter = ::Puppet::Pops::Adapters::SourcePosAdapter.adapt(type_expr) @alias_of = adapter.extract_text end @name = object.name end end end