rtfm 0.5.0
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/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.markdown +97 -0
- data/Rakefile +89 -0
- data/VERSION +1 -0
- data/lib/rtfm.rb +20 -0
- data/lib/rtfm/groffstring.rb +45 -0
- data/lib/rtfm/manpage.rb +71 -0
- data/lib/rtfm/option.rb +37 -0
- data/lib/rtfm/sections/authors.rb +25 -0
- data/lib/rtfm/sections/description.rb +32 -0
- data/lib/rtfm/sections/see_also.rb +24 -0
- data/lib/rtfm/sections/synopsis.rb +41 -0
- data/lib/rtfm/sections/text.rb +10 -0
- data/lib/rtfm/tasks.rb +44 -0
- data/lib/rubygems_plugin.rb +171 -0
- data/man/testing.2 +54 -0
- data/spec/authors_spec.rb +23 -0
- data/spec/description_spec.rb +55 -0
- data/spec/groffstring_spec.rb +42 -0
- data/spec/rtfm_spec.rb +53 -0
- data/spec/see_also_spec.rb +35 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/synopsis_spec.rb +43 -0
- metadata +109 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Michael Edgar
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
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 OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
# rtfm
|
2
|
+
|
3
|
+
Using RTFM, you can declaratively create nice, standard man pages for your
|
4
|
+
Ruby projects using a slick, maintainable DSL. It also includes rake tasks
|
5
|
+
to aid in debugging, generating, and installing your man files.
|
6
|
+
|
7
|
+
## Normal manfiles are... interesting
|
8
|
+
|
9
|
+
You might say: "but wait, aren't man pages already written in a DSL?", and you'd
|
10
|
+
be right! However, here's an example of it:
|
11
|
+
|
12
|
+
.It Fl p
|
13
|
+
Acts mostly same as -n switch, but print the value of variable
|
14
|
+
.Li "$_"
|
15
|
+
at the each end of the loop. For example:
|
16
|
+
.Bd -literal -offset indent
|
17
|
+
% echo matz | ruby -p -e '$_.tr! "a-z", "A-Z"'
|
18
|
+
MATZ
|
19
|
+
.Ed
|
20
|
+
.Pp
|
21
|
+
.It Fl r Ar library
|
22
|
+
Causes Ruby to load the library using require. It is useful when using
|
23
|
+
.Fl n
|
24
|
+
or
|
25
|
+
.Fl p .
|
26
|
+
|
27
|
+
That's from the Ruby manfile. This is using the `groff` macro language,
|
28
|
+
which is awfully spiffy. However, it makes man files extremely tedious to
|
29
|
+
maintain. It even spits out errors if you put a blank line in your manfile!
|
30
|
+
|
31
|
+
## A word of caution
|
32
|
+
|
33
|
+
Now, manfiles are long and complex, and contain a lot of information. Chances
|
34
|
+
are, your RTFM-based manfile isn't actually going to be too much shorter than
|
35
|
+
your normal groff-based entry. The difference is that by using the RTFM
|
36
|
+
DSL, it should be very, very simple to maintain. Which is a huge
|
37
|
+
win for your users.
|
38
|
+
|
39
|
+
## Example Manual Page:
|
40
|
+
|
41
|
+
RTFM::ManPage.new("testing", 2) do |page|
|
42
|
+
page.summary = "testing man page"
|
43
|
+
page.option :verbose, "The verbose flag does a lot of stuff."
|
44
|
+
page.option :silliness, "Set how silly the application should be.", :argument => "n"
|
45
|
+
page.option :input, "The input flag takes a filename", :argument => "<input>"
|
46
|
+
|
47
|
+
page.description do |desc|
|
48
|
+
desc.body = "This is a small, temporary description of the testing " +
|
49
|
+
"man page."
|
50
|
+
end
|
51
|
+
page.see_also do |also|
|
52
|
+
also.reference "rails", 1
|
53
|
+
also.reference "ruby"
|
54
|
+
end
|
55
|
+
page.bugs = "There are a few bugs, but nothing too serious."
|
56
|
+
page.history = "This program has a storied history that I am too " +
|
57
|
+
"lazy to include here."
|
58
|
+
page.authors do |authors|
|
59
|
+
authors.add "Michael Edgar", "adgar@carboni.ca"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
## Other wins
|
64
|
+
|
65
|
+
The weird thing about making manfiles is that each section has its own semantics -
|
66
|
+
sometimes, macros even change their meanings slightly. There are idioms and "best practices"
|
67
|
+
for each. The cool part of RTFM is that we can try to match the idioms for you - you
|
68
|
+
just provide the information we need.
|
69
|
+
|
70
|
+
An example: in the "SEE ALSO" section, you can provide a list of other manual pages
|
71
|
+
that are related to yours. In the example above, you see a couple of simple references.
|
72
|
+
If you read the deep, dark documentation on the subject, you'll know that these references
|
73
|
+
should be sorted by manual section, and then alphabetically within sections. RTFM will do that
|
74
|
+
for you. It's the little things that count.
|
75
|
+
|
76
|
+
## Upcoming
|
77
|
+
|
78
|
+
Hopefully we can get some of this integrated into RubyGems, though that's a long shot -
|
79
|
+
it's a long-standing, cross-platform project. But it's nice to dream.
|
80
|
+
|
81
|
+
I hope to add the ability to insert raw groff if you are an advanced user and want some
|
82
|
+
really spiffy formatting.
|
83
|
+
|
84
|
+
## Note on Patches/Pull Requests
|
85
|
+
|
86
|
+
* Fork the project.
|
87
|
+
* Make your feature addition or bug fix.
|
88
|
+
* Add tests for it. This is important so I don't break it in a
|
89
|
+
future version unintentionally.
|
90
|
+
* Commit, do not mess with rakefile, version, or history.
|
91
|
+
(if you want to have your own version, that is fine but
|
92
|
+
bump version in a commit by itself I can ignore when I pull)
|
93
|
+
* Send me a pull request. Bonus points for topic branches.
|
94
|
+
|
95
|
+
## Copyright
|
96
|
+
|
97
|
+
Copyright (c) 2010 Michael Edgar. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "rtfm"
|
8
|
+
gem.summary = "Create makefiles declaratively. Comes with rake tasks."
|
9
|
+
gem.description = <<-EOF
|
10
|
+
Using RTFM, you can declaratively create nice, standard man pages for your
|
11
|
+
Ruby projects using a slick, maintainable DSL. It also includes rake tasks
|
12
|
+
to aid in debugging, generating, and installing your man files.
|
13
|
+
EOF
|
14
|
+
gem.email = "michael.j.edgar@dartmouth.edu"
|
15
|
+
gem.homepage = "http://github.com/michaeledgar/rtfm"
|
16
|
+
gem.authors = ["Michael Edgar"]
|
17
|
+
gem.add_development_dependency "bacon", ">= 0"
|
18
|
+
gem.add_development_dependency "yard", ">= 0"
|
19
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
20
|
+
end
|
21
|
+
Jeweler::GemcutterTasks.new
|
22
|
+
rescue LoadError
|
23
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
24
|
+
end
|
25
|
+
|
26
|
+
require 'rake/testtask'
|
27
|
+
Rake::TestTask.new(:spec) do |spec|
|
28
|
+
spec.libs << 'lib' << 'spec'
|
29
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
30
|
+
spec.verbose = true
|
31
|
+
end
|
32
|
+
|
33
|
+
begin
|
34
|
+
require 'rcov/rcovtask'
|
35
|
+
Rcov::RcovTask.new do |test|
|
36
|
+
test.libs << 'test'
|
37
|
+
test.pattern = 'test/**/test_*.rb'
|
38
|
+
test.verbose = true
|
39
|
+
end
|
40
|
+
rescue LoadError
|
41
|
+
task :rcov do
|
42
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
task :spec => :check_dependencies
|
47
|
+
|
48
|
+
task :default => :spec
|
49
|
+
|
50
|
+
begin
|
51
|
+
require 'yard'
|
52
|
+
YARD::Rake::YardocTask.new
|
53
|
+
rescue LoadError
|
54
|
+
task :yardoc do
|
55
|
+
abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
$:.unshift File.expand_path(File.join(File.dirname(__FILE__), "lib"))
|
60
|
+
require 'rtfm'
|
61
|
+
require 'rtfm/tasks'
|
62
|
+
|
63
|
+
RTFM::ManPage.new("testing", 2) do |page|
|
64
|
+
page.summary = "testing man page"
|
65
|
+
page.option :r, "Some r flag"
|
66
|
+
page.option :j, "Some j flag"
|
67
|
+
page.option :k, "Some k flag"
|
68
|
+
page.option :"0", "Some zero flag"
|
69
|
+
page.option :A, "Some capitalized flag"
|
70
|
+
page.option :Z, "some big-z flag"
|
71
|
+
page.option :verbose, "The verbose flag does a lot of stuff."
|
72
|
+
page.option :silliness, "Set how silly the application should be.", :argument => "n"
|
73
|
+
page.option :input, "The input flag takes a filename", :argument => "<input>"
|
74
|
+
|
75
|
+
page.description do |desc|
|
76
|
+
desc.body = "This is a small, temporary description of the testing " +
|
77
|
+
"man page."
|
78
|
+
end
|
79
|
+
page.see_also do |also|
|
80
|
+
also.reference "rails", 1
|
81
|
+
also.reference "ruby"
|
82
|
+
end
|
83
|
+
page.bugs = "There are a few bugs, but nothing too serious."
|
84
|
+
page.history = "This program has a storied history that I am too " +
|
85
|
+
"lazy to include here."
|
86
|
+
page.authors do |section|
|
87
|
+
section.author "Michael Edgar", "adgar@carboni.ca"
|
88
|
+
end
|
89
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.5.0
|
data/lib/rtfm.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
$:.unshift File.expand_path(File.dirname(__FILE__))
|
2
|
+
|
3
|
+
if RUBY_VERSION < "1.9"
|
4
|
+
class String
|
5
|
+
unless method_defined?(:ord)
|
6
|
+
def ord
|
7
|
+
self[0]
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
require 'rtfm/groffstring'
|
14
|
+
require 'rtfm/option'
|
15
|
+
|
16
|
+
Dir[File.expand_path(File.join(File.dirname(__FILE__), "rtfm", "sections", "**"))].each do |f|
|
17
|
+
require f
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'rtfm/manpage'
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module RTFM
|
2
|
+
class GroffString
|
3
|
+
|
4
|
+
attr_reader :source
|
5
|
+
|
6
|
+
def self.groffify(str = "")
|
7
|
+
out = self.new(str)
|
8
|
+
yield out
|
9
|
+
out.to_s
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(str = "")
|
13
|
+
@source = str.dup
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_s
|
17
|
+
source
|
18
|
+
end
|
19
|
+
|
20
|
+
def rstrip
|
21
|
+
source.rstrip
|
22
|
+
end
|
23
|
+
|
24
|
+
def add_line(line)
|
25
|
+
source << line.rstrip << "\n"
|
26
|
+
end
|
27
|
+
alias_method :<<, :add_line
|
28
|
+
|
29
|
+
def section(section)
|
30
|
+
self.Sh section.upcase
|
31
|
+
end
|
32
|
+
|
33
|
+
def put_name
|
34
|
+
self.Nm
|
35
|
+
end
|
36
|
+
|
37
|
+
def reference(page, section = nil)
|
38
|
+
self.Xr page, (section || "")
|
39
|
+
end
|
40
|
+
|
41
|
+
def method_missing(meth, *args, &block)
|
42
|
+
add_line ".#{meth} #{args.join(" ")}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/rtfm/manpage.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
module RTFM
|
2
|
+
class ManPage < Struct.new(:name, :section, :date, :summary)
|
3
|
+
class << self
|
4
|
+
def text_section(*args)
|
5
|
+
args.each do |sect|
|
6
|
+
class_eval %Q{
|
7
|
+
def #{sect}
|
8
|
+
@#{sect} ||= TextSection.new(:#{sect}, "")
|
9
|
+
end
|
10
|
+
def #{sect}=(str)
|
11
|
+
@#{sect} = TextSection.new(:#{sect}, str)
|
12
|
+
end
|
13
|
+
}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
alias_method :text_sections, :text_section
|
17
|
+
|
18
|
+
def add_section(name, klass)
|
19
|
+
klass = klass.to_s.intern
|
20
|
+
class_eval %Q{
|
21
|
+
def #{name}
|
22
|
+
@#{name} ||= #{klass}.new
|
23
|
+
yield @#{name} if block_given?
|
24
|
+
@#{name}
|
25
|
+
end
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
def all_pages
|
30
|
+
@pages ||= []
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
text_section :bugs, :diagnostics, :compatibility, :standards, :history
|
35
|
+
add_section :see_also, SeeAlsoSection
|
36
|
+
add_section :description, DescriptionSection
|
37
|
+
add_section :authors, AuthorsSection
|
38
|
+
add_section :synopsis, SynopsisSection
|
39
|
+
|
40
|
+
def initialize(name, section=nil)
|
41
|
+
self.class.all_pages << self
|
42
|
+
self.name, self.section = name, section
|
43
|
+
self.date = DateTime.now
|
44
|
+
yield self
|
45
|
+
end
|
46
|
+
|
47
|
+
def add_option(name, desc, opts={})
|
48
|
+
opt = Option.new(name, desc, opts)
|
49
|
+
description.add_option opt
|
50
|
+
unless opts.has_key?(:synopsis) && !opts.delete(:synopsis)
|
51
|
+
synopsis.add_option(opt)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
alias_method :option, :add_option
|
55
|
+
|
56
|
+
def to_groff
|
57
|
+
GroffString.groffify do |out|
|
58
|
+
out.Dd date.strftime("%B %d, %Y")
|
59
|
+
out.Os
|
60
|
+
out.Dt name, (section || "")
|
61
|
+
out.section "NAME"
|
62
|
+
out.Nm name
|
63
|
+
out.Nd summary
|
64
|
+
[@synopsis, @description, @see_also, @history, @authors, @bugs].each do |sect|
|
65
|
+
out << sect.to_groff if sect
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
data/lib/rtfm/option.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
module RTFM
|
2
|
+
class Option < Struct.new(:title, :desc, :opts)
|
3
|
+
def title
|
4
|
+
super.to_s
|
5
|
+
end
|
6
|
+
|
7
|
+
def argument
|
8
|
+
opts && (opts[:argument] || opts[:arg])
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_groff(how)
|
12
|
+
GroffString.groffify do |out|
|
13
|
+
args = [:Fl, self.title]
|
14
|
+
|
15
|
+
if self.argument
|
16
|
+
argument = self.argument.to_s
|
17
|
+
if argument[0,1] == "<" && argument[-1,1] == ">"
|
18
|
+
args << "Ao" << argument[1..-2] << "Ac"
|
19
|
+
elsif argument[0,1] == "[" && argument[-1,1] == "]"
|
20
|
+
args << "Oo" << argument[1..-2] << "Oc"
|
21
|
+
else
|
22
|
+
args << :Ar << argument
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
case how
|
27
|
+
when :option
|
28
|
+
out.Op *args
|
29
|
+
when :item
|
30
|
+
out.Pp
|
31
|
+
out.It *args
|
32
|
+
out << self.desc
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module RTFM
|
2
|
+
class AuthorsSection
|
3
|
+
def initialize
|
4
|
+
@authors = []
|
5
|
+
yield self if block_given?
|
6
|
+
end
|
7
|
+
def add_author(name, email = nil)
|
8
|
+
@authors << {:name => name, :email => email}
|
9
|
+
end
|
10
|
+
alias_method :author, :add_author
|
11
|
+
alias_method :add, :add_author
|
12
|
+
|
13
|
+
def to_groff
|
14
|
+
GroffString.groffify do |out|
|
15
|
+
out.section "authors"
|
16
|
+
@authors.each do |author|
|
17
|
+
args = ["\"#{author[:name]}\""]
|
18
|
+
if author[:email] then args << "Aq" << author[:email] end
|
19
|
+
out.An *args
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module RTFM
|
2
|
+
class DescriptionSection < Struct.new(:body, :options)
|
3
|
+
def initialize(*args)
|
4
|
+
super
|
5
|
+
self.options ||= []
|
6
|
+
yield self if block_given?
|
7
|
+
end
|
8
|
+
|
9
|
+
def add_option(*args)
|
10
|
+
if args.size == 1 && args.first.is_a?(Option)
|
11
|
+
then self.options << args.first
|
12
|
+
else self.options << Option.new(*args)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
alias_method :option, :add_option
|
16
|
+
|
17
|
+
def to_groff
|
18
|
+
GroffString.groffify do |out|
|
19
|
+
out.section "description"
|
20
|
+
out << self.body
|
21
|
+
if options.any?
|
22
|
+
out.Bl "-tag", "-width", "\"mmmmmmmmmm\"", "-compact"
|
23
|
+
options.each do |option|
|
24
|
+
out << option.to_groff(:item)
|
25
|
+
end
|
26
|
+
out.El
|
27
|
+
out.Pp
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module RTFM
|
2
|
+
class SeeAlsoSection
|
3
|
+
def initialize
|
4
|
+
@references = {}
|
5
|
+
yield self if block_given?
|
6
|
+
end
|
7
|
+
def reference(title, section = 0)
|
8
|
+
(@references[section] ||= []) << title
|
9
|
+
end
|
10
|
+
def to_groff
|
11
|
+
GroffString.groffify do |out|
|
12
|
+
out.section "SEE ALSO"
|
13
|
+
@references.keys.sort.each do |section|
|
14
|
+
@references[section].sort.each do |title|
|
15
|
+
if section == 0
|
16
|
+
then out.reference title
|
17
|
+
else out.reference title, section
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module RTFM
|
2
|
+
class SynopsisSection
|
3
|
+
def initialize
|
4
|
+
@options = []
|
5
|
+
yield self if block_given?
|
6
|
+
end
|
7
|
+
|
8
|
+
def add_option(*args)
|
9
|
+
if args.size == 1 && args.first.is_a?(Option)
|
10
|
+
then @options << args.first
|
11
|
+
else @options << Option.new(*args)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
alias_method :option, :add_option
|
15
|
+
|
16
|
+
def compare_flags(a, b)
|
17
|
+
a_ord, b_ord = a.ord, b.ord
|
18
|
+
if ('0'.ord .. '9'.ord).include?(a_ord)
|
19
|
+
a_ord += 'z'.ord
|
20
|
+
end
|
21
|
+
if ('0'.ord .. '9'.ord).include?(b_ord)
|
22
|
+
b_ord += 'z'.ord
|
23
|
+
end
|
24
|
+
a_ord <=> b_ord
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_groff
|
28
|
+
flags = @options.select {|opt| opt.title.size == 1 && !opt.argument}
|
29
|
+
long_args = @options - flags
|
30
|
+
|
31
|
+
GroffString.groffify do |out|
|
32
|
+
out.section "synopsis"
|
33
|
+
out.put_name
|
34
|
+
out.Op "Fl", flags.map {|flag| flag.title}.sort {|a, b| compare_flags(a,b)}.join
|
35
|
+
long_args.each do |opt|
|
36
|
+
out << opt.to_groff(:option)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/rtfm/tasks.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rtfm'
|
3
|
+
|
4
|
+
namespace :man do
|
5
|
+
desc "Display your man files in man."
|
6
|
+
task :debug do
|
7
|
+
require 'tempfile'
|
8
|
+
require 'rtfm'
|
9
|
+
RTFM::ManPage.all_pages.each do |page|
|
10
|
+
Tempfile.open("#{page.name}.#{page.section}") do |f|
|
11
|
+
f << page.to_groff
|
12
|
+
f.flush
|
13
|
+
system("man #{f.path}")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
desc "Generate your manual files in the man/ directory"
|
19
|
+
task :gen do
|
20
|
+
FileUtils.makedirs "man/"
|
21
|
+
RTFM::ManPage.all_pages.each do |page|
|
22
|
+
File.open("man/#{page.name}.#{page.section}", "w") do |f|
|
23
|
+
f << page.to_groff
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "Install your manual files globally"
|
29
|
+
task :install => :gen do
|
30
|
+
if Rake.application.unix?
|
31
|
+
Dir["man/*"].each do |manfile|
|
32
|
+
section = manfile.split(".").last
|
33
|
+
begin
|
34
|
+
FileUtils.cp(manfile, "/usr/share/man/man#{section}/")
|
35
|
+
rescue Errno::EACCES
|
36
|
+
puts "I'm sorry, but you need root privileges to install man pages with this version"+
|
37
|
+
" of RTFM."
|
38
|
+
end
|
39
|
+
end
|
40
|
+
else
|
41
|
+
raise "Can't install man pages without a unix-based OS."
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
require 'rubygems/command_manager'
|
2
|
+
|
3
|
+
require 'rubygems/command'
|
4
|
+
require 'rubygems_analyzer'
|
5
|
+
|
6
|
+
class Gem::Commands::ManCommand < Gem::Command
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
super 'man', 'Manage man-files bundled with gems'
|
10
|
+
options[:action] = :install
|
11
|
+
|
12
|
+
add_option('-v', '--view', "Views the manual files included in",
|
13
|
+
"the gem.") do |value, options|
|
14
|
+
options[:action] = :view
|
15
|
+
end
|
16
|
+
|
17
|
+
add_option('-i', '--install', "Installs all the gem's manual files globally") do |value, options|
|
18
|
+
options[:action] = :install
|
19
|
+
end
|
20
|
+
|
21
|
+
add_option('-r', '--remove', "Removes the gem's manual files globally") do |value, options|
|
22
|
+
options[:action] = :remove
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def execute
|
27
|
+
if Gem.win_platform? || !has_man?
|
28
|
+
alert_error "You must have the 'man' command to use this extension."
|
29
|
+
return
|
30
|
+
end
|
31
|
+
|
32
|
+
get_all_gem_names.each do |name|
|
33
|
+
path = get_path name, options[:version]
|
34
|
+
if path then
|
35
|
+
man_path = File.join path, 'man'
|
36
|
+
if File.exist?(man_path) && File.directory?(man_path) then
|
37
|
+
Dir[File.join man_path, "**"].each do |man_file|
|
38
|
+
dispatch(man_file)
|
39
|
+
end
|
40
|
+
else
|
41
|
+
alert_error "Gem '#{name}' does not appear to have packaged man files."
|
42
|
+
end
|
43
|
+
else
|
44
|
+
alert_error "Gem '#{name}' not installed."
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
def dispatch(file)
|
51
|
+
case options[:action]
|
52
|
+
when :install
|
53
|
+
install(file)
|
54
|
+
when :view
|
55
|
+
view(file)
|
56
|
+
when :remove
|
57
|
+
remove(file)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
MAN_DIR = "/usr/share/man/"
|
63
|
+
|
64
|
+
##
|
65
|
+
# Views the man file in the man program
|
66
|
+
def view(source)
|
67
|
+
system("man #{source}")
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# Installs the given file into the appropriate man directory.
|
72
|
+
def install(source)
|
73
|
+
full_name = File.split(source).last
|
74
|
+
section = full_name.split(".").last
|
75
|
+
destination = File.join MAN_DIR, "man#{section}", full_name
|
76
|
+
File.open(destination, "wb") do |out|
|
77
|
+
out << watermark_file(source)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
##
|
82
|
+
# Installs the given file into the appropriate man directory.
|
83
|
+
def remove(source)
|
84
|
+
full_name = File.split(source).last
|
85
|
+
section = full_name.split(".").last
|
86
|
+
destination = File.join MAN_DIR, "man#{section}", full_name
|
87
|
+
if is_watermarked_file?(destination)
|
88
|
+
FileUtils.unlink(destination)
|
89
|
+
else
|
90
|
+
alert_error "The man file at #{destination} was not installed by Rubygems. It has "+
|
91
|
+
"not been deleted."
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def watermark
|
96
|
+
%Q{.\\" Installed by Rubygems' Man Extension\n}
|
97
|
+
end
|
98
|
+
|
99
|
+
##
|
100
|
+
# Watermarks a man page. Assumes input file is not already compressed.
|
101
|
+
def watermark_file(file)
|
102
|
+
add_watermark(File.read(file))
|
103
|
+
end
|
104
|
+
|
105
|
+
##
|
106
|
+
# Watermarks some groff text, so you can tell it's been rubygem'd
|
107
|
+
def add_watermark(text)
|
108
|
+
"#{watermark}#{text}"
|
109
|
+
end
|
110
|
+
|
111
|
+
##
|
112
|
+
# Watermarks a man page. Assumes input file is not already compressed.
|
113
|
+
def is_watermarked_file?(file)
|
114
|
+
is_watermarked?(File.read(file))
|
115
|
+
end
|
116
|
+
|
117
|
+
##
|
118
|
+
# Is the given text watermarked?
|
119
|
+
def is_watermarked?(text)
|
120
|
+
text[0..(watermark.size-1)] == watermark
|
121
|
+
end
|
122
|
+
|
123
|
+
# Return the full path to the cached gem file matching the given
|
124
|
+
# name and version requirement. Returns 'nil' if no match.
|
125
|
+
#
|
126
|
+
# Example:
|
127
|
+
#
|
128
|
+
# get_path('rake', '> 0.4') # -> '/usr/lib/ruby/gems/1.8/cache/rake-0.4.2.gem'
|
129
|
+
# get_path('rake', '< 0.1') # -> nil
|
130
|
+
# get_path('rak') # -> nil (exact name required)
|
131
|
+
#--
|
132
|
+
# TODO: This should be refactored so that it's a general service. I don't
|
133
|
+
# think any of our existing classes are the right place though. Just maybe
|
134
|
+
# 'Cache'?
|
135
|
+
#
|
136
|
+
# TODO: It just uses Gem.dir for now. What's an easy way to get the list of
|
137
|
+
# source directories?
|
138
|
+
def get_path(gemname, version_req)
|
139
|
+
return gemname if gemname =~ /\.gem$/i
|
140
|
+
|
141
|
+
specs = Gem::source_index.find_name gemname, version_req
|
142
|
+
|
143
|
+
selected = specs.sort_by { |s| s.version }.last
|
144
|
+
|
145
|
+
return nil if selected.nil?
|
146
|
+
|
147
|
+
# We expect to find (basename).gem in the 'cache' directory.
|
148
|
+
# Furthermore, the name match must be exact (ignoring case).
|
149
|
+
if gemname =~ /^#{selected.name}$/i
|
150
|
+
filename = selected.full_name
|
151
|
+
path = nil
|
152
|
+
|
153
|
+
Gem.path.find do |gem_dir|
|
154
|
+
path = File.join gem_dir, 'gems', filename
|
155
|
+
File.exist? path
|
156
|
+
end
|
157
|
+
|
158
|
+
path
|
159
|
+
else
|
160
|
+
nil
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def has_man?
|
165
|
+
system("man 1>/dev/null 2>&1")
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
169
|
+
|
170
|
+
Gem::CommandManager.instance.register_command :man
|
171
|
+
|
data/man/testing.2
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
.\" Hello it's a comment
|
2
|
+
.Dd January 09, 2010
|
3
|
+
.Os
|
4
|
+
.Dt testing 2
|
5
|
+
.Sh NAME
|
6
|
+
.Nm testing
|
7
|
+
.Nd testing man page
|
8
|
+
.Sh SYNOPSIS
|
9
|
+
.Nm
|
10
|
+
.Op Fl AZjkr0
|
11
|
+
.Op Fl verbose
|
12
|
+
.Op Fl silliness Ar n
|
13
|
+
.Op Fl input Ao input Ac
|
14
|
+
.Sh DESCRIPTION
|
15
|
+
This is a small, temporary description of the testing man page.
|
16
|
+
.Bl -tag -width "mmmmmmmmmm" -compact
|
17
|
+
.Pp
|
18
|
+
.It Fl r
|
19
|
+
Some r flag
|
20
|
+
.Pp
|
21
|
+
.It Fl j
|
22
|
+
Some j flag
|
23
|
+
.Pp
|
24
|
+
.It Fl k
|
25
|
+
Some k flag
|
26
|
+
.Pp
|
27
|
+
.It Fl 0
|
28
|
+
Some zero flag
|
29
|
+
.Pp
|
30
|
+
.It Fl A
|
31
|
+
Some capitalized flag
|
32
|
+
.Pp
|
33
|
+
.It Fl Z
|
34
|
+
some big-z flag
|
35
|
+
.Pp
|
36
|
+
.It Fl verbose
|
37
|
+
The verbose flag does a lot of stuff.
|
38
|
+
.Pp
|
39
|
+
.It Fl silliness Ar n
|
40
|
+
Set how silly the application should be.
|
41
|
+
.Pp
|
42
|
+
.It Fl input Ao input Ac
|
43
|
+
The input flag takes a filename
|
44
|
+
.El
|
45
|
+
.Pp
|
46
|
+
.Sh SEE ALSO
|
47
|
+
.Xr ruby
|
48
|
+
.Xr rails 1
|
49
|
+
.Sh HISTORY
|
50
|
+
This program has a storied history that I am too lazy to include here.
|
51
|
+
.Sh AUTHORS
|
52
|
+
.An "Michael Edgar" Aq adgar@carboni.ca
|
53
|
+
.Sh BUGS
|
54
|
+
There are a few bugs, but nothing too serious.
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "authors section" do
|
4
|
+
before do
|
5
|
+
@authors = AuthorsSection.new do |sect|
|
6
|
+
sect.author "Michael Edgar"
|
7
|
+
sect.author "Ari Brown", "seydar@carboni.ca"
|
8
|
+
end
|
9
|
+
@groffed = @authors.to_groff
|
10
|
+
end
|
11
|
+
|
12
|
+
it "makes an authors section" do
|
13
|
+
@groffed.should.match(/^\.Sh AUTHORS$/)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "adds an author without an email" do
|
17
|
+
@groffed.should.match(/^\.An "Michael Edgar"$/)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "adds an author with an email" do
|
21
|
+
@groffed.should.match(/^\.An "Ari Brown" Aq seydar@carboni.ca/)
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "description section" do
|
4
|
+
|
5
|
+
FULL_DESC = "This is the description for the description section. Typically, this would" +
|
6
|
+
" be much longer, but for now, it is short. Huzzah!"
|
7
|
+
|
8
|
+
before do
|
9
|
+
@desc = DescriptionSection.new do |desc|
|
10
|
+
desc.body = FULL_DESC
|
11
|
+
desc.option :verbose, "Makes output wordy and unnecessarily long"
|
12
|
+
desc.option :input, "Specifies an input file", :arg => "<file>"
|
13
|
+
desc.option :output, "Specifies an output file", :argument => "[output]"
|
14
|
+
desc.option :silly, "Has a silly argument", :arg => "silliness"
|
15
|
+
end
|
16
|
+
@groffed = @desc.to_groff
|
17
|
+
end
|
18
|
+
|
19
|
+
it "creates a description section" do
|
20
|
+
@groffed.should.match(/^\.Sh DESCRIPTION$/)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "displays its body below the section header" do
|
24
|
+
@groffed.should.match(/^\.Sh DESCRIPTION\nThis is the/)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "contains the full body text" do
|
28
|
+
@groffed.should.include(FULL_DESC)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "creates a list for options" do
|
32
|
+
@groffed.should.match(/^\.Bl -tag/)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "creates list items with option names" do
|
36
|
+
@groffed.should.match(/^\.It Fl verbose/)
|
37
|
+
@groffed.should.match(/^\.It Fl input/)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "displays arguments for options" do
|
41
|
+
@groffed.should.match(/^\.It Fl silly Ar silliness/)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "changes arguments with angle brackets to proper groff" do
|
45
|
+
@groffed.should.match(/Ao file Ac/)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "changes arguments with square brackets to proper groff" do
|
49
|
+
@groffed.should.match(/Oo output Oc/)
|
50
|
+
end
|
51
|
+
it "puts option descriptions next to option names" do
|
52
|
+
@groffed.should.match(/^\.It Fl verbose\nMakes output wordy/)
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "groff_string" do
|
4
|
+
before do
|
5
|
+
@groff = GroffString.new
|
6
|
+
@groff_with_line = GroffString.new("Some text")
|
7
|
+
end
|
8
|
+
|
9
|
+
it "uses its initial value" do
|
10
|
+
@groff.to_s.should.equal ""
|
11
|
+
@groff_with_line.to_s.should.equal("Some text")
|
12
|
+
end
|
13
|
+
|
14
|
+
it "converts to a string" do
|
15
|
+
@groff.should.respond_to(:to_s)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "adds references using the .Xr macro" do
|
19
|
+
@groff.reference("rails", 1)
|
20
|
+
@groff.to_s.should.include(".Xr rails 1")
|
21
|
+
end
|
22
|
+
|
23
|
+
it "generates sections" do
|
24
|
+
@groff.section("NAME")
|
25
|
+
@groff.to_s.should.include(".Sh NAME")
|
26
|
+
end
|
27
|
+
|
28
|
+
it "generates arbitrary macros" do
|
29
|
+
@groff.Br("some", "arguments", :here)
|
30
|
+
@groff.to_s.should.include(".Br some arguments here")
|
31
|
+
end
|
32
|
+
|
33
|
+
it "adds lines using add_line" do
|
34
|
+
@groff.add_line("some arbitrary line goes here")
|
35
|
+
@groff.to_s.should.include("some arbitrary line goes here\n")
|
36
|
+
end
|
37
|
+
|
38
|
+
it "adds lines with <<" do
|
39
|
+
@groff << "werd some line"
|
40
|
+
@groff.to_s.should.include("werd some line\n")
|
41
|
+
end
|
42
|
+
end
|
data/spec/rtfm_spec.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "rtfm" do
|
4
|
+
before do
|
5
|
+
@rtfm = ManPage.new("testing", 2) do |page|
|
6
|
+
page.date = Date.parse("1/2/2010")
|
7
|
+
page.summary = "testing man page"
|
8
|
+
page.see_also do |also|
|
9
|
+
also.reference "madeup", 4
|
10
|
+
also.reference "rails", 1
|
11
|
+
also.reference "ruby"
|
12
|
+
also.reference "perl", 1
|
13
|
+
end
|
14
|
+
page.bugs = "There are a few bugs, but nothing too serious."
|
15
|
+
page.history = "This program has a storied history that I am too lazy to include here."
|
16
|
+
end
|
17
|
+
@groff = @rtfm.to_groff
|
18
|
+
end
|
19
|
+
|
20
|
+
it "displays the correct date" do
|
21
|
+
@groff.should.match(/^\.Dd January 02, 2010$/)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "has a NAME section" do
|
25
|
+
@groff.should.match(/^\.Sh NAME$/)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "generates a name line" do
|
29
|
+
@groff.should.match(/^\.Nm testing/)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "generates a summary line" do
|
33
|
+
@groff.should.match(/^\.Nd testing man page/)
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "rtfm-sections" do
|
37
|
+
it "has a bugs section" do
|
38
|
+
@groff.should.match(/^\.Sh BUGS$/)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "includes its bugs text" do
|
42
|
+
@groff.should.match(/^There are a few bugs/)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "has a history section" do
|
46
|
+
@groff.should.match(/^\.Sh HISTORY$/)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "includes its history text" do
|
50
|
+
@groff.should.match(/^This program has a storied history/)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "see also section" do
|
4
|
+
before do
|
5
|
+
@see_also = SeeAlsoSection.new do |also|
|
6
|
+
also.reference "madeup", 4
|
7
|
+
also.reference "rails", 1
|
8
|
+
also.reference "ruby"
|
9
|
+
also.reference "perl", 1
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
it "has a see also section" do
|
14
|
+
@see_also.to_groff.should.match(/^\.Sh SEE ALSO/)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "includes references in its see also section" do
|
18
|
+
groffed = @see_also.to_groff
|
19
|
+
groffed.should.match(/^\.Xr rails 1$/)
|
20
|
+
groffed.should.match(/^\.Xr ruby$/)
|
21
|
+
groffed.should.match(/^\.Xr perl 1$/)
|
22
|
+
groffed.should.match(/^\.Xr madeup 4$/)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "sorts by section" do
|
26
|
+
groffed = @see_also.to_groff
|
27
|
+
groffed.index(/^\.Xr ruby$/).should.be < groffed.index(/^\.Xr rails 1$/)
|
28
|
+
groffed.index(/^\.Xr rails 1$/).should.be < groffed.index(/^\.Xr madeup 4$/)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "sorts within sections" do
|
32
|
+
groffed = @see_also.to_groff
|
33
|
+
groffed.index(/^\.Xr perl 1$/).should.be < groffed.index(/^\.Xr rails 1$/)
|
34
|
+
end
|
35
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "synopsis section" do
|
4
|
+
|
5
|
+
before do
|
6
|
+
@synopsis = SynopsisSection.new do |syno|
|
7
|
+
syno.option :r, "Some r flag"
|
8
|
+
syno.option :j, "Some j flag"
|
9
|
+
syno.option :k, "Some k flag"
|
10
|
+
syno.option :"0", "Some zero flag"
|
11
|
+
syno.option :A, "Some capitalized flag"
|
12
|
+
syno.option :Z, "some big-z flag"
|
13
|
+
syno.option :verbose, "The verbose flag does a lot of stuff."
|
14
|
+
syno.option :silliness, "Set how silly the application should be.", :argument => "n"
|
15
|
+
syno.option :input, "The input flag takes a filename", :argument => "<input>"
|
16
|
+
end
|
17
|
+
@groffed = @synopsis.to_groff
|
18
|
+
end
|
19
|
+
|
20
|
+
it "contains the name of the man page" do
|
21
|
+
@groffed.should.match(/^\.Nm$/)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "displays long-form flags" do
|
25
|
+
@groffed.should.match(/^\.Op Fl verbose$/)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "combines short-form flags into one entry" do
|
29
|
+
@groffed.should.match(/^\.Op Fl ([rjk0AZ]{6})/)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "sorts short-form flags within their entry" do
|
33
|
+
@groffed.should.match(/AZjkr0/)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "encodes < and > in option arguments" do
|
37
|
+
@groffed.should.match(/Ao input Ac/)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "underlines arguments" do
|
41
|
+
@groffed.should.match(/\.Op Fl silliness Ar n/)
|
42
|
+
end
|
43
|
+
end
|
metadata
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rtfm
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Michael Edgar
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-01-10 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: bacon
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: yard
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
description: |
|
36
|
+
Using RTFM, you can declaratively create nice, standard man pages for your
|
37
|
+
Ruby projects using a slick, maintainable DSL. It also includes rake tasks
|
38
|
+
to aid in debugging, generating, and installing your man files.
|
39
|
+
|
40
|
+
email: michael.j.edgar@dartmouth.edu
|
41
|
+
executables: []
|
42
|
+
|
43
|
+
extensions: []
|
44
|
+
|
45
|
+
extra_rdoc_files:
|
46
|
+
- LICENSE
|
47
|
+
- README.markdown
|
48
|
+
files:
|
49
|
+
- .document
|
50
|
+
- .gitignore
|
51
|
+
- LICENSE
|
52
|
+
- README.markdown
|
53
|
+
- Rakefile
|
54
|
+
- VERSION
|
55
|
+
- lib/rtfm.rb
|
56
|
+
- lib/rtfm/groffstring.rb
|
57
|
+
- lib/rtfm/manpage.rb
|
58
|
+
- lib/rtfm/option.rb
|
59
|
+
- lib/rtfm/sections/authors.rb
|
60
|
+
- lib/rtfm/sections/description.rb
|
61
|
+
- lib/rtfm/sections/see_also.rb
|
62
|
+
- lib/rtfm/sections/synopsis.rb
|
63
|
+
- lib/rtfm/sections/text.rb
|
64
|
+
- lib/rtfm/tasks.rb
|
65
|
+
- lib/rubygems_plugin.rb
|
66
|
+
- man/testing.2
|
67
|
+
- spec/authors_spec.rb
|
68
|
+
- spec/description_spec.rb
|
69
|
+
- spec/groffstring_spec.rb
|
70
|
+
- spec/rtfm_spec.rb
|
71
|
+
- spec/see_also_spec.rb
|
72
|
+
- spec/spec_helper.rb
|
73
|
+
- spec/synopsis_spec.rb
|
74
|
+
has_rdoc: true
|
75
|
+
homepage: http://github.com/michaeledgar/rtfm
|
76
|
+
licenses: []
|
77
|
+
|
78
|
+
post_install_message:
|
79
|
+
rdoc_options:
|
80
|
+
- --charset=UTF-8
|
81
|
+
require_paths:
|
82
|
+
- lib
|
83
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: "0"
|
88
|
+
version:
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: "0"
|
94
|
+
version:
|
95
|
+
requirements: []
|
96
|
+
|
97
|
+
rubyforge_project:
|
98
|
+
rubygems_version: 1.3.5
|
99
|
+
signing_key:
|
100
|
+
specification_version: 3
|
101
|
+
summary: Create makefiles declaratively. Comes with rake tasks.
|
102
|
+
test_files:
|
103
|
+
- spec/authors_spec.rb
|
104
|
+
- spec/description_spec.rb
|
105
|
+
- spec/groffstring_spec.rb
|
106
|
+
- spec/rtfm_spec.rb
|
107
|
+
- spec/see_also_spec.rb
|
108
|
+
- spec/spec_helper.rb
|
109
|
+
- spec/synopsis_spec.rb
|