Skip to content

Commit 7dff756

Browse files
committed
Merge pull request #156 from eputnam/pdoc184
(PDOC-184) generate markdown
2 parents 840dfca + cc7ffae commit 7dff756

31 files changed

Lines changed: 1659 additions & 32 deletions

README.md

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,17 +162,33 @@ Strings can produce documentation in JSON and then either generate a `.json` fil
162162
To generate JSON documentation to a file, run:
163163

164164
```
165-
$ puppet strings generate --emit-json documentation.json
165+
$ puppet strings generate --format json --out documentation.json
166166
```
167167

168168
To generate and then print JSON documentation to stdout, run:
169169

170170
```
171-
$ puppet strings generate --emit-json-stdout
171+
$ puppet strings generate --format json
172172
```
173173

174174
For details about Strings JSON output, see [Strings JSON schema](https://github.com/puppetlabs/puppet-strings/blob/master/JSON.md).
175175

176+
### Output documents in Markdown
177+
178+
Strings can also produce documentation in Markdown and then either generate an `.md` file or print Markdown to stdout. The generated markdown layout has been reviewed and approved by Puppet's tech pubs team and is the same that is used in Puppet Supported modules.
179+
180+
To generate REFERENCE.md:
181+
182+
```
183+
$ puppet strings generate --format markdown
184+
```
185+
186+
To write Markdown documentation to another file, use the --out option:
187+
188+
```
189+
$ puppet strings generate --format markdown --out docs/INFO.md
190+
```
191+
176192
### Output documents to GitHub Pages
177193

178194
To generate documents and then make them available on [GitHub Pages](https://pages.github.com/), use the Strings rake task `strings:gh_pages:update`. See [Rake tasks](#rake-tasks) for setup and usage details.
@@ -285,6 +301,35 @@ end
285301

286302
All provider method calls, including `confine`, `defaultfor`, and `commands`, are automatically parsed and documented by Strings. The `desc` method is used to generate the docstring, and can include tags such as `@example` if written as a heredoc.
287303

304+
Document types that use the new [Resource API](https://github.com/puppetlabs/puppet-resource_api):
305+
306+
```ruby
307+
Puppet::ResourceApi.register_type(
308+
name: 'database',
309+
docs: 'An example database server resource type.',
310+
attributes: {
311+
ensure: {
312+
type: 'Enum[present, absent, up, down]',
313+
desc: 'What state the database should be in.',
314+
default: 'up',
315+
},
316+
address: {
317+
type: 'String',
318+
desc: 'The database server name.',
319+
behaviour: :namevar,
320+
},
321+
encrypt: {
322+
type: 'Boolean',
323+
desc: 'Whether or not to encrypt the database.',
324+
default: false,
325+
behaviour: :parameter,
326+
},
327+
},
328+
)
329+
```
330+
331+
Here, the `docs` key acts like the `desc` method of the traditional resource type. Everything else is the same, except that now everything is a value in the data structure, not passed to methods.
332+
288333
**Note**: Puppet Strings can not evaluate your Ruby code, so only certain static expressions are supported.
289334

290335
### Documenting functions

lib/puppet-strings.rb

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ module PuppetStrings
1414
# @option options [Boolean] :debug Enable YARD debug output.
1515
# @option options [Boolean] :backtrace Enable YARD backtraces.
1616
# @option options [String] :markup The YARD markup format to use (defaults to 'markdown').
17-
# @option options [String] :json Enables JSON output to the given file. If the file is nil, STDOUT is used.
17+
# @option options [String] :path Write the selected format to a file path
18+
# @option options [Boolean] :markdown From the --format option, is the format Markdown?
19+
# @option options [Boolean] :json Is the format JSON?
1820
# @option options [Array<String>] :yard_args The arguments to pass to yard.
1921
# @return [void]
2022
def self.generate(search_patterns = DEFAULT_SEARCH_PATTERNS, options = {})
@@ -27,15 +29,18 @@ def self.generate(search_patterns = DEFAULT_SEARCH_PATTERNS, options = {})
2729
args << '--backtrace' if options[:backtrace]
2830
args << "-m#{options[:markup] || 'markdown'}"
2931

30-
render_as_json = options.key? :json
31-
json_file = nil
32-
if render_as_json
33-
json_file = options[:json]
32+
file = nil
33+
if options[:json] || options[:markdown]
34+
file = if options[:json]
35+
options[:path]
36+
elsif options[:markdown]
37+
options[:path] || "REFERENCE.md"
38+
end
3439
# Disable output and prevent stats/progress when writing to STDOUT
3540
args << '-n'
36-
args << '-q' unless json_file
37-
args << '--no-stats' unless json_file
38-
args << '--no-progress' unless json_file
41+
args << '-q' unless file
42+
args << '--no-stats' unless file
43+
args << '--no-progress' unless file
3944
end
4045

4146
yard_args = options[:yard_args]
@@ -46,10 +51,24 @@ def self.generate(search_patterns = DEFAULT_SEARCH_PATTERNS, options = {})
4651
YARD::CLI::Yardoc.run(*args)
4752

4853
# If outputting JSON, render the output
49-
if render_as_json
50-
require 'puppet-strings/json'
51-
PuppetStrings::Json.render(json_file)
54+
if options[:json]
55+
render_json(file)
5256
end
57+
58+
# If outputting Markdown, render the output
59+
if options[:markdown]
60+
render_markdown(file)
61+
end
62+
end
63+
64+
def self.render_json(path)
65+
require 'puppet-strings/json'
66+
PuppetStrings::Json.render(path)
67+
end
68+
69+
def self.render_markdown(path)
70+
require 'puppet-strings/markdown'
71+
PuppetStrings::Markdown.render(path)
5372
end
5473

5574
# Runs the YARD documentation server.

lib/puppet-strings/json.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ def self.tags_to_hashes(tags)
3434
next t.to_hash if t.respond_to?(:to_hash)
3535

3636
tag = { tag_name: t.tag_name }
37+
# grab nested information for @option tags
38+
if tag[:tag_name] == 'option'
39+
tag[:opt_name] = t.pair.name
40+
tag[:opt_text] = t.pair.text
41+
tag[:opt_types] = t.pair.types
42+
tag[:parent] = t.name
43+
end
3744
tag[:text] = t.text if t.text
3845
tag[:types] = t.types if t.types
3946
tag[:name] = t.name if t.name

lib/puppet-strings/markdown.rb

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
require 'puppet-strings/json'
2+
3+
# module for parsing Yard Registries and generating markdown
4+
module PuppetStrings::Markdown
5+
require_relative 'markdown/puppet_classes'
6+
require_relative 'markdown/functions'
7+
require_relative 'markdown/defined_types'
8+
require_relative 'markdown/resource_types'
9+
require_relative 'markdown/table_of_contents'
10+
11+
# generates markdown documentation
12+
# @return [String] markdown doc
13+
def self.generate
14+
final = "# Reference\n\n"
15+
final << PuppetStrings::Markdown::TableOfContents.render
16+
final << PuppetStrings::Markdown::PuppetClasses.render
17+
final << PuppetStrings::Markdown::DefinedTypes.render
18+
final << PuppetStrings::Markdown::ResourceTypes.render
19+
final << PuppetStrings::Markdown::Functions.render
20+
21+
final
22+
end
23+
24+
# mimicks the behavior of the json render, although path will never be nil
25+
# @param [String] path path to destination file
26+
def self.render(path = nil)
27+
if path.nil?
28+
puts generate
29+
exit
30+
else
31+
File.open(path, 'w') { |file| file.write(generate) }
32+
YARD::Logger.instance.debug "Wrote markdown to #{path}"
33+
end
34+
end
35+
end
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
require 'puppet-strings'
2+
require 'puppet-strings/json'
3+
require 'puppet-strings/yard'
4+
5+
module PuppetStrings::Markdown
6+
# This class makes elements in a YARD::Registry hash easily accessible for templates.
7+
#
8+
# Here's an example hash:
9+
#{:name=>:klass,
10+
# :file=>"(stdin)",
11+
# :line=>16,
12+
# :inherits=>"foo::bar",
13+
# :docstring=>
14+
# {:text=>"An overview for a simple class.",
15+
# :tags=>
16+
# [{:tag_name=>"summary", :text=>"A simple class."},
17+
# {:tag_name=>"since", :text=>"1.0.0"},
18+
# {:tag_name=>"see", :name=>"www.puppet.com"},
19+
# {:tag_name=>"example",
20+
# :text=>
21+
# "class { 'klass':\n" +
22+
# " param1 => 1,\n" +
23+
# " param3 => 'foo',\n" +
24+
# "}",
25+
# :name=>"This is an example"},
26+
# {:tag_name=>"author", :text=>"eputnam"},
27+
# {:tag_name=>"option", :name=>"opts"},
28+
# {:tag_name=>"raise", :text=>"SomeError"},
29+
# {:tag_name=>"param",
30+
# :text=>"First param.",
31+
# :types=>["Integer"],
32+
# :name=>"param1"},
33+
# {:tag_name=>"param",
34+
# :text=>"Second param.",
35+
# :types=>["Any"],
36+
# :name=>"param2"},
37+
# {:tag_name=>"param",
38+
# :text=>"Third param.",
39+
# :types=>["String"],
40+
# :name=>"param3"}]},
41+
# :defaults=>{"param1"=>"1", "param2"=>"undef", "param3"=>"'hi'"},
42+
# :source=>
43+
# "class klass (\n" +
44+
# " Integer $param1 = 1,\n" +
45+
# " $param2 = undef,\n" +
46+
# " String $param3 = 'hi'\n" +
47+
# ") inherits foo::bar {\n" +
48+
# "}"}
49+
class Base
50+
def initialize(registry, component_type)
51+
@type = component_type
52+
@registry = registry
53+
@tags = registry[:docstring][:tags] || []
54+
end
55+
56+
# generate 1:1 tag methods
57+
# e.g. {:tag_name=>"author", :text=>"eputnam"}
58+
{ :return_val => 'return',
59+
:since => 'since',
60+
:summary => 'summary' }.each do |method_name, tag_name|
61+
# @return [String] unless the tag is nil or the string.length == 0
62+
define_method method_name do
63+
@tags.select { |tag| tag[:tag_name] == "#{tag_name}" }[0][:text] unless @tags.select { |tag| tag[:tag_name] == "#{tag_name}" }[0].nil? || @tags.select { |tag| tag[:tag_name] == "#{tag_name}" }[0][:text].length.zero?
64+
end
65+
end
66+
67+
# @return [String] top-level name
68+
def name
69+
@registry[:name].to_s unless @registry[:name].nil?
70+
end
71+
72+
# @return [String] 'Overview' text (untagged text)
73+
def text
74+
@registry[:docstring][:text] unless @registry[:docstring][:text].empty?
75+
end
76+
77+
# @return [String] data type of return value
78+
def return_type
79+
@tags.select { |tag| tag[:tag_name] == 'return' }[0][:types][0] unless @tags.select { |tag| tag[:tag_name] == 'return' }[0].nil?
80+
end
81+
82+
# @return [String] text from @since tag
83+
def since
84+
@tags.select { |tag| tag[:tag_name] == 'since' }[0][:text] unless @tags.select { |tag| tag[:tag_name] == 'since' }[0].nil?
85+
end
86+
87+
# @return [Array] @see tag hashes
88+
def see
89+
@tags.select { |tag| tag[:tag_name] == 'see' } unless @tags.select { |tag| tag[:tag_name] == 'see' }[0].nil?
90+
end
91+
92+
# @return [Array] parameter tag hashes
93+
def params
94+
@tags.select { |tag| tag[:tag_name] == 'param' } unless @tags.select { |tag| tag[:tag_name] == 'param' }[0].nil?
95+
end
96+
97+
# @return [Array] example tag hashes
98+
def examples
99+
@tags.select { |tag| tag[:tag_name] == 'example' } unless @tags.select { |tag| tag[:tag_name] == 'example' }[0].nil?
100+
end
101+
102+
# @return [Array] raise tag hashes
103+
def raises
104+
@tags.select { |tag| tag[:tag_name] == 'raise' } unless @tags.select { |tag| tag[:tag_name] == 'raise' }[0].nil?
105+
end
106+
107+
# @return [Array] option tag hashes
108+
def options
109+
@tags.select { |tag| tag[:tag_name] == 'option' } unless @tags.select { |tag| tag[:tag_name] == 'option' }[0].nil?
110+
end
111+
112+
# @param parameter_name
113+
# parameter name to match to option tags
114+
# @return [Array] option tag hashes that have a parent parameter_name
115+
def options_for_param(parameter_name)
116+
opts_for_p = options.select { |o| o[:parent] == parameter_name } unless options.nil?
117+
opts_for_p unless opts_for_p.nil? || opts_for_p.length.zero?
118+
end
119+
120+
# @return [Array] any defaults found for the component
121+
def defaults
122+
@registry[:defaults] unless @registry[:defaults].nil?
123+
end
124+
125+
# @return [Hash] information needed for the table of contents
126+
def toc_info
127+
{
128+
name: name.to_s,
129+
link: link,
130+
desc: summary || @registry[:docstring][:text].gsub("\n", ". ")
131+
}
132+
end
133+
134+
# @return [String] makes the component name suitable for a GitHub markdown link
135+
def link
136+
name.delete('::').strip.gsub(' ','-').downcase
137+
end
138+
139+
# Some return, default, or valid values need to be in backticks. Instead of fu in the handler or code_object, this just does the change on the front.
140+
# @param value
141+
# any string
142+
# @return [String] value or value in backticks if it is in a list
143+
def value_string(value)
144+
to_symbol = %w[undef true false]
145+
if to_symbol.include? value
146+
return "`#{value}`"
147+
else
148+
return value
149+
end
150+
end
151+
152+
# @return [String] full markdown rendering of a component
153+
def render(template)
154+
file = File.join(File.dirname(__FILE__),"templates/#{template}")
155+
ERB.new(File.read(file), nil, '-').result(binding)
156+
end
157+
end
158+
end
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
require 'puppet-strings/markdown/base'
2+
3+
module PuppetStrings::Markdown
4+
class DefinedType < Base
5+
def initialize(registry)
6+
@template = 'classes_and_defines.erb'
7+
super(registry, 'defined type')
8+
end
9+
10+
def render
11+
super(@template)
12+
end
13+
end
14+
end
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
require_relative 'defined_type'
2+
3+
module PuppetStrings::Markdown
4+
module DefinedTypes
5+
6+
# @return [Array] list of defined types
7+
def self.in_dtypes
8+
YARD::Registry.all(:puppet_defined_type).sort_by!(&:name).map!(&:to_hash)
9+
end
10+
11+
def self.render
12+
final = in_dtypes.length > 0 ? "## Defined types\n\n" : ""
13+
in_dtypes.each do |type|
14+
final << PuppetStrings::Markdown::DefinedType.new(type).render
15+
end
16+
final
17+
end
18+
19+
def self.toc_info
20+
final = []
21+
22+
in_dtypes.each do |type|
23+
final.push(PuppetStrings::Markdown::DefinedType.new(type).toc_info)
24+
end
25+
26+
final
27+
end
28+
end
29+
end

0 commit comments

Comments
 (0)