Class: Ronn::Document

Inherits:
Object
  • Object
show all
Includes:
Utils
Defined in:
lib/ronn/document.rb

Overview

The Document class can be used to load and inspect a ronn document and to convert a ronn document into other formats, like roff or HTML.

Ronn files may optionally follow the naming convention: “<name>.<section>.ronn”. The <name> and <section> are used in generated documentation unless overridden by the information extracted from the document’s name section.

Constant Summary

Constants included from Utils

Utils::HTML, Utils::HTML_BLOCK, Utils::HTML_EMPTY, Utils::HTML_INLINE

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Utils

#block_element?, #child_of?, #empty_element?, #html_element?, #inline_element?

Constructor Details

#initialize(path = nil, attributes = {}, &block) ⇒ Document

Create a Ronn::Document given a path or with the data returned by calling the block. The document is loaded and preprocessed before the intialize method returns. The attributes hash may contain values for any writeable attributes defined on this class.



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/ronn/document.rb', line 71

def initialize(path = nil, attributes = {}, &block)
  @path = path
  @basename = path.to_s =~ /^-?$/ ? nil : File.basename(path)
  @reader = block ||
            lambda do |f|
              if ['-', nil].include?(f)
                $stdin.read
              else
                File.read(f, encoding: @encoding)
              end
            end
  @data = @reader.call(path)
  @name, @section, @tagline = sniff

  @styles = %w[man]
  @manual, @organization, @date = nil
  @markdown, @input_html, @html = nil
  @index = Ronn::Index[path || '.']
  @index.add_manual(self) if path && name

  attributes.each { |attr_name, value| send("#{attr_name}=", value) }
end

Instance Attribute Details

#dataObject (readonly)

The raw input data, read from path or stream and unmodified.



30
31
32
# File 'lib/ronn/document.rb', line 30

def data
  @data
end

#dateObject

The date the man page was published. If not set explicitly, it first checks for “SOURCE_DATE_EPOCH” to support reproducible builds, then the file’s modified time or, if no file is given, the current time. Center displayed in the document footer.



185
186
187
188
189
190
191
192
193
# File 'lib/ronn/document.rb', line 185

def date
  return @date if @date

  return Time.at(ENV['SOURCE_DATE_EPOCH'].to_i).gmtime if ENV['SOURCE_DATE_EPOCH']

  return File.mtime(path) if File.exist?(path)

  Time.now
end

#encodingObject

Encoding that the Ronn document is in



27
28
29
# File 'lib/ronn/document.rb', line 27

def encoding
  @encoding
end

#indexObject

The index used to resolve man and file references.



33
34
35
# File 'lib/ronn/document.rb', line 33

def index
  @index
end

#manualObject

The manual this document belongs to; center displayed in the header.



51
52
53
# File 'lib/ronn/document.rb', line 51

def manual
  @manual
end

#nameObject

Returns the manual page name based first on the document’s contents and then on the path name. Usually a single word name of a program or filename; displayed along with the section in the left and right portions of the header as well as the bottom right section of the footer.



140
141
142
# File 'lib/ronn/document.rb', line 140

def name
  @name || path_name
end

#organizationObject

The name of the group, organization, or individual responsible for this document; displayed in the left portion of the footer.



55
56
57
# File 'lib/ronn/document.rb', line 55

def organization
  @organization
end

#outdirObject

Output directory to write files to.



65
66
67
# File 'lib/ronn/document.rb', line 65

def outdir
  @outdir
end

#pathObject (readonly)

Path to the Ronn document. This may be ‘-’ or nil when the Ronn::Document object is created with a stream, in which case stdin will be read.



24
25
26
# File 'lib/ronn/document.rb', line 24

def path
  @path
end

#sectionObject

Returns the manual page section based first on the document’s contents and then on the path name. A string whose first character is numeric; displayed in parenthesis along with the name.



153
154
155
# File 'lib/ronn/document.rb', line 153

def section
  @section || path_section
end

#stylesObject

Array of style modules to apply to the document.



62
63
64
# File 'lib/ronn/document.rb', line 62

def styles
  @styles
end

#taglineObject

Single sentence description of the thing being described by this man page; displayed in the NAME section.



47
48
49
# File 'lib/ronn/document.rb', line 47

def tagline
  @tagline
end

Instance Method Details

#basename(type = nil) ⇒ Object

Generate a file basename of the form “<name>.<section>.<type>” for the given file extension. Uses the name and section from the source file path but falls back on the name and section defined in the document.



98
99
100
101
102
# File 'lib/ronn/document.rb', line 98

def basename(type = nil)
  type = nil if ['', 'roff'].include?(type.to_s)
  [path_name || @name, path_section || @section, type]
    .compact.join('.')
end

#convert(format) ⇒ Object

Convert the document to :roff, :html, or :html_fragment and return the result as a string.



245
246
247
# File 'lib/ronn/document.rb', line 245

def convert(format)
  send "to_#{format}"
end

#htmlObject

A Nokogiri DocumentFragment for the manual content fragment.



239
240
241
# File 'lib/ronn/document.rb', line 239

def html
  @html ||= process_html!
end

#markdownObject

Preprocessed markdown input text.



234
235
236
# File 'lib/ronn/document.rb', line 234

def markdown
  @markdown ||= process_markdown!
end

#name?Boolean

Truthful when the name was extracted from the name section of the document.

Returns:

  • (Boolean)


146
147
148
# File 'lib/ronn/document.rb', line 146

