diff --git a/lib/puppetx/puppetlabs/strings/yard/templates/default/definedtype/html/docstring.erb b/lib/puppetx/puppetlabs/strings/yard/templates/default/definedtype/html/docstring.erb index edebfa0..900985f 100644 --- a/lib/puppetx/puppetlabs/strings/yard/templates/default/definedtype/html/docstring.erb +++ b/lib/puppetx/puppetlabs/strings/yard/templates/default/definedtype/html/docstring.erb @@ -27,23 +27,7 @@

Return:

<% end %> diff --git a/lib/puppetx/puppetlabs/strings/yard/templates/default/definedtype/html/parameter_details.erb b/lib/puppetx/puppetlabs/strings/yard/templates/default/definedtype/html/parameter_details.erb index b55a473..a085ac0 100644 --- a/lib/puppetx/puppetlabs/strings/yard/templates/default/definedtype/html/parameter_details.erb +++ b/lib/puppetx/puppetlabs/strings/yard/templates/default/definedtype/html/parameter_details.erb @@ -1,41 +1,6 @@

Parameter Summary

diff --git a/lib/puppetx/puppetlabs/strings/yard/templates/default/definedtype/setup.rb b/lib/puppetx/puppetlabs/strings/yard/templates/default/definedtype/setup.rb index adc2778..39ae076 100644 --- a/lib/puppetx/puppetlabs/strings/yard/templates/default/definedtype/setup.rb +++ b/lib/puppetx/puppetlabs/strings/yard/templates/default/definedtype/setup.rb @@ -1,7 +1,13 @@ include T('default/module') +require File.join(File.dirname(__FILE__),'../html_helper') +require File.join(File.dirname(__FILE__),'../template_helper') + def init sections :header, :box_info, :pre_docstring, :docstring, :parameter_details + + @template_helper = TemplateHelper.new + @html_helper = HTMLHelper.new end def parameter_details @@ -12,7 +18,7 @@ def parameter_details @param_details = [] - @param_details = extract_param_details(params, param_tags) + @param_details = @template_helper.extract_param_details(params, param_tags, true) erb(:parameter_details) end @@ -30,63 +36,8 @@ def header end def docstring - examples = Hash.new - example_tags = object.tags.find_all { |tag| tag.tag_name == "example" } - example_tags.each do |example| - examples["#{example.name}"] = example.text - end - return_tag = object.tags.find { |tag| tag.tag_name == "return"} - return_text = return_tag.nil? ? nil : return_tag.text - return_types = return_tag.nil? ? nil : return_tag.types - return_details = (return_text.nil? && return_types.nil?) ? nil : [return_text, return_types] - - since_tag = object.tags.find { |tag| tag.tag_name == "since"} - since_text = since_tag.nil? ? nil : since_tag.text - - @class_details = {:name => object.name, :desc => object.docstring, :examples => examples, :since => since_text, :return => return_details} + @class_details = @template_helper.extract_tag_data(object) erb(:docstring) end - -# Given the parameter information and YARD param tags, extracts the -# useful information and returns it as an array of hashes which can -# be printed and formatted in the paramters_details erb file -# -# @param params_hash [Array] parameter details obtained programmatically -# @param tags_hash [Array] parameter details obtained from comments -# -# @return [Hash] The relevant information about each parameter -# @option opts [String] :name The name of the parameter -# @option opts [String] :fq_name The fully qualified parameter name -# @option opts [String] :desc The description provided in the comment -# @options opts [Array] :types The parameter type(s) specified in the comment -# @options opts [Boolean] :exists? True only if the parameter exists in the documented logic and not just in a comment -def extract_param_details(params_hash, tags_hash) - parameter_info = [] - - # Extract the information for parameters that actually exist - params_hash.each do |param| - param_tag = tags_hash.find { |tag| tag.name == param[0] } - - description = param_tag.nil? ? nil : param_tag.text - param_types = param_tag.nil? ? nil : param_tag.types - - parameter_info.push({:name => param[0], :fq_name => param[1], :desc => description, :types => param_types, :exists? => true}) - end - - # Check if there were any comments for parameters that do not exist - tags_hash.each do |tag| - param_exists = false - parameter_info.each do |parameter| - if parameter[:name] == tag.name - param_exists = true - end - end - if !param_exists - parameter_info.push({:name => tag.name, :fq_name => nil, :desc => tag.text, :types => tag.types, :exists? => false}) - end - end - - parameter_info -end diff --git a/lib/puppetx/puppetlabs/strings/yard/templates/default/html_helper.rb b/lib/puppetx/puppetlabs/strings/yard/templates/default/html_helper.rb new file mode 100644 index 0000000..b9a4a58 --- /dev/null +++ b/lib/puppetx/puppetlabs/strings/yard/templates/default/html_helper.rb @@ -0,0 +1,68 @@ +# A class containing helper methods to aid the generation of HTML +# given formatted data +class HTMLHelper + + # Generates the HTML to format the relevant data about return values + def generate_return_types(types, desc = nil) + result = [] + + result << "(" << types.join(", ") << ")" + + if !desc.nil? + result << "-

