ron 0.1 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README +10 -6
- data/Rakefile +7 -0
- data/bin/ron +46 -17
- data/lib/ron.rb +5 -4
- data/lib/ron/document.rb +221 -49
- data/lib/ron/layout.html +53 -22
- data/lib/ron/roff.rb +26 -5
- data/man/ron.1.ron +44 -21
- data/man/ron.5.ron +5 -3
- data/man/ron.7.ron +149 -0
- data/ron.gemspec +13 -3
- data/test/angle_bracket_syntax.html +12 -0
- data/test/angle_bracket_syntax.ron +12 -0
- data/test/basic_document.html +3 -0
- data/test/{simple.ron → basic_document.ron} +2 -0
- data/test/custom_title_document.html +3 -0
- data/test/custom_title_document.ron +5 -0
- data/test/definition_list_syntax.html +21 -0
- data/test/definition_list_syntax.ron +18 -0
- data/test/document_test.rb +64 -11
- data/test/ron_test.rb +30 -5
- data/test/titleless_document.html +2 -0
- data/test/titleless_document.ron +2 -0
- metadata +13 -3
data/README
CHANGED
@@ -1,4 +1,8 @@
|
|
1
|
-
|
1
|
+
ron(7) -- the opposite of roff
|
2
|
+
==============================
|
3
|
+
|
4
|
+
DESCRIPTION
|
5
|
+
-----------
|
2
6
|
|
3
7
|
Ron is a humane text format and toolchain for
|
4
8
|
creating UNIX man pages -- and things that
|
@@ -19,17 +23,17 @@ format in more detail.
|
|
19
23
|
INSTALL
|
20
24
|
-------
|
21
25
|
|
22
|
-
|
23
|
-
rubygems:
|
26
|
+
Ron can be installed using rubygems:
|
24
27
|
|
25
|
-
$ [sudo] gem install ron
|
28
|
+
$ [sudo] gem install ron
|
26
29
|
|
27
30
|
Or, clone the git repository and install from
|
28
31
|
source:
|
29
32
|
|
30
33
|
$ git clone git://github.com/rtomayko/ron.git
|
31
34
|
$ cd ron
|
32
|
-
$ rake
|
35
|
+
$ rake package
|
36
|
+
$ [sudo] rake install
|
33
37
|
|
34
38
|
EXAMPLES
|
35
39
|
--------
|
@@ -47,7 +51,7 @@ BASIC USAGE
|
|
47
51
|
-----------
|
48
52
|
|
49
53
|
To generate a roff man page from the included
|
50
|
-
markdown.5.ron file and open it in man(1):
|
54
|
+
`markdown.5.ron` file and open it in man(1):
|
51
55
|
|
52
56
|
$ ron -b man/markdown.5.ron
|
53
57
|
building: man/markdown.5
|
data/Rakefile
CHANGED
@@ -11,6 +11,13 @@ Rake::TestTask.new(:test) do |t|
|
|
11
11
|
t.ruby_opts = ['-rubygems'] if defined? Gem
|
12
12
|
end
|
13
13
|
|
14
|
+
# DOCS =================================================================
|
15
|
+
|
16
|
+
desc 'Build the manual'
|
17
|
+
task 'man' do
|
18
|
+
sh "ron -br5 --manual='Ron Manual' --organization='Ryan Tomayko' man/*.ron"
|
19
|
+
end
|
20
|
+
|
14
21
|
# PACKAGING ============================================================
|
15
22
|
|
16
23
|
require 'rubygems/specification'
|
data/bin/ron
CHANGED
@@ -1,24 +1,37 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
## Usage: ron [ OPTIONS ] [ FILE
|
2
|
+
## Usage: ron [ OPTIONS ] [ FILE ]
|
3
3
|
## ron --build FILE ...
|
4
4
|
## ron --install FILE ...
|
5
|
-
## ron --man FILE
|
6
|
-
## Convert ron
|
5
|
+
## ron --man FILE ...
|
6
|
+
## Convert ron FILE to roff man page or HTML and write to standard
|
7
|
+
## output. With no FILE, ron reads from standard input. The build,
|
8
|
+
## install, and man forms accept multiple FILE arguments.
|
7
9
|
##
|
8
|
-
##
|
9
|
-
##
|
10
|
-
## -
|
11
|
-
## -
|
10
|
+
## Modes:
|
11
|
+
## --pipe write to standard output (default behavior)
|
12
|
+
## -b, --build write to files instead of standard output
|
13
|
+
## -i, --install write to file in MAN_HOME or system man path
|
14
|
+
## -m, --man open man page like man(1)
|
12
15
|
##
|
13
|
-
##
|
14
|
-
##
|
16
|
+
## Formats:
|
17
|
+
## --roff generate roff/man text; this is the default behavior
|
18
|
+
## -5, --html generate entire HTML page with layout
|
19
|
+
## -f, --fragment generate HTML fragment instead of entire HTML page
|
15
20
|
##
|
16
|
-
##
|
21
|
+
## Document attributes:
|
22
|
+
## --date=DATE published date in YYYY-MM-DD format;
|
23
|
+
## displayed bottom-center in footer
|
24
|
+
## --manual=NAME name of the manual this document belongs to;
|
25
|
+
## displayed top-center in header
|
26
|
+
## --organization=NAME publishing group, organization, or individual;
|
27
|
+
## displayed bottom-left in footer
|
28
|
+
##
|
29
|
+
## --help show this help message
|
17
30
|
##
|
18
|
-
## See the ron(2)
|
19
31
|
require 'optparse'
|
20
32
|
|
21
33
|
formats = []
|
34
|
+
options = {}
|
22
35
|
build = false
|
23
36
|
install = false
|
24
37
|
man = false
|
@@ -38,12 +51,22 @@ end
|
|
38
51
|
|
39
52
|
# parse command line options
|
40
53
|
ARGV.options do |option|
|
41
|
-
|
42
|
-
option.on("
|
54
|
+
# modes
|
55
|
+
option.on("--pipe") { }
|
43
56
|
option.on("-b", "--build") { build = true }
|
44
57
|
option.on("-i", "--install") { install = true }
|
45
58
|
option.on("-m", "--man") { man = true }
|
46
59
|
|
60
|
+
# format options
|
61
|
+
option.on("-r", "--roff") { formats << 'roff' }
|
62
|
+
option.on("-5", "--html") { formats << 'html' }
|
63
|
+
option.on("-f", "--fragment") { formats << 'html_fragment' }
|
64
|
+
|
65
|
+
# manual attribute options
|
66
|
+
[:name, :section, :manual, :organization, :date].each do |option_attr|
|
67
|
+
option.on("--#{option_attr}=VALUE") { |val| options[option_attr] = val }
|
68
|
+
end
|
69
|
+
|
47
70
|
option.on_tail("--help") { usage ; exit }
|
48
71
|
option.parse!
|
49
72
|
end
|
@@ -55,13 +78,19 @@ elsif ARGV.empty?
|
|
55
78
|
ARGV.push '-'
|
56
79
|
end
|
57
80
|
|
81
|
+
# turn the --date arg into
|
82
|
+
if options[:date]
|
83
|
+
options[:date] = Date.strptime(options[:date], '%Y-%m-%d')
|
84
|
+
end
|
85
|
+
|
58
86
|
formats = ['roff'] if formats.empty?
|
87
|
+
formats.delete('html') if formats.include?('html_fragment')
|
59
88
|
pid = nil
|
60
89
|
|
61
90
|
require 'ron'
|
62
91
|
wr = STDOUT
|
63
92
|
ARGV.each do |file|
|
64
|
-
doc = Ron.new(file) { file == '-' ? STDIN.read : File.read(file) }
|
93
|
+
doc = Ron.new(file, options) { file == '-' ? STDIN.read : File.read(file) }
|
65
94
|
|
66
95
|
# setup the man pipeline if the --man option was specified
|
67
96
|
if man && !build
|
@@ -79,11 +108,11 @@ ARGV.each do |file|
|
|
79
108
|
formats.each do |format|
|
80
109
|
output = doc.convert(format)
|
81
110
|
if build
|
82
|
-
path = doc.
|
111
|
+
path = doc.path_for(format)
|
83
112
|
info "building: #{path}"
|
84
|
-
File.open(path, 'wb') { |f| f.
|
113
|
+
File.open(path, 'wb') { |f| f.puts(output) }
|
85
114
|
else
|
86
|
-
wr.
|
115
|
+
wr.puts(output)
|
87
116
|
end
|
88
117
|
end
|
89
118
|
|
data/lib/ron.rb
CHANGED
@@ -3,13 +3,14 @@
|
|
3
3
|
# install standard UNIX roff(7) formatted manpages or to generate
|
4
4
|
# beautiful HTML manpages.
|
5
5
|
module Ron
|
6
|
-
VERSION = '0.
|
6
|
+
VERSION = '0.2'
|
7
7
|
|
8
8
|
require 'ron/document'
|
9
9
|
require 'ron/roff'
|
10
10
|
|
11
|
-
# Create a new Ron::Document for the given ron file.
|
12
|
-
|
13
|
-
|
11
|
+
# Create a new Ron::Document for the given ron file. See
|
12
|
+
# Ron::Document.new for usage information.
|
13
|
+
def self.new(filename, attributes={}, &block)
|
14
|
+
Document.new(filename, attributes, &block)
|
14
15
|
end
|
15
16
|
end
|
data/lib/ron/document.rb
CHANGED
@@ -1,60 +1,178 @@
|
|
1
|
+
require 'set'
|
1
2
|
require 'nokogiri'
|
2
3
|
require 'rdiscount'
|
3
4
|
require 'ron/roff'
|
4
5
|
|
5
6
|
module Ron
|
7
|
+
# The Document class can be used to load and inspect a ron document
|
8
|
+
# and to convert a ron document into other formats, like roff or
|
9
|
+
# HTML.
|
10
|
+
#
|
11
|
+
# Ron files may optionally follow the naming convention:
|
12
|
+
# "<name>.<section>.ron". The <name> and <section> are used in
|
13
|
+
# generated documentation unless overridden by the information
|
14
|
+
# extracted from the document's name section.
|
6
15
|
class Document
|
7
|
-
|
8
|
-
attr_reader :filename, :data, :basename, :name, :section, :tagline
|
16
|
+
attr_reader :path, :data
|
9
17
|
|
10
|
-
|
11
|
-
|
18
|
+
# The man pages name: usually a single word name of
|
19
|
+
# a program or filename; displayed along with the section in
|
20
|
+
# the left and right portions of the header as well as the bottom
|
21
|
+
# right section of the footer.
|
22
|
+
attr_accessor :name
|
23
|
+
|
24
|
+
# The man page's section: a string whose first character
|
25
|
+
# is numeric; displayed in parenthesis along with the name.
|
26
|
+
attr_accessor :section
|
27
|
+
|
28
|
+
# Single sentence description of the thing being described
|
29
|
+
# by this man page; displayed in the NAME section.
|
30
|
+
attr_accessor :tagline
|
31
|
+
|
32
|
+
# The manual this document belongs to; center displayed in
|
33
|
+
# the header.
|
34
|
+
attr_accessor :manual
|
35
|
+
|
36
|
+
# The name of the group, organization, or individual responsible
|
37
|
+
# for this document; displayed in the left portion of the footer.
|
38
|
+
attr_accessor :organization
|
39
|
+
|
40
|
+
# The date the document was published; center displayed in
|
41
|
+
# the document footer.
|
42
|
+
attr_accessor :date
|
43
|
+
|
44
|
+
# Create a Ron::Document given a path or with the data returned by
|
45
|
+
# calling the block. The document is loaded and preprocessed before
|
46
|
+
# the intialize method returns. The attributes hash may contain values
|
47
|
+
# for any writeable attributes defined on this class.
|
48
|
+
def initialize(path=nil, attributes={}, &block)
|
49
|
+
@path = path
|
50
|
+
@basename = path.to_s =~ /^-?$/ ? nil : File.basename(path)
|
12
51
|
@reader = block || Proc.new { |f| File.read(f) }
|
13
|
-
@data = @reader.call(
|
52
|
+
@data = @reader.call(path)
|
53
|
+
@name, @section, @tagline = nil
|
54
|
+
@manual, @organization, @date = nil
|
55
|
+
@fragment = preprocess
|
56
|
+
attributes.each { |attr_name,value| send("#{attr_name}=", value) }
|
57
|
+
end
|
14
58
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
59
|
+
# Generate a file basename of the form "<name>.<section>.<type>"
|
60
|
+
# for the given file extension. Uses the name and section from
|
61
|
+
# the source file path but falls back on the name and section
|
62
|
+
# defined in the document.
|
63
|
+
def basename(type=nil)
|
64
|
+
type = nil if ['', 'roff'].include?(type.to_s)
|
65
|
+
[path_name || @name, path_section || @section, type].
|
66
|
+
compact.join('.')
|
67
|
+
end
|
68
|
+
|
69
|
+
# Construct a path for a file near the source file. Uses the
|
70
|
+
# Document#basename method to generate the basename part and
|
71
|
+
# appends it to the dirname of the source document.
|
72
|
+
def path_for(type=nil)
|
73
|
+
if @basename
|
74
|
+
File.join(File.dirname(path), basename(type))
|
75
|
+
else
|
76
|
+
basename(type)
|
77
|
+
end
|
22
78
|
end
|
23
79
|
|
24
|
-
#
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
File.join(File.dirname(filename), name)
|
80
|
+
# Returns the <name> part of the path, or nil when no path is
|
81
|
+
# available. This is used as the manual page name when the
|
82
|
+
# file contents do not include a name section.
|
83
|
+
def path_name
|
84
|
+
@basename[/^[^.]+/] if @basename
|
30
85
|
end
|
31
86
|
|
32
|
-
#
|
87
|
+
# Returns the <section> part of the path, or nil when
|
88
|
+
# no path is available.
|
89
|
+
def path_section
|
90
|
+
$1 if @basename.to_s =~ /\.(\d\w*)\./
|
91
|
+
end
|
92
|
+
|
93
|
+
# Returns the manual page name based first on the document's
|
94
|
+
# contents and then on the path name.
|
95
|
+
def name
|
96
|
+
@name || path_name
|
97
|
+
end
|
98
|
+
|
99
|
+
# Truthful when the name was extracted from the name section
|
100
|
+
# of the document.
|
101
|
+
def name?
|
102
|
+
@name
|
103
|
+
end
|
104
|
+
|
105
|
+
# Returns the manual page section based first on the document's
|
106
|
+
# contents and then on the path name.
|
107
|
+
def section
|
108
|
+
@section || path_section
|
109
|
+
end
|
110
|
+
|
111
|
+
# True when the section number was extracted from the name
|
112
|
+
# section of the document.
|
113
|
+
def section?
|
114
|
+
@section
|
115
|
+
end
|
116
|
+
|
117
|
+
# The date the man page was published. If not set explicitly,
|
118
|
+
# this is the file's modified time or, if no file is given,
|
119
|
+
# the current time.
|
120
|
+
def date
|
121
|
+
return @date if @date
|
122
|
+
return File.mtime(path) if File.exist?(path)
|
123
|
+
Time.now
|
124
|
+
end
|
125
|
+
|
126
|
+
# Convert the document to :roff, :html, or :html_fragment and
|
127
|
+
# return the result as a string.
|
33
128
|
def convert(format)
|
34
129
|
send "to_#{format}"
|
35
130
|
end
|
36
131
|
|
37
|
-
# Convert the document to roff.
|
132
|
+
# Convert the document to roff and return the result as a string.
|
38
133
|
def to_roff
|
39
134
|
RoffFilter.new(
|
40
135
|
to_html_fragment,
|
41
136
|
name,
|
42
137
|
section,
|
43
|
-
tagline
|
138
|
+
tagline,
|
139
|
+
manual,
|
140
|
+
organization,
|
141
|
+
date
|
44
142
|
).to_s
|
45
143
|
end
|
46
144
|
|
47
|
-
# Convert the document to HTML and return result
|
48
|
-
# as a string.
|
145
|
+
# Convert the document to HTML and return the result as a string.
|
49
146
|
def to_html
|
50
147
|
layout_filter(to_html_fragment)
|
51
148
|
end
|
52
149
|
|
53
|
-
# Convert the document to HTML and return result
|
150
|
+
# Convert the document to HTML and return the result
|
54
151
|
# as a string. The HTML does not include <html>, <head>,
|
55
152
|
# or <style> tags.
|
56
153
|
def to_html_fragment
|
57
|
-
|
154
|
+
buf = []
|
155
|
+
if name? && section?
|
156
|
+
buf << "<h2 id='NAME'>NAME</h2>"
|
157
|
+
buf << "<p><code>#{name}</code> -- #{tagline}</p>"
|
158
|
+
elsif tagline
|
159
|
+
buf << "<h1>#{[name, tagline].compact.join(' -- ')}</h1>"
|
160
|
+
end
|
161
|
+
buf << @fragment.to_s
|
162
|
+
buf.join("\n")
|
163
|
+
end
|
164
|
+
|
165
|
+
protected
|
166
|
+
# Parse the document and extract the name, section, and tagline
|
167
|
+
# from its contents. This is called while the object is being
|
168
|
+
# initialized.
|
169
|
+
def preprocess
|
170
|
+
[
|
171
|
+
:angle_quote_pre_filter,
|
172
|
+
:markdown_filter,
|
173
|
+
:angle_quote_post_filter,
|
174
|
+
:definition_list_filter
|
175
|
+
].inject(data) { |res,filter| send(filter, res) }
|
58
176
|
end
|
59
177
|
|
60
178
|
# Apply the standard HTML layout template.
|
@@ -64,31 +182,12 @@ module Ron
|
|
64
182
|
eval("%Q{#{template}}", binding, template_file)
|
65
183
|
end
|
66
184
|
|
67
|
-
# Run markdown on the data and extract name, section, and
|
68
|
-
# tagline.
|
69
|
-
def markdown_filter(data)
|
70
|
-
html = Markdown.new(data).to_html
|
71
|
-
@tagline, html = html.split("</h1>\n", 2)
|
72
|
-
@tagline.sub!('<h1>', '')
|
73
|
-
|
74
|
-
# grab name and section from title
|
75
|
-
if @tagline =~ /([\w_:-]+)\((\d\w*)\) -- (.*)/
|
76
|
-
@name, @section = $1, $2
|
77
|
-
@tagline = $3
|
78
|
-
end
|
79
|
-
|
80
|
-
"<h2 id='NAME'>NAME</h2>\n" +
|
81
|
-
"<p><code>#{@name}</code> -- #{@tagline}</p>\n" +
|
82
|
-
html
|
83
|
-
end
|
84
|
-
|
85
185
|
# Convert special format unordered lists to definition lists.
|
86
186
|
def definition_list_filter(html)
|
87
|
-
doc =
|
88
|
-
|
187
|
+
doc = parse_html(html)
|
89
188
|
# process all unordered lists depth-first
|
90
|
-
doc.
|
91
|
-
items = ul.
|
189
|
+
doc.search('ul').to_a.reverse.each do |ul|
|
190
|
+
items = ul.search('li')
|
92
191
|
next if items.any? { |item| item.text.split("\n", 2).first !~ /:$/ }
|
93
192
|
|
94
193
|
ul.name = 'dl'
|
@@ -103,15 +202,88 @@ module Ron
|
|
103
202
|
term, definition = container.inner_html.split(":\n", 2)
|
104
203
|
|
105
204
|
dt = item.before("<dt>#{term}</dt>").previous_sibling
|
106
|
-
dt['class'] = 'flush' if dt.content.length <=
|
205
|
+
dt['class'] = 'flush' if dt.content.length <= 7
|
107
206
|
|
108
207
|
item.name = 'dd'
|
109
208
|
container.swap(wrap.sub(/></, ">#{definition}<"))
|
110
209
|
end
|
111
210
|
end
|
211
|
+
doc
|
212
|
+
end
|
112
213
|
|
113
|
-
|
214
|
+
# Perform angle quote (<THESE>) post filtering.
|
215
|
+
def angle_quote_post_filter(html)
|
216
|
+
doc = parse_html(html)
|
217
|
+
# convert all angle quote vars nested in code blocks
|
218
|
+
# back to the original text
|
219
|
+
doc.search('code text()').each do |node|
|
220
|
+
next unless node.to_s.include?('var>')
|
221
|
+
new = node.document.create_text_node(
|
222
|
+
node.to_s.
|
223
|
+
gsub('<var>', '<').
|
224
|
+
gsub("</var>", '>')
|
225
|
+
)
|
226
|
+
node.replace(new)
|
227
|
+
end
|
228
|
+
doc
|
114
229
|
end
|
115
230
|
|
231
|
+
# Run markdown on the data and extract name, section, and
|
232
|
+
# tagline.
|
233
|
+
def markdown_filter(data)
|
234
|
+
html = Markdown.new(data).to_html
|
235
|
+
@tagline, html = html.split("</h1>\n", 2)
|
236
|
+
if html.nil?
|
237
|
+
html = @tagline
|
238
|
+
@tagline = nil
|
239
|
+
else
|
240
|
+
# grab name and section from title
|
241
|
+
@tagline.sub!('<h1>', '')
|
242
|
+
if @tagline =~ /([\w_.\[\]~+=@:-]+)\s*\((\d\w*)\)\s*--?\s*(.*)/
|
243
|
+
@name = $1
|
244
|
+
@section = $2
|
245
|
+
@tagline = $3
|
246
|
+
elsif @tagline =~ /([\w_.\[\]~+=@:-]+)\s+--\s+(.*)/
|
247
|
+
@name = $1
|
248
|
+
@tagline = $2
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
html.to_s
|
253
|
+
end
|
254
|
+
|
255
|
+
# Convert all <WORD> to <var>WORD</var> but only if WORD
|
256
|
+
# isn't an HTML tag.
|
257
|
+
def angle_quote_pre_filter(data)
|
258
|
+
data.gsub(/\<([^:.\/]+?)\>/) do |match|
|
259
|
+
contents = $1
|
260
|
+
tag, attrs = contents.split(' ', 2)
|
261
|
+
if attrs =~ /\/=/ ||
|
262
|
+
HTML.include?(tag.sub(/^\//, '')) ||
|
263
|
+
data.include?("</#{tag}>")
|
264
|
+
match.to_s
|
265
|
+
else
|
266
|
+
"<var>#{contents}</var>"
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
HTML = %w[
|
272
|
+
a abbr acronym b bdo big br cite code dfn
|
273
|
+
em i img input kbd label q samp select
|
274
|
+
small span strong sub sup textarea tt var
|
275
|
+
address blockquote div dl fieldset form
|
276
|
+
h1 h2 h3 h4 h5 h6 hr noscript ol p pre
|
277
|
+
table ul
|
278
|
+
].to_set
|
279
|
+
|
280
|
+
private
|
281
|
+
def parse_html(html)
|
282
|
+
if html.kind_of?(Nokogiri::HTML::DocumentFragment)
|
283
|
+
html
|
284
|
+
else
|
285
|
+
Nokogiri::HTML.fragment(html.to_s)
|
286
|
+
end
|
287
|
+
end
|
116
288
|
end
|
117
289
|
end
|
data/lib/ron/layout.html
CHANGED
@@ -1,39 +1,70 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
2
|
<html>
|
3
3
|
<head>
|
4
|
-
<
|
4
|
+
<meta http-equiv='content-type' value='text/html;charset=utf8'>
|
5
|
+
<meta name='generator' value='Ron/v#{Ron::VERSION}'>
|
6
|
+
<title>#{name}(#{section})#{tagline ? " -- " + tagline : ''}</title>
|
5
7
|
<style type='text/css'>
|
6
|
-
body {
|
8
|
+
body {margin:0}
|
9
|
+
#man, #man code, #man pre, #man tt, #man kbd, #man samp {
|
7
10
|
font-family:consolas,monospace;
|
8
|
-
font-size:
|
11
|
+
font-size:16px;
|
9
12
|
line-height:1.25;
|
10
|
-
color:#
|
11
|
-
|
12
|
-
#man { max-width:
|
13
|
-
#man h1, #man h2, #man h3
|
14
|
-
#man h1 { text-align:center }
|
15
|
-
#man h2 { font-size:
|
16
|
-
#man h3 { font-size:
|
17
|
-
#man p, #man ul, #man ol, #man dl, #man pre
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
#man
|
26
|
-
#man
|
27
|
-
#man
|
13
|
+
color:#434241;
|
14
|
+
background:#fff; }
|
15
|
+
#man { max-width:85ex; text-align:justify; margin:0 25px 25px 25px }
|
16
|
+
#man h1, #man h2, #man h3 { color:#232221;clear:left }
|
17
|
+
#man h1 { font-size:28px; margin:10px 0 30px 0; text-align:center }
|
18
|
+
#man h2 { font-size:18px; margin-bottom:2px; margin-top:10px; line-height:1.2; }
|
19
|
+
#man h3 { font-size:16px; margin:0 0 0 4ex; }
|
20
|
+
#man p, #man ul, #man ol, #man dl, #man pre { margin:0 0 18px 0; }
|
21
|
+
#man pre {
|
22
|
+
color:#333231;
|
23
|
+
background:#edeceb;
|
24
|
+
padding:5px 10px;
|
25
|
+
border-left:2ex solid #ddd;
|
26
|
+
margin-top:-10px;
|
27
|
+
margin-bottom:12px; }
|
28
|
+
#man > p, #man > ul, #man > ol, #man > dl, #man > pre { margin-left:8ex; }
|
29
|
+
#man dt { margin:0; clear:left }
|
30
|
+
#man dt.flush { float:left; width:8ex }
|
31
|
+
#man dd { margin:0 0 0 9ex }
|
32
|
+
#man code, #man strong, #man b { font-weight:bold; color:#131211; }
|
33
|
+
#man pre code { font-weight:normal; color:#232221; background:inherit }
|
34
|
+
#man em, var, u {
|
35
|
+
font-style:normal; color:#333231; border-bottom:1px solid #999; }
|
36
|
+
#man h1.man-title { display:none; }
|
37
|
+
#man ol.man, #man ol.man li { margin:2px 0 10px 0; padding:0;
|
38
|
+
float:left; width:33%; list-style-type:none;
|
39
|
+
text-transform:uppercase; font-size:18px; color:#999;
|
40
|
+
letter-spacing:1px;}
|
41
|
+
#man ol.man { width:100%; }
|
42
|
+
#man ol.man li.tl { text-align:left }
|
43
|
+
#man ol.man li.tc { text-align:center;letter-spacing:4px }
|
44
|
+
#man ol.man li.tr { text-align:right }
|
45
|
+
#man ol.man a { color:#999 }
|
46
|
+
#man ol.man a:hover { color:#333231 }
|
28
47
|
</style>
|
29
48
|
</head>
|
30
49
|
<body>
|
31
50
|
<div id='man'>
|
32
51
|
|
33
|
-
<h1>#{
|
52
|
+
<h1 class='man-title'>#{name}(#{section})</h1>
|
53
|
+
|
54
|
+
<ol class='head man'>
|
55
|
+
<li class='tl'>#{name if section}#{"("+section+")" if name and section}</li>
|
56
|
+
<li class='tc'>#{manual}</li>
|
57
|
+
<li class='tr'>#{name if section}#{"("+section+")" if name and section}</li>
|
58
|
+
</ol>
|
34
59
|
|
35
60
|
#{html}
|
36
61
|
|
62
|
+
<ol class='foot man'>
|
63
|
+
<li class='tl'>#{organization}</li>
|
64
|
+
<li class='tc'>#{date.strftime('%B %Y')}</li>
|
65
|
+
<li class='tr'>#{name if section}#{"("+section+")" if name and section}</li>
|
66
|
+
</ol>
|
67
|
+
|
37
68
|
</div>
|
38
69
|
</body>
|
39
70
|
</html>
|
data/lib/ron/roff.rb
CHANGED
@@ -16,9 +16,9 @@ module Ron
|
|
16
16
|
|
17
17
|
protected
|
18
18
|
def title_heading(name, section, tagline, manual, version, date)
|
19
|
-
comment "generated with Ron"
|
19
|
+
comment "generated with Ron/v#{Ron::VERSION}"
|
20
20
|
comment "http://github.com/rtomayko/ron/"
|
21
|
-
macro "TH", %["#{escape(name.upcase)}" #{section} "#{date}" "#{version}" "#{manual}"]
|
21
|
+
macro "TH", %["#{escape(name.upcase)}" #{section} "#{date.strftime('%B %Y')}" "#{version}" "#{manual}"]
|
22
22
|
end
|
23
23
|
|
24
24
|
def block_filter(node)
|
@@ -74,6 +74,22 @@ module Ron
|
|
74
74
|
end
|
75
75
|
write "\n"
|
76
76
|
|
77
|
+
# ordered/unordered lists
|
78
|
+
# when 'ul'
|
79
|
+
# # macro "IP", '\(bu'
|
80
|
+
# block_filter(node.children)
|
81
|
+
# when 'ol'
|
82
|
+
# macro "IP", '1.'
|
83
|
+
# block_filter(node.children)
|
84
|
+
# when 'li'
|
85
|
+
# macro "IP" unless prev.nil?
|
86
|
+
# if node.search('p').any?
|
87
|
+
# block_filter(node.children)
|
88
|
+
# else
|
89
|
+
# inline_filter(node.children)
|
90
|
+
# end
|
91
|
+
# write "\n"
|
92
|
+
|
77
93
|
else
|
78
94
|
warn "unrecognized block tag: %p", node.name
|
79
95
|
end
|
@@ -85,14 +101,19 @@ module Ron
|
|
85
101
|
return
|
86
102
|
end
|
87
103
|
|
104
|
+
prev = node.previous_sibling
|
105
|
+
prev = prev.previous_sibling until prev.nil? || prev.element?
|
106
|
+
|
88
107
|
case node.name
|
89
108
|
when 'text'
|
90
|
-
|
91
|
-
|
109
|
+
text = node.content
|
110
|
+
text = text.sub(/^\n+/m, '') if prev && prev.name == 'br'
|
111
|
+
write escape(text.sub(/\n+$/, ' '))
|
112
|
+
when 'code', 'b', 'strong', 'kbd', 'samp'
|
92
113
|
write '\fB'
|
93
114
|
inline_filter(node.children)
|
94
115
|
write '\fR'
|
95
|
-
when 'em', 'i', 'u'
|
116
|
+
when 'var', 'em', 'i', 'u'
|
96
117
|
write '\fI'
|
97
118
|
inline_filter(node.children)
|
98
119
|
write '\fR'
|
data/man/ron.1.ron
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
-
ron(1) --
|
2
|
-
|
1
|
+
ron(1) -- build markdown-based man pages
|
2
|
+
========================================
|
3
3
|
|
4
4
|
## SYNOPSIS
|
5
5
|
|
6
|
-
`ron` [
|
7
|
-
`ron` --build
|
8
|
-
`ron` --install
|
9
|
-
`ron` --man
|
6
|
+
`ron` [ <OPTIONS> ]
|
7
|
+
`ron` --build <FILE> ...
|
8
|
+
`ron` --install <FILE> ...
|
9
|
+
`ron` --man <FILE>
|
10
10
|
|
11
11
|
## DESCRIPTION
|
12
12
|
|
@@ -15,7 +15,7 @@ ron(1) -- the opposite of roff
|
|
15
15
|
and install standard UNIX / roff(7) formatted man pages and to
|
16
16
|
generate nicely formatted HTML manual pages.
|
17
17
|
|
18
|
-
The `ron` command converts one or more named input <
|
18
|
+
The `ron` command converts one or more named input <FILE>s
|
19
19
|
(or standard input when no files are named or the file name `-`
|
20
20
|
is given) from humane man page markup to one or more destination
|
21
21
|
output formats. If no output format is selected explicitly, `ron`
|
@@ -24,13 +24,13 @@ writes output in roff format.
|
|
24
24
|
## FILES
|
25
25
|
|
26
26
|
The `ron` command expects input to be formatted as ron(5) text.
|
27
|
-
Source files are typically named '
|
28
|
-
`hello.1.ron`). The
|
29
|
-
and section defined in
|
27
|
+
Source files are typically named '<NAME>.<SECTION>.ron' (e.g.,
|
28
|
+
`hello.1.ron`). The <NAME> and <SECTION> should match the name
|
29
|
+
and section defined in <FILE>'s heading.
|
30
30
|
|
31
31
|
When building roff and/or HTML output files with the `--build`
|
32
32
|
argument, destination filenames are determined by taking the basename
|
33
|
-
of the input
|
33
|
+
of the input <FILE> and adding the appropriate file extension (or
|
34
34
|
removing the file extension in the case of roff output).
|
35
35
|
|
36
36
|
For example, the command `ron -b --html --roff hello.1.ron` would
|
@@ -46,8 +46,8 @@ The following arguments change this behavior:
|
|
46
46
|
* `-b`, `--build`:
|
47
47
|
Write output directly to files instead of standard output. When
|
48
48
|
the `--roff` option is provided, writes roff output to
|
49
|
-
|
50
|
-
output to '
|
49
|
+
<file>.<section>. When the `--html` option is provided, writes
|
50
|
+
output to '<file>.<section>.html'.
|
51
51
|
|
52
52
|
* `-i`, `--install`:
|
53
53
|
Install the roff formatted man page to the local system such
|
@@ -59,20 +59,42 @@ The following arguments change this behavior:
|
|
59
59
|
_/usr/local/man_.
|
60
60
|
|
61
61
|
* `-m`, `--man`:
|
62
|
-
Display
|
62
|
+
Display <FILE>s as if man(1) were invoked on the roff output
|
63
63
|
file. This simulates default man behavior by piping the roff
|
64
64
|
output through groff(1) and the paging program specified by the
|
65
65
|
`MANPAGER` environment variable.
|
66
66
|
|
67
|
-
|
67
|
+
These options control the format used in generated output:
|
68
68
|
|
69
|
-
* `--
|
70
|
-
Generate HTML output.
|
71
|
-
|
72
|
-
* `--roff`:
|
69
|
+
* `-r`, `--roff`:
|
73
70
|
Generate roff output. This is the default behavior when no
|
74
71
|
other format argument is provided.
|
75
72
|
|
73
|
+
* `-5`, `--html`:
|
74
|
+
Generate output in HTML format.
|
75
|
+
|
76
|
+
* `-f`, `--fragment`:
|
77
|
+
Generate output in HTML format but only the document
|
78
|
+
fragment, not the header, title, or footer.
|
79
|
+
|
80
|
+
All document attributes displayed in the header and footer areas
|
81
|
+
of generated content can be specified with these options:
|
82
|
+
|
83
|
+
* `--manual`=<MANUAL>:
|
84
|
+
The name of the manual this man page belongs to; <MANUAL> is
|
85
|
+
prominently displayed top-center in the header area.
|
86
|
+
|
87
|
+
* `--organization`=<NAME>:
|
88
|
+
The name of the group, organization, or individual
|
89
|
+
responsible for publishing the document; <NAME> is displayed
|
90
|
+
in the bottom-left footer area.
|
91
|
+
|
92
|
+
* `--date`=<DATE>:
|
93
|
+
The document's published date; <DATE> must be formatted
|
94
|
+
`YYYY-MM-DD` and is displayed in the bottom-center footer
|
95
|
+
area. The <FILE> mtime is used when no <DATE> is given,
|
96
|
+
or the current time when no <FILE> is available.
|
97
|
+
|
76
98
|
## EXAMPLES
|
77
99
|
|
78
100
|
Generate `roff(7)` output on stdout:
|
@@ -113,7 +135,7 @@ Install the roff man page for a ron file:
|
|
113
135
|
* `MANHOME`:
|
114
136
|
Location where roff formatted man pages should be installed.
|
115
137
|
Relevant only when the `--installed` argument is provided.
|
116
|
-
|
138
|
+
<PATH> is to the base of a man file hierarchy. e.g.,
|
117
139
|
`/usr/local/share/man`, `/home/rtomayko/man`.
|
118
140
|
|
119
141
|
* `MANPAGER`:
|
@@ -125,7 +147,8 @@ Install the roff man page for a ron file:
|
|
125
147
|
|
126
148
|
## BUGS
|
127
149
|
|
128
|
-
Ron is written in Ruby.
|
150
|
+
Ron is written in Ruby. C and/or standalone Perl implementations
|
151
|
+
would be preferable for ease of packaging and distribution.
|
129
152
|
|
130
153
|
## COPYRIGHT
|
131
154
|
|
data/man/ron.5.ron
CHANGED
@@ -99,11 +99,13 @@ uses the following bits of markdown(5) to accomplish this:
|
|
99
99
|
displayed in in <b>boldface</b>. Note that all text included
|
100
100
|
within `backticks` is displayed literally; other inline markup
|
101
101
|
is not processed.
|
102
|
+
* `**double-stars**`:
|
103
|
+
Like `backticks` but may contain other inline markup.
|
102
104
|
* `_`_underbars_`_`:
|
103
105
|
User-specified arguments, variables, or user input; typically
|
104
|
-
displayed
|
105
|
-
*
|
106
|
-
|
106
|
+
displayed with <u>underline</u>.
|
107
|
+
* `<angle-quotes>`:
|
108
|
+
Same as _underbars_. This is not compatible with Markdown.
|
107
109
|
|
108
110
|
Here is grep(1)'s DESCRIPTION section represented in `ron`:
|
109
111
|
|
data/man/ron.7.ron
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
ron(7) -- the opposite of roff
|
2
|
+
==============================
|
3
|
+
|
4
|
+
DESCRIPTION
|
5
|
+
-----------
|
6
|
+
|
7
|
+
Ron is a humane text format and toolchain for
|
8
|
+
creating UNIX man pages -- and things that
|
9
|
+
appear as man pages from a distance. Use it
|
10
|
+
to build and install standard UNIX roff man
|
11
|
+
pages or to generate nicely formatted HTML
|
12
|
+
manual pages for the web.
|
13
|
+
|
14
|
+
The ron file format is based on Markdown. In
|
15
|
+
fact, ron files are 100% Markdown compatible
|
16
|
+
but have a more rigidly defined structure and
|
17
|
+
extend Markdown in some ways to provide
|
18
|
+
features commonly found in man pages (e.g.,
|
19
|
+
definition lists). The ron(5) manual page
|
20
|
+
included with this distribution defines the
|
21
|
+
format in more detail.
|
22
|
+
|
23
|
+
INSTALL
|
24
|
+
-------
|
25
|
+
|
26
|
+
Ron can be installed using rubygems:
|
27
|
+
|
28
|
+
$ [sudo] gem install ron
|
29
|
+
|
30
|
+
Or, clone the git repository and install from
|
31
|
+
source:
|
32
|
+
|
33
|
+
$ git clone git://github.com/rtomayko/ron.git
|
34
|
+
$ cd ron
|
35
|
+
$ rake package
|
36
|
+
$ [sudo] rake install
|
37
|
+
|
38
|
+
EXAMPLES
|
39
|
+
--------
|
40
|
+
|
41
|
+
The .ron files located under the repository's
|
42
|
+
./man directory show off a wide range of ron
|
43
|
+
capabilities. The HTML versions of these
|
44
|
+
files are available at:
|
45
|
+
|
46
|
+
http://rtomayko.github.com/ron/ron.1.html
|
47
|
+
http://rtomayko.github.com/ron/ron.5.html
|
48
|
+
http://rtomayko.github.com/ron/markdown.5.html
|
49
|
+
|
50
|
+
BASIC USAGE
|
51
|
+
-----------
|
52
|
+
|
53
|
+
To generate a roff man page from the included
|
54
|
+
`markdown.5.ron` file and open it in man(1):
|
55
|
+
|
56
|
+
$ ron -b man/markdown.5.ron
|
57
|
+
building: man/markdown.5
|
58
|
+
$ man man/markdown.5
|
59
|
+
|
60
|
+
To generate a standalone HTML version:
|
61
|
+
|
62
|
+
$ ron -b --html man/markdown.5.ron
|
63
|
+
building: man/markdown.5.html
|
64
|
+
$ open man/markdown.5.html
|
65
|
+
|
66
|
+
To build roff and HTML versions of all ron
|
67
|
+
files:
|
68
|
+
|
69
|
+
$ ron -b --roff --html man/*.ron
|
70
|
+
|
71
|
+
If you just want to view a ron file as if it
|
72
|
+
were a man page without building any
|
73
|
+
intermediate files:
|
74
|
+
|
75
|
+
$ ron -m man/markdown.5.ron
|
76
|
+
|
77
|
+
The ron(1) manual page included with this
|
78
|
+
distribution includes full documentation on
|
79
|
+
ron command line options.
|
80
|
+
|
81
|
+
RATIONALE
|
82
|
+
---------
|
83
|
+
|
84
|
+
Some people think UNIX manual pages are a
|
85
|
+
poor and outdated form of documentation. I
|
86
|
+
disagree.
|
87
|
+
|
88
|
+
- Man pages typically follow a well defined
|
89
|
+
structure that's immediately familiar and
|
90
|
+
provides a useful starting point for
|
91
|
+
developers documenting new tools,
|
92
|
+
libraries, and formats.
|
93
|
+
|
94
|
+
- Man pages get to the point. Because they're
|
95
|
+
written in an inverted style, with a
|
96
|
+
SYNOPSIS section followed by additional
|
97
|
+
detail, prose, and finally references to
|
98
|
+
other sources of information, man pages
|
99
|
+
provide the best of both cheat sheet and
|
100
|
+
reference style documentation.
|
101
|
+
|
102
|
+
- Man pages have very limited text formatting
|
103
|
+
capabilities. This is a feature. You get
|
104
|
+
bold and underline, basically, and they're
|
105
|
+
typically applied consistently across man
|
106
|
+
pages.
|
107
|
+
|
108
|
+
- Most man pages use only a single level of
|
109
|
+
section hierarchy (although two levels are
|
110
|
+
technically supported). Hierarchy destroys
|
111
|
+
otherwise good documentation by adding
|
112
|
+
unnecessary complexity. Feynman described
|
113
|
+
the whole of quantum electro dynamics with
|
114
|
+
only two levels of hierarchy. How can you
|
115
|
+
possibly need more? Man pages force you to
|
116
|
+
keep it simple.
|
117
|
+
|
118
|
+
- Man pages have a simple referencing syntax;
|
119
|
+
e.g., sh(1), fork(2), markdown(5). HTML
|
120
|
+
versions can use this to generate links
|
121
|
+
between pages.
|
122
|
+
|
123
|
+
- The classical terminal man page display is
|
124
|
+
typographically well thought out. Big bold
|
125
|
+
section headings, justified monospaced
|
126
|
+
text, nicely indented paragraphs,
|
127
|
+
intelligently aligned definition lists, and
|
128
|
+
an informational header and footer.
|
129
|
+
|
130
|
+
All that being said, trying to figure out how
|
131
|
+
to create a man page can be a really tedious
|
132
|
+
process. The roff/man macro languages are
|
133
|
+
highly extensible, fractured between multiple
|
134
|
+
dialects, and include a bunch of stuff that's
|
135
|
+
entirely irrelevant to modern man page
|
136
|
+
creation. It's also horribly ugly compared to
|
137
|
+
today's humane text formats or even HTML
|
138
|
+
(just sayin').
|
139
|
+
|
140
|
+
Ron aims to address many of the issues with
|
141
|
+
man page creation while preserving the things
|
142
|
+
that makes man pages a great form of
|
143
|
+
documentation.
|
144
|
+
|
145
|
+
COPYRIGHT
|
146
|
+
---------
|
147
|
+
|
148
|
+
Ron is Copyright (C) 2009 Ryan Tomayko
|
149
|
+
See the file COPYING for more information.
|
data/ron.gemspec
CHANGED
@@ -3,8 +3,8 @@ Gem::Specification.new do |s|
|
|
3
3
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
4
4
|
|
5
5
|
s.name = 'ron'
|
6
|
-
s.version = '0.
|
7
|
-
s.date = '2009-11-
|
6
|
+
s.version = '0.2'
|
7
|
+
s.date = '2009-11-23'
|
8
8
|
|
9
9
|
s.description = "The opposite of roff"
|
10
10
|
s.summary = "The opposite of roff"
|
@@ -25,10 +25,20 @@ Gem::Specification.new do |s|
|
|
25
25
|
man/markdown.5.ron
|
26
26
|
man/ron.1.ron
|
27
27
|
man/ron.5.ron
|
28
|
+
man/ron.7.ron
|
28
29
|
ron.gemspec
|
30
|
+
test/angle_bracket_syntax.html
|
31
|
+
test/angle_bracket_syntax.ron
|
32
|
+
test/basic_document.html
|
33
|
+
test/basic_document.ron
|
34
|
+
test/custom_title_document.html
|
35
|
+
test/custom_title_document.ron
|
36
|
+
test/definition_list_syntax.html
|
37
|
+
test/definition_list_syntax.ron
|
29
38
|
test/document_test.rb
|
30
39
|
test/ron_test.rb
|
31
|
-
test/
|
40
|
+
test/titleless_document.html
|
41
|
+
test/titleless_document.ron
|
32
42
|
]
|
33
43
|
# = MANIFEST =
|
34
44
|
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<h2 id='NAME'>NAME</h2>
|
2
|
+
<p><code>angle_bracket_syntax</code> -- angle bracket syntax test</p>
|
3
|
+
<p>A <var>WORD</var> in angle brackets is converted to <var>WORD</var>,</p>
|
4
|
+
|
5
|
+
<pre><code>except when <WORD> is
|
6
|
+
part of a preformatted
|
7
|
+
code block,
|
8
|
+
</code></pre>
|
9
|
+
|
10
|
+
<p>or when <code><WORD></code> is enclosed in backticks.</p>
|
11
|
+
|
12
|
+
<p>or when <var>WORD</var> has a <dot.> or <foo:colon>.</p>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
angle_bracket_syntax(5) -- angle bracket syntax test
|
2
|
+
====================================================
|
3
|
+
|
4
|
+
A <WORD> in angle brackets is converted to <var>WORD</var>,
|
5
|
+
|
6
|
+
except when <WORD> is
|
7
|
+
part of a preformatted
|
8
|
+
code block,
|
9
|
+
|
10
|
+
or when `<WORD>` is enclosed in backticks.
|
11
|
+
|
12
|
+
or when <WORD> has a <dot.> or <foo:colon>.
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<h2 id='NAME'>NAME</h2>
|
2
|
+
<p><code>defition_list_syntax</code> -- hiya</p>
|
3
|
+
<p>Definition lists look like unordered lists:</p>
|
4
|
+
|
5
|
+
<dl>
|
6
|
+
<dt class="flush">term</dt>
|
7
|
+
<dd><p>definition</p></dd>
|
8
|
+
<dt>another one</dt>
|
9
|
+
<dd>
|
10
|
+
<p>The definition may span
|
11
|
+
multiple lines and even</p>
|
12
|
+
|
13
|
+
<p>start</p>
|
14
|
+
|
15
|
+
<p>new paragraphs</p>
|
16
|
+
</dd>
|
17
|
+
<dt>
|
18
|
+
<code>--somearg</code>=<var>VALUE</var>
|
19
|
+
</dt>
|
20
|
+
<dd><p>We can do that too.</p></dd>
|
21
|
+
</dl>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
defition_list_syntax(5) -- hiya
|
2
|
+
===============================
|
3
|
+
|
4
|
+
Definition lists look like unordered lists:
|
5
|
+
|
6
|
+
* term:
|
7
|
+
definition
|
8
|
+
|
9
|
+
* another one:
|
10
|
+
The definition may span
|
11
|
+
multiple lines and even
|
12
|
+
|
13
|
+
start
|
14
|
+
|
15
|
+
new paragraphs
|
16
|
+
|
17
|
+
* `--somearg`=<VALUE>:
|
18
|
+
We can do that too.
|
data/test/document_test.rb
CHANGED
@@ -2,34 +2,87 @@ require 'contest'
|
|
2
2
|
require 'ron/document'
|
3
3
|
|
4
4
|
class DocumentTest < Test::Unit::TestCase
|
5
|
-
SIMPLE_FILE = "#{File.dirname(__FILE__)}/
|
6
|
-
HELLO_DATA = "# hello(1) -- hello world"
|
5
|
+
SIMPLE_FILE = "#{File.dirname(__FILE__)}/basic_document.ron"
|
7
6
|
|
8
|
-
test "
|
7
|
+
test "new with path" do
|
9
8
|
doc = Ron::Document.new(SIMPLE_FILE)
|
10
9
|
assert_equal File.read(SIMPLE_FILE), doc.data
|
11
10
|
end
|
12
11
|
|
13
|
-
test "
|
14
|
-
doc = Ron::Document.new('hello.1.ron') {
|
15
|
-
assert_equal
|
12
|
+
test "new with path and block" do
|
13
|
+
doc = Ron::Document.new('hello.1.ron') { "# hello(1) -- hello world" }
|
14
|
+
assert_equal "# hello(1) -- hello world", doc.data
|
16
15
|
end
|
17
16
|
|
18
|
-
|
17
|
+
test "new with path and block but missing name section" do
|
18
|
+
doc = Ron::Document.new('foo.7.ron') { '' }
|
19
|
+
assert_equal 'foo', doc.name
|
20
|
+
assert_equal '7', doc.section
|
21
|
+
end
|
22
|
+
|
23
|
+
test "new with non conventional path and missing name section" do
|
24
|
+
doc = Ron::Document.new('bar.ron') { '' }
|
25
|
+
assert_equal 'bar', doc.name
|
26
|
+
assert_equal nil, doc.section
|
27
|
+
assert_equal "./bar.html", doc.path_for('html')
|
28
|
+
assert_equal "./bar", doc.path_for('roff')
|
29
|
+
assert_equal "./bar", doc.path_for('')
|
30
|
+
assert_equal "./bar", doc.path_for(nil)
|
31
|
+
end
|
32
|
+
|
33
|
+
test "new with path and name section mismatch" do
|
34
|
+
doc = Ron::Document.new('foo/rick.7.ron') { "# randy(3) -- I'm confused." }
|
35
|
+
assert_equal 'randy', doc.name
|
36
|
+
assert_equal 'rick', doc.path_name
|
37
|
+
assert_equal '3', doc.section
|
38
|
+
assert_equal '7', doc.path_section
|
39
|
+
assert_equal 'rick.7', doc.basename
|
40
|
+
assert_equal 'foo/rick.7.bar', doc.path_for(:bar)
|
41
|
+
end
|
42
|
+
|
43
|
+
test "new with no path and a name section" do
|
44
|
+
doc = Ron::Document.new { "# brandy(5) -- wootderitis" }
|
45
|
+
assert_equal nil, doc.path_name
|
46
|
+
assert_equal nil, doc.path_section
|
47
|
+
assert_equal 'brandy', doc.name
|
48
|
+
assert_equal '5', doc.section
|
49
|
+
assert_equal 'brandy.5', doc.basename
|
50
|
+
assert_equal 'brandy.5.foo', doc.path_for(:foo)
|
51
|
+
end
|
52
|
+
|
53
|
+
context "simple conventionally named document" do
|
19
54
|
setup do
|
20
|
-
@doc = Ron::Document.new('hello.1.ron') {
|
55
|
+
@doc = Ron::Document.new('hello.1.ron') { "# hello(1) -- hello world" }
|
21
56
|
end
|
22
57
|
|
23
58
|
should "load data" do
|
24
|
-
assert_equal
|
59
|
+
assert_equal "# hello(1) -- hello world", @doc.data
|
25
60
|
end
|
26
61
|
|
27
|
-
should "extract the name" do
|
62
|
+
should "extract the manual page name from the filename or document" do
|
28
63
|
assert_equal 'hello', @doc.name
|
29
64
|
end
|
30
65
|
|
31
|
-
should "extract the section" do
|
66
|
+
should "extract the manual page section from the filename or document" do
|
32
67
|
assert_equal '1', @doc.section
|
33
68
|
end
|
69
|
+
|
70
|
+
should "convert to an HTML fragment" do
|
71
|
+
assert_equal %[<h2 id='NAME'>NAME</h2>\n<p><code>hello</code> -- hello world</p>\n],
|
72
|
+
@doc.to_html_fragment
|
73
|
+
end
|
74
|
+
|
75
|
+
should "convert to HTML with a layout" do
|
76
|
+
assert_match %r{^<!DOCTYPE html.*}m, @doc.to_html
|
77
|
+
assert_match %[<h2 id='NAME'>NAME</h2>\n<p><code>hello</code> -- hello world</p>],
|
78
|
+
@doc.to_html
|
79
|
+
end
|
80
|
+
|
81
|
+
should "construct a path to related documents" do
|
82
|
+
assert_equal "./hello.1.html", @doc.path_for(:html)
|
83
|
+
assert_equal "./hello.1", @doc.path_for(:roff)
|
84
|
+
assert_equal "./hello.1", @doc.path_for('')
|
85
|
+
assert_equal "./hello.1", @doc.path_for(nil)
|
86
|
+
end
|
34
87
|
end
|
35
88
|
end
|
data/test/ron_test.rb
CHANGED
@@ -1,21 +1,22 @@
|
|
1
1
|
require 'contest'
|
2
|
+
require 'ron'
|
2
3
|
|
3
|
-
# ron command tests
|
4
4
|
class RonTest < Test::Unit::TestCase
|
5
5
|
testdir = File.dirname(__FILE__)
|
6
6
|
bindir = File.dirname(testdir)
|
7
7
|
ENV['PATH'] = "#{bindir}:#{ENV['PATH']}"
|
8
|
+
ENV['RUBYLIB'] = $LOAD_PATH.join(':')
|
8
9
|
|
9
10
|
SIMPLE_FILE = "#{File.dirname(__FILE__)}/simple.ron"
|
10
11
|
|
11
12
|
test "takes ron text on stdin and produces roff on stdout" do
|
12
|
-
output = `echo '# hello(1) -- hello world' | ron`
|
13
|
+
output = `echo '# hello(1) -- hello world' | ron --date=2009-11-23`
|
13
14
|
lines = output.split("\n")
|
14
15
|
assert_equal 7, lines.size
|
15
|
-
assert_equal %[.\\" generated with Ron], lines.shift
|
16
|
+
assert_equal %[.\\" generated with Ron/v#{Ron::VERSION}], lines.shift
|
16
17
|
assert_equal %[.\\" http://github.com/rtomayko/ron/], lines.shift
|
17
18
|
assert_equal %[.], lines.shift
|
18
|
-
assert_equal %[.TH "HELLO" 1 "" "" ""], lines.shift
|
19
|
+
assert_equal %[.TH "HELLO" 1 "November 2009" "" ""], lines.shift
|
19
20
|
assert_equal %[.], lines.shift
|
20
21
|
assert_equal %[.SH "NAME"], lines.shift
|
21
22
|
assert_equal %[\\fBhello\\fR \\-\\- hello world], lines.shift
|
@@ -24,6 +25,30 @@ class RonTest < Test::Unit::TestCase
|
|
24
25
|
|
25
26
|
test "produces html instead of roff with the --html argument" do
|
26
27
|
output = `echo '# hello(1) -- hello world' | ron --html`
|
27
|
-
assert_match(/<h2 id=
|
28
|
+
assert_match(/<h2 id='NAME'>NAME<\/h2>/, output)
|
29
|
+
end
|
30
|
+
|
31
|
+
test "produces html fragment with the --fragment argument" do
|
32
|
+
output = `echo '# hello(1) -- hello world' | ron --fragment`
|
33
|
+
assert_equal "<h2 id='NAME'>NAME</h2>\n<p><code>hello</code> -- hello world</p>\n",
|
34
|
+
output
|
35
|
+
end
|
36
|
+
|
37
|
+
# file based tests
|
38
|
+
Dir[testdir + '/*.ron'].each do |source|
|
39
|
+
dest = source.sub(/ron$/, 'html')
|
40
|
+
wrong = source.sub(/ron$/, "wrong")
|
41
|
+
test File.basename(source, '.ron') do
|
42
|
+
html = `ron --html --fragment #{source}`
|
43
|
+
expected = File.read(dest) rescue ''
|
44
|
+
if expected != html
|
45
|
+
File.open(wrong, 'wb') { |f| f.write(html) }
|
46
|
+
diff = `diff -u #{dest} #{wrong} 2>/dev/null`
|
47
|
+
fail "the #{dest} file does not exist" if diff.empty?
|
48
|
+
flunk diff
|
49
|
+
elsif File.exist?(wrong)
|
50
|
+
File.unlink(wrong)
|
51
|
+
end
|
52
|
+
end
|
28
53
|
end
|
29
54
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ron
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: "0.
|
4
|
+
version: "0.2"
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Tomayko
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-11-
|
12
|
+
date: 2009-11-23 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -63,10 +63,20 @@ files:
|
|
63
63
|
- man/markdown.5.ron
|
64
64
|
- man/ron.1.ron
|
65
65
|
- man/ron.5.ron
|
66
|
+
- man/ron.7.ron
|
66
67
|
- ron.gemspec
|
68
|
+
- test/angle_bracket_syntax.html
|
69
|
+
- test/angle_bracket_syntax.ron
|
70
|
+
- test/basic_document.html
|
71
|
+
- test/basic_document.ron
|
72
|
+
- test/custom_title_document.html
|
73
|
+
- test/custom_title_document.ron
|
74
|
+
- test/definition_list_syntax.html
|
75
|
+
- test/definition_list_syntax.ron
|
67
76
|
- test/document_test.rb
|
68
77
|
- test/ron_test.rb
|
69
|
-
- test/
|
78
|
+
- test/titleless_document.html
|
79
|
+
- test/titleless_document.ron
|
70
80
|
has_rdoc: true
|
71
81
|
homepage: http://github.com/rtomayko/ron/
|
72
82
|
licenses: []
|