diff --git a/lib/puppet-strings/markdown.rb b/lib/puppet-strings/markdown.rb index b9e988954..da7577f41 100644 --- a/lib/puppet-strings/markdown.rb +++ b/lib/puppet-strings/markdown.rb @@ -11,23 +11,52 @@ module PuppetStrings::Markdown require_relative 'markdown/resource_types' require_relative 'markdown/puppet_tasks' require_relative 'markdown/puppet_plans' - require_relative 'markdown/table_of_contents' + + # Get modules that handle collecting and rendering each section. + # + # @return [Array[module]] The modules + def self.groups + [ + PuppetStrings::Markdown::PuppetClasses, + PuppetStrings::Markdown::DefinedTypes, + PuppetStrings::Markdown::ResourceTypes, + PuppetStrings::Markdown::Functions, + PuppetStrings::Markdown::DataTypes, + PuppetStrings::Markdown::PuppetTasks, + PuppetStrings::Markdown::PuppetPlans, + ] + end # generates markdown documentation # @return [String] markdown doc def self.generate - final = "# Reference\n\n" - final += "\n\n" - final += PuppetStrings::Markdown::TableOfContents.render - final += PuppetStrings::Markdown::PuppetClasses.render - final += PuppetStrings::Markdown::DefinedTypes.render - final += PuppetStrings::Markdown::ResourceTypes.render - final += PuppetStrings::Markdown::Functions.render - final += PuppetStrings::Markdown::DataTypes.render - final += PuppetStrings::Markdown::PuppetTasks.render - final += PuppetStrings::Markdown::PuppetPlans.render - - final + output = [ + "# Reference\n\n", + "\n\n", + "## Table of Contents\n\n", + ] + + # Create table of contents + template = erb(File.join(__dir__, 'markdown', 'templates', 'table_of_contents.erb')) + groups.each do |group| + group_name = group.name + items = group.items.map { |item| item.toc_info } + has_private = items.any? { |item| item[:private] } + has_public = items.any? { |item| !item[:private] } + + output << template.result(binding) + end + + # Create actual contents + groups.each do |group| + items = group.items.reject { |item| item.private? } + unless items.empty? + output << "## #{group.name}\n\n" + output.append(items.map { |item| item.render }) + end + end + + output.join('') end # mimicks the behavior of the json render, although path will never be nil @@ -41,4 +70,17 @@ def self.render(path = nil) YARD::Logger.instance.debug "Wrote markdown to #{path}" end end + + # Helper function to load an ERB template. + # + # @param [String] path The full path to the template file. + # @return [ERB] Template + def self.erb(path) + if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.6.0') + ERB.new(File.read(path), trim_mode: '-') + else + # This outputs warnings in Ruby 2.6+. + ERB.new(File.read(path), nil, '-') + end + end end diff --git a/lib/puppet-strings/markdown/base.rb b/lib/puppet-strings/markdown/base.rb index 2a40cf515..c73e8a7c0 100644 --- a/lib/puppet-strings/markdown/base.rb +++ b/lib/puppet-strings/markdown/base.rb @@ -199,17 +199,4 @@ def clean_link(input) input.tr('^a-zA-Z0-9_-', '-') end end - - # Helper function to load an ERB template. - # - # @param [String] path The full path to the template file. - # @return [ERB] Template - def self.erb(path) - if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.6.0') - ERB.new(File.read(path), trim_mode: '-') - else - # This outputs warnings in Ruby 2.6+. - ERB.new(File.read(path), nil, '-') - end - end end diff --git a/lib/puppet-strings/markdown/data_types.rb b/lib/puppet-strings/markdown/data_types.rb index 0fb1bd02c..d7c4dde0a 100644 --- a/lib/puppet-strings/markdown/data_types.rb +++ b/lib/puppet-strings/markdown/data_types.rb @@ -3,41 +3,19 @@ require_relative 'data_type' module PuppetStrings::Markdown + # Adapter to get information about data types module DataTypes + def self.name + 'Data types' + end # @return [Array] list of data types - def self.in_dtypes + def self.items arr = YARD::Registry.all(:puppet_data_type).map!(&:to_hash) arr.concat(YARD::Registry.all(:puppet_data_type_alias).map!(&:to_hash)) arr.sort! { |a,b| a[:name] <=> b[:name] } arr.map! { |a| PuppetStrings::Markdown::DataType.new(a) } end - - def self.contains_private? - result = false - unless in_dtypes.nil? - in_dtypes.find { |type| type.private? }.nil? ? false : true - end - end - - def self.render - final = in_dtypes.length > 0 ? "## Data types\n\n" : "" - in_dtypes.each do |type| - final += type.render unless type.private? - end - final - end - - - def self.toc_info - final = ["Data types"] - - in_dtypes.each do |type| - final.push(type.toc_info) - end - - final - end end end diff --git a/lib/puppet-strings/markdown/defined_types.rb b/lib/puppet-strings/markdown/defined_types.rb index a4c281a1c..daf2a9dd5 100644 --- a/lib/puppet-strings/markdown/defined_types.rb +++ b/lib/puppet-strings/markdown/defined_types.rb @@ -3,37 +3,16 @@ require_relative 'defined_type' module PuppetStrings::Markdown + # Adapter to get information about defined types module DefinedTypes + def self.name + 'Defined types' + end # @return [Array] list of defined types - def self.in_dtypes + def self.items arr = YARD::Registry.all(:puppet_defined_type).sort_by!(&:name).map!(&:to_hash) arr.map! { |a| PuppetStrings::Markdown::DefinedType.new(a) } end - - def self.contains_private? - result = false - unless in_dtypes.nil? - in_dtypes.find { |type| type.private? }.nil? ? false : true - end - end - - def self.render - final = in_dtypes.length > 0 ? "## Defined types\n\n" : "" - in_dtypes.each do |type| - final += type.render unless type.private? - end - final - end - - def self.toc_info - final = ["Defined types"] - - in_dtypes.each do |type| - final.push(type.toc_info) - end - - final - end end end diff --git a/lib/puppet-strings/markdown/functions.rb b/lib/puppet-strings/markdown/functions.rb index c111478c5..5345261c4 100644 --- a/lib/puppet-strings/markdown/functions.rb +++ b/lib/puppet-strings/markdown/functions.rb @@ -3,38 +3,17 @@ require_relative 'function' module PuppetStrings::Markdown + # Adapter to get information about functions module Functions + def self.name + 'Functions' + end # @return [Array] list of functions - def self.in_functions + def self.items arr = YARD::Registry.all(:puppet_function).sort_by!(&:name).map!(&:to_hash) arr.map! { |a| PuppetStrings::Markdown::Function.new(a) } end - - def self.contains_private? - result = false - unless in_functions.nil? - in_functions.find { |func| func.private? }.nil? ? false : true - end - end - - def self.render - final = in_functions.length > 0 ? "## Functions\n\n" : "" - in_functions.each do |func| - final += func.render unless func.private? - end - final - end - - def self.toc_info - final = ["Functions"] - - in_functions.each do |func| - final.push(func.toc_info) - end - - final - end end end diff --git a/lib/puppet-strings/markdown/puppet_classes.rb b/lib/puppet-strings/markdown/puppet_classes.rb index f22dd5789..6fcf1e7fe 100644 --- a/lib/puppet-strings/markdown/puppet_classes.rb +++ b/lib/puppet-strings/markdown/puppet_classes.rb @@ -3,37 +3,16 @@ require_relative 'puppet_class' module PuppetStrings::Markdown + # Adapter to get information about classes module PuppetClasses + def self.name + 'Classes' + end # @return [Array] list of classes - def self.in_classes + def self.items arr = YARD::Registry.all(:puppet_class).sort_by!(&:name).map!(&:to_hash) arr.map! { |a| PuppetStrings::Markdown::PuppetClass.new(a) } end - - def self.contains_private? - result = false - unless in_classes.nil? - in_classes.find { |klass| klass.private? }.nil? ? false : true - end - end - - def self.render - final = in_classes.length > 0 ? "## Classes\n\n" : "" - in_classes.each do |klass| - final += klass.render unless klass.private? - end - final - end - - def self.toc_info - final = ["Classes"] - - in_classes.each do |klass| - final.push(klass.toc_info) - end - - final - end end end diff --git a/lib/puppet-strings/markdown/puppet_plans.rb b/lib/puppet-strings/markdown/puppet_plans.rb index 7586eab01..e15286b49 100644 --- a/lib/puppet-strings/markdown/puppet_plans.rb +++ b/lib/puppet-strings/markdown/puppet_plans.rb @@ -3,37 +3,16 @@ require_relative 'puppet_plan' module PuppetStrings::Markdown + # Adapter to get information about plans module PuppetPlans + def self.name + 'Plans' + end # @return [Array] list of classes - def self.in_plans + def self.items arr = YARD::Registry.all(:puppet_plan).sort_by!(&:name).map!(&:to_hash) arr.map! { |a| PuppetStrings::Markdown::PuppetPlan.new(a) } end - - def self.contains_private? - result = false - unless in_plans.nil? - in_plans.find { |plan| plan.private? }.nil? ? false : true - end - end - - def self.render - final = in_plans.length > 0 ? "## Plans\n\n" : "" - in_plans.each do |plan| - final += plan.render unless plan.private? - end - final - end - - def self.toc_info - final = ["Plans"] - - in_plans.each do |plan| - final.push(plan.toc_info) - end - - final - end end end diff --git a/lib/puppet-strings/markdown/puppet_tasks.rb b/lib/puppet-strings/markdown/puppet_tasks.rb index 9d394f3fc..1c5437bd1 100644 --- a/lib/puppet-strings/markdown/puppet_tasks.rb +++ b/lib/puppet-strings/markdown/puppet_tasks.rb @@ -3,34 +3,16 @@ require_relative 'puppet_task' module PuppetStrings::Markdown + # Adapter to get information about tasks module PuppetTasks + def self.name + 'Tasks' + end # @return [Array] list of classes - def self.in_tasks + def self.items arr = YARD::Registry.all(:puppet_task).sort_by!(&:name).map!(&:to_hash) arr.map! { |a| PuppetStrings::Markdown::PuppetTask.new(a) } end - - def self.contains_private? - false - end - - def self.render - final = in_tasks.length > 0 ? "## Tasks\n\n" : "" - in_tasks.each do |task| - final += task.render unless task.private? - end - final - end - - def self.toc_info - final = ["Tasks"] - - in_tasks.each do |task| - final.push(task.toc_info) - end - - final - end end end diff --git a/lib/puppet-strings/markdown/resource_types.rb b/lib/puppet-strings/markdown/resource_types.rb index 01abe1c65..2af9d7162 100644 --- a/lib/puppet-strings/markdown/resource_types.rb +++ b/lib/puppet-strings/markdown/resource_types.rb @@ -3,37 +3,16 @@ require_relative 'resource_type' module PuppetStrings::Markdown + # Adapter to get information about resource types module ResourceTypes + def self.name + 'Resource types' + end # @return [Array] list of resource types - def self.in_rtypes + def self.items arr = YARD::Registry.all(:puppet_type).sort_by!(&:name).map!(&:to_hash) arr.map! { |a| PuppetStrings::Markdown::ResourceType.new(a) } end - - def self.contains_private? - result = false - unless in_rtypes.nil? - in_rtypes.find { |type| type.private? }.nil? ? false : true - end - end - - def self.render - final = in_rtypes.length > 0 ? "## Resource types\n\n" : "" - in_rtypes.each do |type| - final += type.render unless type.private? - end - final - end - - def self.toc_info - final = ["Resource types"] - - in_rtypes.each do |type| - final.push(type.toc_info) - end - - final - end end end diff --git a/lib/puppet-strings/markdown/table_of_contents.rb b/lib/puppet-strings/markdown/table_of_contents.rb deleted file mode 100644 index 5f7e15382..000000000 --- a/lib/puppet-strings/markdown/table_of_contents.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true - -module PuppetStrings::Markdown - module TableOfContents - def self.render - final = "## Table of Contents\n\n" - - [PuppetStrings::Markdown::PuppetClasses, - PuppetStrings::Markdown::DefinedTypes, - PuppetStrings::Markdown::ResourceTypes, - PuppetStrings::Markdown::Functions, - PuppetStrings::Markdown::DataTypes, - PuppetStrings::Markdown::PuppetTasks, - PuppetStrings::Markdown::PuppetPlans].each do |r| - toc = r.toc_info - group_name = toc.shift - group = toc - priv = r.contains_private? - - template = File.join(File.dirname(__FILE__), 'templates/table_of_contents.erb') - final += PuppetStrings::Markdown.erb(template).result(binding) - end - final - end - end -end diff --git a/lib/puppet-strings/markdown/templates/table_of_contents.erb b/lib/puppet-strings/markdown/templates/table_of_contents.erb index f20582a3a..72d91e207 100644 --- a/lib/puppet-strings/markdown/templates/table_of_contents.erb +++ b/lib/puppet-strings/markdown/templates/table_of_contents.erb @@ -1,26 +1,26 @@ -<% if group.length > 0 -%> +<% unless items.empty? -%> ### <%= group_name %> +<% if has_public -%> +<% if has_private # only display public heading if we have both -%> -<% if priv -%> #### Public <%= group_name %> +<% end -%> -<% group.each do |item| -%> +<% items.each do |item| -%> <% unless item[:private] -%> * [`<%= item[:name] %>`](#<%= item[:link] %>)<% unless item[:desc].nil? || item[:desc].empty? %>: <%= item[:desc] %><% end %> <% end -%> <% end -%> +<% end -%> +<% if has_private -%> #### Private <%= group_name %> -<% group.each do |item| -%> +<% items.each do |item| -%> <% if item[:private] -%> * `<%= item[:name] %>`<% unless item[:desc].nil? || item[:desc].empty? %>: <%= item[:desc] %><% end %> <% end -%> <% end -%> -<% else -%> -<% group.each do |item| -%> -* [`<%= item[:name] %>`](#<%= item[:link] %>)<% unless item[:desc].nil? || item[:desc].empty? %>: <%= item[:desc] %><% end %> -<% end -%> <% end -%> <% end -%> diff --git a/spec/unit/puppet-strings/markdown_spec.rb b/spec/unit/puppet-strings/markdown_spec.rb index 96246e58b..e9fe6ca34 100644 --- a/spec/unit/puppet-strings/markdown_spec.rb +++ b/spec/unit/puppet-strings/markdown_spec.rb @@ -2,56 +2,55 @@ require 'spec_helper' require 'puppet-strings/markdown' -require 'puppet-strings/markdown/table_of_contents' require 'tempfile' describe PuppetStrings::Markdown do - let(:fixture_path) do - File.expand_path("../../fixtures", __dir__) - end + describe 'rendering fixtures' do + let(:fixture_path) do + File.expand_path("../../fixtures", __dir__) + end - def fixture_content(fixture) - @fixtures ||= {} - @fixtures[fixture] ||= File.read(File.join(fixture_path, fixture)) - end + def fixture_content(fixture) + @fixtures ||= {} + @fixtures[fixture] ||= File.read(File.join(fixture_path, fixture)) + end - def parse_shared_content - # Populate the YARD registry with both Puppet and Ruby source - YARD::Parser::SourceParser.parse_string(fixture_content("puppet/class.pp"), :puppet) - YARD::Parser::SourceParser.parse_string(fixture_content("puppet/function.pp"), :puppet) - YARD::Parser::SourceParser.parse_string(fixture_content("ruby/func4x.rb"), :ruby) - YARD::Parser::SourceParser.parse_string(fixture_content("ruby/func4x_1.rb"), :ruby) - YARD::Parser::SourceParser.parse_string(fixture_content("ruby/func3x.rb"), :ruby) - YARD::Parser::SourceParser.parse_string(fixture_content("ruby/func3x.rb"), :ruby) - YARD::Parser::SourceParser.parse_string(fixture_content("ruby/provider.rb"), :ruby) - YARD::Parser::SourceParser.parse_string(fixture_content("ruby/resource_type.rb"), :ruby) - YARD::Parser::SourceParser.parse_string(fixture_content("ruby/resource_api.rb"), :ruby) - - # task metadata derives the task name from the filename, so we have to parse - # directly from the filesystem to correctly pick up the name - YARD::Parser::SourceParser.parse(File.join(fixture_path, "json/backup.json")) - end + def parse_shared_content + # Populate the YARD registry with both Puppet and Ruby source + YARD::Parser::SourceParser.parse_string(fixture_content("puppet/class.pp"), :puppet) + YARD::Parser::SourceParser.parse_string(fixture_content("puppet/function.pp"), :puppet) + YARD::Parser::SourceParser.parse_string(fixture_content("ruby/func4x.rb"), :ruby) + YARD::Parser::SourceParser.parse_string(fixture_content("ruby/func4x_1.rb"), :ruby) + YARD::Parser::SourceParser.parse_string(fixture_content("ruby/func3x.rb"), :ruby) + YARD::Parser::SourceParser.parse_string(fixture_content("ruby/func3x.rb"), :ruby) + YARD::Parser::SourceParser.parse_string(fixture_content("ruby/provider.rb"), :ruby) + YARD::Parser::SourceParser.parse_string(fixture_content("ruby/resource_type.rb"), :ruby) + YARD::Parser::SourceParser.parse_string(fixture_content("ruby/resource_api.rb"), :ruby) + + # task metadata derives the task name from the filename, so we have to parse + # directly from the filesystem to correctly pick up the name + YARD::Parser::SourceParser.parse(File.join(fixture_path, "json/backup.json")) + end - def parse_plan_content - # the parser behaves differently when parsing puppet files in the the plans directory, - # so we have to parse directly from the filesystem to correctly pick up the name - YARD::Parser::SourceParser.parse(File.join(fixture_path, "plans/plan.pp")) - end + def parse_plan_content + # the parser behaves differently when parsing puppet files in the the plans directory, + # so we have to parse directly from the filesystem to correctly pick up the name + YARD::Parser::SourceParser.parse(File.join(fixture_path, "plans/plan.pp")) + end - def parse_data_type_content - YARD::Parser::SourceParser.parse_string(fixture_content("ruby/data_type.rb"), :ruby) - YARD::Parser::SourceParser.parse_string(fixture_content("puppet/type_alias.pp"), :puppet) - end + def parse_data_type_content + YARD::Parser::SourceParser.parse_string(fixture_content("ruby/data_type.rb"), :ruby) + YARD::Parser::SourceParser.parse_string(fixture_content("puppet/type_alias.pp"), :puppet) + end - let(:output) { PuppetStrings::Markdown.generate } + let(:output) { PuppetStrings::Markdown.generate } - RSpec.shared_examples 'markdown lint checker' do |parameter| - it 'should not generate markdown lint errors from the rendered markdown' do - expect(output).to have_no_markdown_lint_errors + RSpec.shared_examples 'markdown lint checker' do |parameter| + it 'should not generate markdown lint errors from the rendered markdown' do + expect(output).to have_no_markdown_lint_errors + end end - end - describe 'markdown rendering' do before(:each) do parse_shared_content end @@ -160,4 +159,105 @@ def parse_data_type_content end end end + + it 'renders only private functions correctly' do + expect(YARD::Parser::SourceParser.parse_string(<<~'PUPPET', :puppet).enumerator.length).to eq(1) + # @return void + # @api private + function func_private() {} + PUPPET + + expect(described_class.generate).to eq(<<~'MARKDOWN') + # Reference + + + + ## Table of Contents + + ### Functions + + #### Private Functions + + * `func_private` + + MARKDOWN + end + + it 'renders only public functions correctly' do + expect(YARD::Parser::SourceParser.parse_string(<<~'PUPPET', :puppet).enumerator.length).to eq(1) + # @return void + function func_public() {} + PUPPET + + expect(described_class.generate).to eq(<<~'MARKDOWN') + # Reference + + + + ## Table of Contents + + ### Functions + + * [`func_public`](#func_public) + + ## Functions + + ### `func_public` + + Type: Puppet Language + + The func_public function. + + #### `func_public()` + + The func_public function. + + Returns: `Any` void + + MARKDOWN + end + + it 'renders both public and private functions correctly' do + expect(YARD::Parser::SourceParser.parse_string(<<~'PUPPET', :puppet).enumerator.length).to eq(2) + # @return void + function func_public() {} + + # @return void + # @api private + function func_private() {} + PUPPET + + expect(described_class.generate).to eq(<<~'MARKDOWN') + # Reference + + + + ## Table of Contents + + ### Functions + + #### Public Functions + + * [`func_public`](#func_public) + + #### Private Functions + + * `func_private` + + ## Functions + + ### `func_public` + + Type: Puppet Language + + The func_public function. + + #### `func_public()` + + The func_public function. + + Returns: `Any` void + + MARKDOWN + end end