ron 0.1

Sign up to get free protection for your applications and to get access to all the features.
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