#{desc}

" + end + + result.join + end + + # Generates the HTML to format the relevant data about parameters + def generate_parameters(params) + result = [] + + params.each do |param| + result << "
  • " + + # Parameters which are documented in the comments but not + # present in the code itself are given the strike through + # styling in order to show the reader that they do not actually + # exist + if !param[:exists?] + result << "" + end + + result << "#{param[:name]} " + result << "" + + if param[:types] + result << "(" << "" << param[:types].join(", ") << "" << ")" + # Don't bother with TBD since 3x functions will never have type info per parameter. + # However if the user does want to list a type for some reason that is still supported, + # we just don't want to suggest that they need to + elsif !param[:puppet_3_func] + result << "(TBD)" + end + + result << "" + + # This is only relevant for manifests, not puppet functions + # This is due to the fact that the scope of a parameter (as illustrated by + # by it's fully qualified name) is not relevant for the parameters in puppet + # functions, but may be for components of a manifest (i.e. classes) + unless param[:fq_name].nil? + result << " => #{param[:fq_name]}" + end + + if param[:desc] + result << "-

    #{param[:desc]}

    " + end + + if !param[:exists?] + result << "
    " + end + + result << "
  • " + end + + result.join + end +end diff --git a/lib/puppetx/puppetlabs/strings/yard/templates/default/puppetnamespace/html/box_info.erb b/lib/puppetx/puppetlabs/strings/yard/templates/default/puppetnamespace/html/box_info.erb new file mode 100644 index 0000000..1780c32 --- /dev/null +++ b/lib/puppetx/puppetlabs/strings/yard/templates/default/puppetnamespace/html/box_info.erb @@ -0,0 +1,11 @@ +
    +
    Defined in:
    +
    + <% @source_files.each do |file| %> + <%= file[0] %>: + <%= file[1] %> +
    + <% end %> +
    +
    +
    diff --git a/lib/puppetx/puppetlabs/strings/yard/templates/default/puppetnamespace/html/header.erb b/lib/puppetx/puppetlabs/strings/yard/templates/default/puppetnamespace/html/header.erb new file mode 100644 index 0000000..74b3dba --- /dev/null +++ b/lib/puppetx/puppetlabs/strings/yard/templates/default/puppetnamespace/html/header.erb @@ -0,0 +1,5 @@ +
    +

    + <%= @header_text %> +

    +
    diff --git a/lib/puppetx/puppetlabs/strings/yard/templates/default/puppetnamespace/html/method_details_list.erb b/lib/puppetx/puppetlabs/strings/yard/templates/default/puppetnamespace/html/method_details_list.erb new file mode 100644 index 0000000..8050727 --- /dev/null +++ b/lib/puppetx/puppetlabs/strings/yard/templates/default/puppetnamespace/html/method_details_list.erb @@ -0,0 +1,53 @@ +

    Function Details

    +<% @class_details.each do |func| %> +

    > + + <% if func[:return] %> + <%= @html_helper.generate_return_types(func[:return][1]) %> + <% end %> + <%= func[:name] %> + +

    +
    +
    +

    <%= htmlify(func[:desc]) %>

    +
    +
    +
    + <% if func[:examples] != {}%> +
    +

    Examples:

    + <% func[:examples].each do |title, text| %> +

    <%= title %>

    +
    <%= text %>
    + <% end %> +
    + <% end %> + <% if func[:since] %> +

    Since:

    + + <% end %> + <% if func[:return] %> +

    Returns:

    + + <% end %> + <% if func[:params] != nil %> +

    Parameters:

    +
    + +
    + <% end %> +
    +<% end %> diff --git a/lib/puppetx/puppetlabs/strings/yard/templates/default/puppetnamespace/html/method_summary.erb b/lib/puppetx/puppetlabs/strings/yard/templates/default/puppetnamespace/html/method_summary.erb new file mode 100644 index 0000000..11ec619 --- /dev/null +++ b/lib/puppetx/puppetlabs/strings/yard/templates/default/puppetnamespace/html/method_summary.erb @@ -0,0 +1,20 @@ +

    Available Functions

    + diff --git a/lib/puppetx/puppetlabs/strings/yard/templates/default/puppetnamespace/setup.rb b/lib/puppetx/puppetlabs/strings/yard/templates/default/puppetnamespace/setup.rb index cf37b36..054cee2 100644 --- a/lib/puppetx/puppetlabs/strings/yard/templates/default/puppetnamespace/setup.rb +++ b/lib/puppetx/puppetlabs/strings/yard/templates/default/puppetnamespace/setup.rb @@ -1,7 +1,89 @@ include T('default/module') +require File.join(File.dirname(__FILE__),'../html_helper') +require File.join(File.dirname(__FILE__),'../template_helper') + def init - sections :header, :box_info, :pre_docstring, T('docstring'), + sections :header, :box_info, :method_summary, [:item_summary], :method_details_list, [T('method_details')] + + @methods = object.children + @template_helper = TemplateHelper.new +end + +def header + # The list is expected to only contain one type of function + if @methods[0]['puppet_4x_function'] + @header_text = "Puppet 4 Functions" + else + @header_text = "Puppet 3 Functions" + end + + erb(:header) +end + +def box_info + @source_files = [] + + @methods.each do |method| + # extract the file name and line number for each method + file_name = method.files[0][0] + line_number = method.files[0][1] + + @source_files.push([method.name, "#{file_name} (#{line_number})"]) + end + + erb(:box_info) +end + +def method_summary + @method_details = [] + @html_helper = HTMLHelper.new + + @methods.each do |method| + # If there are multiple sentences in the method description, only + # use the first one for the summary. If the author did not include + # any periods in their summary, include the whole thing + first_sentence = method.docstring.match(/^(.*?)\./) + brief_summary = first_sentence ? first_sentence : method.docstring + + return_tag = method.tags.find { |tag| tag.tag_name == "return"} + return_types = return_tag.nil? ? nil : return_tag.types + + @method_details.push({:name => method.name, :short_desc => brief_summary, :return_types => return_types}) + end + + erb(:method_summary) +end + +def method_details_list + @class_details = [] + @html_helper = HTMLHelper.new + + @methods.each do |object| + + method_info = @template_helper.extract_tag_data(object) + param_details = nil + param_tags = object.tags.find_all{ |tag| tag.tag_name == "param"} + + if object['puppet_4x_function'] + # Extract the source code + source_code = object.source + # Extract the parameters for the source code + parameters = source_code.match(/(?:def .*)\((.*?)\)/) + # Convert the matched string into an array of strings + params = parameters.nil? ? nil : parameters[1].split(/\s*,\s*/) + + param_details = @template_helper.extract_param_details(params, param_tags) unless params.nil? + else + param_details = @template_helper.comment_only_param_details(param_tags) + end + + method_info[:params] = param_details + + @class_details.push(method_info) + end + + erb(:method_details_list) end diff --git a/lib/puppetx/puppetlabs/strings/yard/templates/default/template_helper.rb b/lib/puppetx/puppetlabs/strings/yard/templates/default/template_helper.rb new file mode 100644 index 0000000..40cf94e --- /dev/null +++ b/lib/puppetx/puppetlabs/strings/yard/templates/default/template_helper.rb @@ -0,0 +1,107 @@ +# A class containing helper methods to aid in the extraction of relevant data +# from comments and YARD tags +class TemplateHelper + + # Extracts data from comments which include the supported YARD tags + def extract_tag_data(object) + examples = Hash.new + example_tags = object.tags.find_all { |tag| tag.tag_name == "example" } + example_tags.each do |example| + examples["#{example.name}"] = example.text + end + + return_tag = object.tags.find { |tag| tag.tag_name == "return"} + return_text = return_tag.nil? ? nil : return_tag.text + return_types = return_tag.nil? ? nil : return_tag.types + return_details = (return_text.nil? && return_types.nil?) ? nil : [return_text, return_types] + + since_tag = object.tags.find { |tag| tag.tag_name == "since"} + since_text = since_tag.nil? ? nil : since_tag.text + + {:name => object.name, :desc => object.docstring, :examples => examples, :since => since_text, :return => return_details} + end + + # Given the parameter information and YARD param tags, extracts the + # useful information and returns it as an array of hashes which can + # be printed and formatted as HTML + # + # @param parameters [Array] parameter details obtained programmatically + # @param tags_hash [Array] parameter details obtained from comments + # @param fq_name [Boolean] does this parameter have a fully qualified name? + # + # @return [Hash] The relevant information about each parameter with the following keys/values: + # {:name => [String] The name of the parameter + # :fq_name => [String] The fully qualified parameter name + # :desc => [String] The description provided in the comment + # :types => [Array] The parameter type(s) specified in the comment + # :exists => [Boolean] True only if the parameter exists in the documented logic and not just in a comment} + def extract_param_details(parameters, tags_hash, fq_name = false) + parameter_info = [] + + # Extract the information for parameters that actually exist + # as opposed to parameters that are defined only in the comments + parameters.each do |param| + + if fq_name + param_name = param[0] + fully_qualified_name = param[1] + else + param_name = param + end + + param_tag = tags_hash.find { |tag| tag.name == param_name } + + description = param_tag.nil? ? nil : param_tag.text + param_types = param_tag.nil? ? nil : param_tag.types + + param_details = {:name => param_name, :desc => description, :types => param_types, :exists? => true} + + if fq_name + param_details[:fq_name] = fully_qualified_name + end + + parameter_info.push(param_details) + end + + # Check if there were any comments for parameters that do not exist + tags_hash.each do |tag| + param_exists = false + parameter_info.each do |parameter| + if parameter[:name] == tag.name + param_exists = true + end + end + if !param_exists + parameter_info.push({:name => tag.name, :desc => tag.text, :types => tag.types, :exists? => false}) + end + end + + parameter_info + end + + # Generates parameter information in situations where the information can only + # come from YARD tags in the comments, not from the code itself. For now the only + # use for this is 3x functions. In this case exists? will always be true since we + # cannot verify if the parameter exists in the code itself. We must trust the user + # to provide information in the comments that is accurate. + # + # @param param_tags [Array] parameter details obtained from comments + # + # @return [Hash] The relevant information about each parameter with the following keys/values: + # {:name => [String] The name of the parameter + # :desc => [String] The description provided in the comment + # :types => [Array] The parameter type(s) specified in the comment + # :exists => [Boolean] True only if the parameter exists in the documented logic and not just in a comment + # :puppet_3_func => [Boolean] Are these parameters for a puppet 3 function? (relevant in HTML generation)} + def comment_only_param_details(param_tags) + return if param_tags.empty? + + parameter_info = [] + + param_tags.each do |tag| + parameter_info.push({:name => tag.name, :desc => tag.text, :types => tag.types, :exists? => true, :puppet_3_func => true}) + end + + parameter_info + end +end