def name?
  !@name.nil?
end

#path_for(type = nil) ⇒ Object

Construct a path for a file near the source file. Uses the Document#basename method to generate the basename part and appends it to the dirname of the source document.



107
108
109
110
111
112
113
114
115
# File 'lib/ronn/document.rb', line 107

def path_for(type = nil)
  if @outdir
    File.join(@outdir, basename(type))
  elsif @basename
    File.join(File.dirname(path), basename(type))
  else
    basename(type)
  end
end

#path_nameObject

Returns the <name> part of the path, or nil when no path is available. This is used as the manual page name when the file contents do not include a name section.



120
121
122
123
124
125
126
127
# File 'lib/ronn/document.rb', line 120

def path_name
  return unless @basename

  parts = @basename.split('.')
  parts.pop if parts.length > 1 && parts.last =~ /^\w+$/
  parts.pop if parts.last =~ /^\d+$/
  parts.join('.')
end

#path_sectionObject

Returns the <section> part of the path, or nil when no path is available.



131
132
133
# File 'lib/ronn/document.rb', line 131

def path_section
  $1 if @basename.to_s =~ /\.(\d\w*)\./
end

#reference_nameObject

The name used to reference this manual.



164
165
166
# File 'lib/ronn/document.rb', line 164

def reference_name
  name + (section && "(#{section})").to_s
end

#section?Boolean

True when the section number was extracted from the name section of the document.

Returns:

  • (Boolean)


159
160
161
# File 'lib/ronn/document.rb', line 159

def section?
  !@section.nil?
end

#sniffObject

Sniff the document header and extract basic document metadata. Return a tuple of the form: [name, section, description], where missing information is represented by nil and any element may be missing.



213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/ronn/document.rb', line 213

def sniff
  html = Kramdown::Document.new(data[0, 512], auto_ids: false,
    smart_quotes: ['apos', 'apos', 'quot', 'quot'],
    typographic_symbols: { hellip: '...', ndash: '--', mdash: '--' }).to_html
  heading, html = html.split("</h1>\n", 2)
  return [nil, nil, nil] if html.nil?

  case heading
  when /([\w_.\[\]~+=@:-]+)\s*\((\d\w*)\)\s*-+\s*(.*)/
    # name(section) -- description
    [$1, $2, $3]
  when /([\w_.\[\]~+=@:-]+)\s+-+\s+(.*)/
    # name -- description
    [$1, nil, $2]
  else
    # description
    [nil, nil, heading.sub('<h1>', '')]
  end
end

#titleObject

The document’s title when no name section was defined. When a name section exists, this value is nil.



177
178
179
# File 'lib/ronn/document.rb', line 177

def title
  @tagline unless name?
end

#title?Boolean

Truthful when the document started with an h1 but did not follow the “<name>(<sect>) – <tagline>” convention. We assume this is some kind of custom title.

Returns:

  • (Boolean)


171
172
173
# File 'lib/ronn/document.rb', line 171

def title?
  !name? && tagline
end

#to_hObject



290
291
292
293
# File 'lib/ronn/document.rb', line 290

def to_h
  %w[name section tagline manual organization date styles toc]
    .each_with_object({}) { |name, hash| hash[name] = send(name) }
end

#to_htmlObject

Convert the document to HTML and return the result as a string. The returned string is a complete HTML document.



260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/ronn/document.rb', line 260

def to_html
  layout = ENV.fetch('RONN_LAYOUT', nil)
  layout_path = nil
  if layout
    layout_path = File.expand_path(layout)
    unless File.exist?(layout_path)
      warn "warn: can't find #{layout}, using default layout."
      layout_path = nil
    end
  end

  template = Ronn::Template.new(self)
  template.context.push html: to_html_fragment(nil)
  template.render(layout_path || 'default')
end

#to_html_fragment(wrap_class = 'mp') ⇒ Object

Convert the document to HTML and return the result as a string. The HTML does not include <html>, <head>, or <style> tags.



279
280
281
282
283
284
# File 'lib/ronn/document.rb', line 279

def to_html_fragment(wrap_class = 'mp')
  frag_nodes = html.at('body').children
  out = frag_nodes.to_s.rstrip
  out = "<div class='#{wrap_class}'>#{out}\n</div>" unless wrap_class.nil?
  out
end

#to_json(*_args) ⇒ Object



300
301
302
303
# File 'lib/ronn/document.rb', line 300

def to_json(*_args)
  require 'json'
  to_h.merge('date' => date.iso8601).to_json
end

#to_markdownObject



286
287
288
# File 'lib/ronn/document.rb', line 286

def to_markdown
  markdown
end

#to_roffObject

Convert the document to roff and return the result as a string.



250
251
252
253
254
255
256
# File 'lib/ronn/document.rb', line 250

def to_roff
  RoffFilter.new(
    to_html_fragment(nil),
    name, section, tagline,
    manual, organization, date
  ).to_s
end

#to_yamlObject



295
296
297
298
# File 'lib/ronn/document.rb', line 295

def to_yaml
  require 'yaml'
  to_h.to_yaml
end

#tocObject Also known as: section_heads

Retrieve a list of top-level section headings in the document and return as an array of [id, text] tuples, where id is the element’s generated id and text is the inner text of the heading element.



198
199
200
201
# File 'lib/ronn/document.rb', line 198

def toc
  @toc ||=
    html.search('h2[@id]').map { |h2| [h2.attributes['id'].content.upcase, h2.inner_text] }
end