ron 0.1

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/COPYING ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (C) 2009 Ryan Tomayko <tomayko.com/about>
2
+
3
+ Permission is hereby granted, free of charge, to any person ob-
4
+ taining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without restric-
6
+ tion, including without limitation the rights to use, copy, modi-
7
+ fy, merge, publish, distribute, sublicense, and/or sell copies of
8
+ the Software, and to permit persons to whom the Software is fur-
9
+ nished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
16
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONIN-
17
+ FRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
19
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README ADDED
@@ -0,0 +1,145 @@
1
+ ron -- the opposite of roff
2
+
3
+ Ron is a humane text format and toolchain for
4
+ creating UNIX man pages -- and things that
5
+ appear as man pages from a distance. Use it
6
+ to build and install standard UNIX roff man
7
+ pages or to generate nicely formatted HTML
8
+ manual pages for the web.
9
+
10
+ The ron file format is based on Markdown. In
11
+ fact, ron files are 100% Markdown compatible
12
+ but have a more rigidly defined structure and
13
+ extend Markdown in some ways to provide
14
+ features commonly found in man pages (e.g.,
15
+ definition lists). The ron(5) manual page
16
+ included with this distribution defines the
17
+ format in more detail.
18
+
19
+ INSTALL
20
+ -------
21
+
22
+ The easiest way to install ron is with
23
+ rubygems:
24
+
25
+ $ [sudo] gem install ron -s gems.gemcutter.org
26
+
27
+ Or, clone the git repository and install from
28
+ source:
29
+
30
+ $ git clone git://github.com/rtomayko/ron.git
31
+ $ cd ron
32
+ $ rake install
33
+
34
+ EXAMPLES
35
+ --------
36
+
37
+ The .ron files located under the repository's
38
+ ./man directory show off a wide range of ron
39
+ capabilities. The HTML versions of these
40
+ files are available at:
41
+
42
+ http://rtomayko.github.com/ron/ron.1.html
43
+ http://rtomayko.github.com/ron/ron.5.html
44
+ http://rtomayko.github.com/ron/markdown.5.html
45
+
46
+ BASIC USAGE
47
+ -----------
48
+
49
+ To generate a roff man page from the included
50
+ markdown.5.ron file and open it in man(1):
51
+
52
+ $ ron -b man/markdown.5.ron
53
+ building: man/markdown.5
54
+ $ man man/markdown.5
55
+
56
+ To generate a standalone HTML version:
57
+
58
+ $ ron -b --html man/markdown.5.ron
59
+ building: man/markdown.5.html
60
+ $ open man/markdown.5.html
61
+
62
+ To build roff and HTML versions of all ron
63
+ files:
64
+
65
+ $ ron -b --roff --html man/*.ron
66
+
67
+ If you just want to view a ron file as if it
68
+ were a man page without building any
69
+ intermediate files:
70
+
71
+ $ ron -m man/markdown.5.ron
72
+
73
+ The ron(1) manual page included with this
74
+ distribution includes full documentation on
75
+ ron command line options.
76
+
77
+ RATIONALE
78
+ ---------
79
+
80
+ Some people think UNIX manual pages are a
81
+ poor and outdated form of documentation. I
82
+ disagree.
83
+
84
+ - Man pages typically follow a well defined
85
+ structure that's immediately familiar and
86
+ provides a useful starting point for
87
+ developers documenting new tools,
88
+ libraries, and formats.
89
+
90
+ - Man pages get to the point. Because they're
91
+ written in an inverted style, with a
92
+ SYNOPSIS section followed by additional
93
+ detail, prose, and finally references to
94
+ other sources of information, man pages
95
+ provide the best of both cheat sheet and
96
+ reference style documentation.
97
+
98
+ - Man pages have very limited text formatting
99
+ capabilities. This is a feature. You get
100
+ bold and underline, basically, and they're
101
+ typically applied consistently across man
102
+ pages.
103
+
104
+ - Most man pages use only a single level of
105
+ section hierarchy (although two levels are
106
+ technically supported). Hierarchy destroys
107
+ otherwise good documentation by adding
108
+ unnecessary complexity. Feynman described
109
+ the whole of quantum electro dynamics with
110
+ only two levels of hierarchy. How can you
111
+ possibly need more? Man pages force you to
112
+ keep it simple.
113
+
114
+ - Man pages have a simple referencing syntax;
115
+ e.g., sh(1), fork(2), markdown(5). HTML
116
+ versions can use this to generate links
117
+ between pages.
118
+
119
+ - The classical terminal man page display is
120
+ typographically well thought out. Big bold
121
+ section headings, justified monospaced
122
+ text, nicely indented paragraphs,
123
+ intelligently aligned definition lists, and
124
+ an informational header and footer.
125
+
126
+ All that being said, trying to figure out how
127
+ to create a man page can be a really tedious
128
+ process. The roff/man macro languages are
129
+ highly extensible, fractured between multiple
130
+ dialects, and include a bunch of stuff that's
131
+ entirely irrelevant to modern man page
132
+ creation. It's also horribly ugly compared to
133
+ today's humane text formats or even HTML
134
+ (just sayin').
135
+
136
+ Ron aims to address many of the issues with
137
+ man page creation while preserving the things
138
+ that makes man pages a great form of
139
+ documentation.
140
+
141
+ COPYRIGHT
142
+ ---------
143
+
144
+ Ron is Copyright (C) 2009 Ryan Tomayko
145
+ See the file COPYING for more information.
@@ -0,0 +1,73 @@
1
+ require 'rake/clean'
2
+ require 'rake/testtask'
3
+
4
+ task :default => :test
5
+ task :spec => :test
6
+
7
+ # SPECS ===============================================================
8
+
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.test_files = FileList['test/*_test.rb']
11
+ t.ruby_opts = ['-rubygems'] if defined? Gem
12
+ end
13
+
14
+ # PACKAGING ============================================================
15
+
16
+ require 'rubygems/specification'
17
+ $spec = eval(File.read('ron.gemspec'))
18
+
19
+ def package(ext='')
20
+ "pkg/ron-#{$spec.version}" + ext
21
+ end
22
+
23
+ desc 'Build packages'
24
+ task :package => %w[.gem .tar.gz].map { |ext| package(ext) }
25
+
26
+ desc 'Build and install as local gem'
27
+ task :install => package('.gem') do
28
+ sh "gem install #{package('.gem')}"
29
+ end
30
+
31
+ directory 'pkg/'
32
+ CLOBBER.include('pkg')
33
+
34
+ file package('.gem') => %w[pkg/ ron.gemspec] + $spec.files do |f|
35
+ sh "gem build ron.gemspec"
36
+ mv File.basename(f.name), f.name
37
+ end
38
+
39
+ file package('.tar.gz') => %w[pkg/] + $spec.files do |f|
40
+ sh <<-SH
41
+ git archive --prefix=ron-#{source_version}/ --format=tar HEAD |
42
+ gzip > #{f.name}
43
+ SH
44
+ end
45
+
46
+ # Gemspec Helpers ====================================================
47
+
48
+ def source_version
49
+ line = File.read('lib/ron.rb')[/^\s*VERSION = .*/]
50
+ line.match(/.*VERSION = '(.*)'/)[1]
51
+ end
52
+
53
+ file 'ron.gemspec' => FileList['{lib,test}/**','Rakefile'] do |f|
54
+ # read spec file and split out manifest section
55
+ spec = File.read(f.name)
56
+ head, manifest, tail = spec.split(" # = MANIFEST =\n")
57
+ # replace version and date
58
+ head.sub!(/\.version = '.*'/, ".version = '#{source_version}'")
59
+ head.sub!(/\.date = '.*'/, ".date = '#{Date.today.to_s}'")
60
+ # determine file list from git ls-files
61
+ files = `git ls-files`.
62
+ split("\n").
63
+ sort.
64
+ reject{ |file| file =~ /^\./ }.
65
+ reject { |file| file =~ /^doc/ }.
66
+ map{ |file| " #{file}" }.
67
+ join("\n")
68
+ # piece file back together and write...
69
+ manifest = " s.files = %w[\n#{files}\n ]\n"
70
+ spec = [head,manifest,tail].join(" # = MANIFEST =\n")
71
+ File.open(f.name, 'w') { |io| io.write(spec) }
72
+ puts "updated #{f.name}"
73
+ end
data/bin/ron ADDED
@@ -0,0 +1,94 @@
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 or html.
7
+ ##
8
+ ## Options
9
+ ## -b, --build write output to files instead of to stdout
10
+ ## -i, --install write manpage to MAN_HOME or system man path
11
+ ## -m, --man show man page like man(1)
12
+ ##
13
+ ## --roff generate roff/man text; this is the default behavior
14
+ ## -5, --html generate HTML
15
+ ##
16
+ ## --help show this help message
17
+ ##
18
+ ## See the ron(2)
19
+ require 'optparse'
20
+
21
+ formats = []
22
+ build = false
23
+ install = false
24
+ man = false
25
+ groff = "groff -Wall -mtty-char -mandoc -Tascii"
26
+ pager = ENV['MANPAGER'] || ENV['PAGER'] || 'more'
27
+
28
+ def info(message, *args)
29
+ STDERR.puts message % args
30
+ end
31
+
32
+ def usage
33
+ puts File.read(__FILE__).
34
+ grep(/^##.*/).
35
+ map { |line| line.chomp[3..-1] }.
36
+ join("\n")
37
+ end
38
+
39
+ # parse command line options
40
+ ARGV.options do |option|
41
+ option.on("--roff") { formats << 'roff' }
42
+ option.on("-5", "--html") { formats << 'html' }
43
+ option.on("-b", "--build") { build = true }
44
+ option.on("-i", "--install") { install = true }
45
+ option.on("-m", "--man") { man = true }
46
+
47
+ option.on_tail("--help") { usage ; exit }
48
+ option.parse!
49
+ end
50
+
51
+ if ARGV.empty? && STDIN.tty?
52
+ usage
53
+ exit
54
+ elsif ARGV.empty?
55
+ ARGV.push '-'
56
+ end
57
+
58
+ formats = ['roff'] if formats.empty?
59
+ pid = nil
60
+
61
+ require 'ron'
62
+ wr = STDOUT
63
+ ARGV.each do |file|
64
+ doc = Ron.new(file) { file == '-' ? STDIN.read : File.read(file) }
65
+
66
+ # setup the man pipeline if the --man option was specified
67
+ if man && !build
68
+ rd, wr = IO.pipe
69
+ if pid = fork
70
+ rd.close
71
+ else
72
+ wr.close
73
+ STDIN.reopen rd
74
+ exec "#{groff} | #{pager}"
75
+ end
76
+ end
77
+
78
+ # write output for each format
79
+ formats.each do |format|
80
+ output = doc.convert(format)
81
+ if build
82
+ path = doc.path(format)
83
+ info "building: #{path}"
84
+ File.open(path, 'wb') { |f| f.write(output) }
85
+ else
86
+ wr.write(output)
87
+ end
88
+ end
89
+
90
+ if pid
91
+ wr.close
92
+ Process.wait
93
+ end
94
+ end
@@ -0,0 +1,15 @@
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.1'
7
+
8
+ require 'ron/document'
9
+ require 'ron/roff'
10
+
11
+ # Create a new Ron::Document for the given ron file.
12
+ def self.new(filename, &block)
13
+ Document.new(filename, &block)
14
+ end
15
+ end
@@ -0,0 +1,117 @@
1
+ require 'nokogiri'
2
+ require 'rdiscount'
3
+ require 'ron/roff'
4
+
5
+ module Ron
6
+ class Document
7
+ VERSION = '0.1'
8
+ attr_reader :filename, :data, :basename, :name, :section, :tagline
9
+
10
+ def initialize(filename, &block)
11
+ @filename = filename
12
+ @reader = block || Proc.new { |f| File.read(f) }
13
+ @data = @reader.call(filename)
14
+
15
+ @basename = File.basename(filename)
16
+ @name, @section =
17
+ if @basename =~ /(\w+)\.(\d\w*)\.ron/
18
+ [$1, $2]
19
+ else
20
+ [@basename[/\w+/], nil]
21
+ end
22
+ end
23
+
24
+ # Construct a path to a file near the input file.
25
+ def path(extension=nil)
26
+ extension = nil if ['', 'roff'].include?(extension.to_s)
27
+ name = "#{@name}.#{section}"
28
+ name = "#{name}.#{extension}" if extension
29
+ File.join(File.dirname(filename), name)
30
+ end
31
+
32
+ # Convert the document to :roff or :html
33
+ def convert(format)
34
+ send "to_#{format}"
35
+ end
36
+
37
+ # Convert the document to roff.
38
+ def to_roff
39
+ RoffFilter.new(
40
+ to_html_fragment,
41
+ name,
42
+ section,
43
+ tagline
44
+ ).to_s
45
+ end
46
+
47
+ # Convert the document to HTML and return result
48
+ # as a string.
49
+ def to_html
50
+ layout_filter(to_html_fragment)
51
+ end
52
+
53
+ # Convert the document to HTML and return result
54
+ # as a string. The HTML does not include <html>, <head>,
55
+ # or <style> tags.
56
+ def to_html_fragment
57
+ definition_list_filter(markdown_filter(data))
58
+ end
59
+
60
+ # Apply the standard HTML layout template.
61
+ def layout_filter(html)
62
+ template_file = File.dirname(__FILE__) + "/layout.html"
63
+ template = File.read(template_file)
64
+ eval("%Q{#{template}}", binding, template_file)
65
+ end
66
+
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
+ # Convert special format unordered lists to definition lists.
86
+ def definition_list_filter(html)
87
+ doc = Nokogiri::HTML(html)
88
+
89
+ # process all unordered lists depth-first
90
+ doc.xpath('//ul').to_a.reverse.each do |ul|
91
+ items = ul.xpath('li')
92
+ next if items.any? { |item| item.text.split("\n", 2).first !~ /:$/ }
93
+
94
+ ul.name = 'dl'
95
+ items.each do |item|
96
+ if item.child.name == 'p'
97
+ wrap = '<p></p>'
98
+ container = item.child
99
+ else
100
+ wrap = '<dd></dd>'
101
+ container = item
102
+ end
103
+ term, definition = container.inner_html.split(":\n", 2)
104
+
105
+ dt = item.before("<dt>#{term}</dt>").previous_sibling
106
+ dt['class'] = 'flush' if dt.content.length <= 10
107
+
108
+ item.name = 'dd'
109
+ container.swap(wrap.sub(/></, ">#{definition}<"))
110
+ end
111
+ end
112
+
113
+ doc.css('html > body').inner_html
114
+ end
115
+
116
+ end
117
+ end