ron 0.3 → 0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/bin/ron DELETED
@@ -1,130 +0,0 @@
1
- #!/usr/bin/env ruby
2
- ## Usage: ron [ OPTIONS ] [ FILE ]
3
- ## ron --build FILE ...
4
- ## ron --install FILE ...
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.
9
- ##
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)
15
- ##
16
- ## Formats:
17
- ## -r, --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
20
- ##
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
30
- ##
31
- require 'date'
32
- require 'optparse'
33
-
34
- formats = []
35
- options = {}
36
- build = false
37
- install = false
38
- man = false
39
- groff = "groff -Wall -mtty-char -mandoc -Tascii"
40
- pager = ENV['MANPAGER'] || ENV['PAGER'] || 'more'
41
-
42
- def info(message, *args)
43
- STDERR.puts message % args
44
- end
45
-
46
- def usage
47
- puts File.read(__FILE__).
48
- grep(/^##.*/).
49
- map { |line| line.chomp[3..-1] }.
50
- join("\n")
51
- end
52
-
53
- # parse command line options
54
- ARGV.options do |option|
55
- # modes
56
- option.on("--pipe") { }
57
- option.on("-b", "--build") { build = true }
58
- option.on("-i", "--install") { install = true }
59
- option.on("-m", "--man") { man = true }
60
-
61
- # format options
62
- option.on("-r", "--roff") { formats << 'roff' }
63
- option.on("-5", "--html") { formats << 'html' }
64
- option.on("-f", "--fragment") { formats << 'html_fragment' }
65
-
66
- # manual attribute options
67
- [:name, :section, :manual, :organization, :date].each do |option_attr|
68
- option.on("--#{option_attr}=VALUE") { |val| options[option_attr] = val }
69
- end
70
-
71
- option.on_tail("--help") { usage ; exit }
72
- option.parse!
73
- end
74
-
75
- if ARGV.empty? && STDIN.tty?
76
- usage
77
- exit
78
- elsif ARGV.empty?
79
- ARGV.push '-'
80
- end
81
-
82
- # turn the --date arg into a real date object
83
- options[:date] &&= Date.strptime(options[:date], '%Y-%m-%d')
84
-
85
- formats = ['roff'] if formats.empty?
86
- formats.delete('html') if formats.include?('html_fragment')
87
- pid = nil
88
-
89
- begin
90
- require 'ron'
91
- rescue LoadError
92
- $:.unshift File.dirname(__FILE__) + "../lib"
93
- require 'ron'
94
- end
95
-
96
- wr = STDOUT
97
- ARGV.each do |file|
98
- doc = Ron.new(file, options) { file == '-' ? STDIN.read : File.read(file) }
99
-
100
- # setup the man pipeline if the --man option was specified
101
- if man && !build
102
- rd, wr = IO.pipe
103
- if pid = fork
104
- rd.close
105
- else
106
- wr.close
107
- STDIN.reopen rd
108
- exec "#{groff} | #{pager}"
109
- end
110
- end
111
-
112
- # write output for each format
113
- formats.each do |format|
114
- if build
115
- path = doc.path_for(format)
116
- info "building: #{path}" if build
117
- output = doc.convert(format)
118
- File.open(path, 'wb') { |f| f.puts(output) }
119
- system "man #{path}" if man && format == 'roff'
120
- else
121
- output = doc.convert(format)
122
- wr.puts(output)
123
- end
124
- end
125
-
126
- if pid
127
- wr.close
128
- Process.wait
129
- end
130
- end
data/lib/ron.rb DELETED
@@ -1,16 +0,0 @@
1
- # Ron is a humane text format and toolchain for authoring manpages (and
2
- # things that appear as manpages from a distance). Use it to build /
3
- # install standard UNIX roff(7) formatted manpages or to generate
4
- # beautiful HTML manpages.
5
- module Ron
6
- VERSION = '0.3'
7
-
8
- require 'ron/document'
9
- require 'ron/roff'
10
-
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)
15
- end
16
- end
@@ -1,289 +0,0 @@
1
- require 'set'
2
- require 'nokogiri'
3
- require 'rdiscount'
4
- require 'ron/roff'
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.
15
- class Document
16
- attr_reader :path, :data
17
-
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)
51
- @reader = block || Proc.new { |f| File.read(f) }
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
58
-
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
78
- end
79
-
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
85
- end
86
-
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.nil?
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.nil?
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.
128
- def convert(format)
129
- send "to_#{format}"
130
- end
131
-
132
- # Convert the document to roff and return the result as a string.
133
- def to_roff
134
- RoffFilter.new(
135
- to_html_fragment,
136
- name,
137
- section,
138
- tagline,
139
- manual,
140
- organization,
141
- date
142
- ).to_s
143
- end
144
-
145
- # Convert the document to HTML and return the result as a string.
146
- def to_html
147
- layout_filter(to_html_fragment)
148
- end
149
-
150
- # Convert the document to HTML and return the result
151
- # as a string. The HTML does not include <html>, <head>,
152
- # or <style> tags.
153
- def to_html_fragment
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) }
176
- end
177
-
178
- # Apply the standard HTML layout template.
179
- def layout_filter(html)
180
- template_file = File.dirname(__FILE__) + "/layout.html"
181
- template = File.read(template_file)
182
- eval("%Q{#{template}}", binding, template_file)
183
- end
184
-
185
- # Convert special format unordered lists to definition lists.
186
- def definition_list_filter(html)
187
- doc = parse_html(html)
188
- # process all unordered lists depth-first
189
- doc.search('ul').to_a.reverse.each do |ul|
190
- items = ul.search('li')
191
- next if items.any? { |item| item.text.split("\n", 2).first !~ /:$/ }
192
-
193
- ul.name = 'dl'
194
- items.each do |item|
195
- if item.child.name == 'p'
196
- wrap = '<p></p>'
197
- container = item.child
198
- else
199
- wrap = '<dd></dd>'
200
- container = item
201
- end
202
- term, definition = container.inner_html.split(":\n", 2)
203
-
204
- dt = item.before("<dt>#{term}</dt>").previous_sibling
205
- dt['class'] = 'flush' if dt.content.length <= 7
206
-
207
- item.name = 'dd'
208
- container.swap(wrap.sub(/></, ">#{definition}<"))
209
- end
210
- end
211
- doc
212
- end
213
-
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&gt;')
221
- new = node.document.create_text_node(
222
- node.to_s.
223
- gsub('&lt;var&gt;', '<').
224
- gsub("&lt;/var&gt;", '>')
225
- )
226
- node.replace(new)
227
- end
228
- doc
229
- end
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
288
- end
289
- end
@@ -1,75 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
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>
7
- <style type='text/css'>
8
- body {margin:0}
9
- #man, #man code, #man pre, #man tt, #man kbd, #man samp {
10
- font-family:consolas,monospace;
11
- font-size:16px;
12
- line-height:1.3;
13
- color:#343331;
14
- background:#fff; }
15
- #man { max-width:89ex; 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:15px 0 30px 0; text-align:center }
18
- #man h2 { font-size:18px; margin-bottom:0; margin-top:10px; line-height:1.3; }
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 7px;
25
- margin:0px 0 20px 0;
26
- border-left:2ex solid #ddd}
27
- #man pre + h2, #man pre + h3 {
28
- margin-top:22px;
29
- }
30
- #man h2 + pre, #man h3 + pre {
31
- margin-top:5px;
32
- }
33
- #man > p, #man > ul, #man > ol, #man > dl, #man > pre { margin-left:8ex; }
34
- #man dt { margin:0; clear:left }
35
- #man dt.flush { float:left; width:8ex }
36
- #man dd { margin:0 0 0 9ex }
37
- #man code, #man strong, #man b { font-weight:bold; color:#131211; }
38
- #man pre code { font-weight:normal; color:#232221; background:inherit }
39
- #man em, var, u {
40
- font-style:normal; color:#333231; border-bottom:1px solid #999; }
41
- #man h1.man-title { display:none; }
42
- #man ol.man, #man ol.man li { margin:2px 0 10px 0; padding:0;
43
- float:left; width:33%; list-style-type:none;
44
- text-transform:uppercase; font-size:18px; color:#999;
45
- letter-spacing:1px;}
46
- #man ol.man { width:100%; }
47
- #man ol.man li.tl { text-align:left }
48
- #man ol.man li.tc { text-align:center;letter-spacing:4px }
49
- #man ol.man li.tr { text-align:right }
50
- #man ol.man a { color:#999 }
51
- #man ol.man a:hover { color:#333231 }
52
- </style>
53
- </head>
54
- <body>
55
- <div id='man'>
56
-
57
- <h1 class='man-title'>#{name}(#{section})</h1>
58
-
59
- <ol class='head man'>
60
- <li class='tl'>#{name if section}#{"("+section+")" if name and section}</li>
61
- <li class='tc'>#{manual}</li>
62
- <li class='tr'>#{name if section}#{"("+section+")" if name and section}</li>
63
- </ol>
64
-
65
- #{html}
66
-
67
- <ol class='foot man'>
68
- <li class='tl'>#{organization}</li>
69
- <li class='tc'>#{date.strftime('%B %Y')}</li>
70
- <li class='tr'>#{name if section}#{"("+section+")" if name and section}</li>
71
- </ol>
72
-
73
- </div>
74
- </body>
75
- </html>