hanna 0.1.12
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +102 -0
- data/Rakefile +4 -0
- data/bin/hanna +81 -0
- data/lib/hanna.rb +1 -0
- data/lib/hanna/hanna.rb +48 -0
- data/lib/hanna/rdoctask.rb +42 -0
- data/lib/hanna/template_files/class_index.haml +3 -0
- data/lib/hanna/template_files/file_index.haml +12 -0
- data/lib/hanna/template_files/index.haml +11 -0
- data/lib/hanna/template_files/layout.haml +34 -0
- data/lib/hanna/template_files/method_index.haml +13 -0
- data/lib/hanna/template_files/method_list.haml +37 -0
- data/lib/hanna/template_files/method_search.js +63 -0
- data/lib/hanna/template_files/page.haml +50 -0
- data/lib/hanna/template_files/prototype-1.6.0.3.js +4320 -0
- data/lib/hanna/template_files/sections.haml +83 -0
- data/lib/hanna/template_files/styles.sass +364 -0
- data/lib/hanna/template_helpers.rb +119 -0
- data/lib/hanna/template_page_patch.rb +38 -0
- data/lib/hanna/version.rb +19 -0
- data/lib/rubygems_plugin.rb +28 -0
- metadata +104 -0
data/README.markdown
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
# Hanna — a better RDoc template
|
2
|
+
|
3
|
+
Hanna is an RDoc template that scales. It's implemented in Haml, making the sources clean
|
4
|
+
and readable. It's built with simplicity, beauty and ease of browsing in mind. (See more
|
5
|
+
in [the wiki][wiki].)
|
6
|
+
|
7
|
+
Hanna gem is available from [Gemcutter][]:
|
8
|
+
|
9
|
+
gem install hanna
|
10
|
+
|
11
|
+
The template was created by [Mislav][] and since then has seen contributions from:
|
12
|
+
|
13
|
+
1. [Tony Strauss](http://github.com/DesigningPatterns), who participated from the early
|
14
|
+
start and made tons of fixes and enhancements to the template;
|
15
|
+
2. [Hongli Lai](http://blog.phusion.nl/) with the search filter for methods.
|
16
|
+
|
17
|
+
|
18
|
+
## Usage
|
19
|
+
|
20
|
+
There is a command-line tool installed with the Hanna gem:
|
21
|
+
|
22
|
+
hanna -h
|
23
|
+
|
24
|
+
This is a wrapper over `rdoc` and it forwards all the parameters to it. Manual usage
|
25
|
+
would require specifying Hanna as a template when invoking RDoc on the command-line:
|
26
|
+
|
27
|
+
rdoc -o doc --inline-source --format=html -T hanna lib/*.rb
|
28
|
+
|
29
|
+
Hanna requires the `--inline-source` (or `-S`) flag.
|
30
|
+
|
31
|
+
An alternative is to set the `RDOCOPT` environment variable:
|
32
|
+
|
33
|
+
RDOCOPT="-S -f html -T hanna"
|
34
|
+
|
35
|
+
This will make RDoc always use Hanna unless it is explicitly overridden.
|
36
|
+
|
37
|
+
Another neat trick is to put the following line in your .gemrc:
|
38
|
+
|
39
|
+
rdoc: --inline-source --line-numbers --format=html --template=hanna
|
40
|
+
|
41
|
+
This will make RubyGems use Hanna when generating documentation for installed gems.
|
42
|
+
|
43
|
+
### Rake task
|
44
|
+
|
45
|
+
For repeated generation of API docs, it's better to set up a Rake task. If you already
|
46
|
+
have an `RDocTask` set up in your Rakefile, the only thing you need to change is this:
|
47
|
+
|
48
|
+
# replace this:
|
49
|
+
require 'rake/rdoctask'
|
50
|
+
# with this:
|
51
|
+
require 'hanna/rdoctask'
|
52
|
+
|
53
|
+
Tip: you can do this in the Rakefile of your Rails project before running `rake doc:rails`.
|
54
|
+
|
55
|
+
Here is an example of a task for the [will_paginate library][wp]:
|
56
|
+
|
57
|
+
# instead of 'rake/rdoctask':
|
58
|
+
require 'hanna/rdoctask'
|
59
|
+
|
60
|
+
desc 'Generate RDoc documentation for the will_paginate plugin.'
|
61
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
62
|
+
rdoc.rdoc_files.include('README.rdoc', 'LICENSE', 'CHANGELOG').
|
63
|
+
include('lib/**/*.rb').
|
64
|
+
exclude('lib/will_paginate/named_scope*').
|
65
|
+
exclude('lib/will_paginate/array.rb').
|
66
|
+
exclude('lib/will_paginate/version.rb')
|
67
|
+
|
68
|
+
rdoc.main = "README.rdoc" # page to start on
|
69
|
+
rdoc.title = "will_paginate documentation"
|
70
|
+
|
71
|
+
rdoc.rdoc_dir = 'doc' # rdoc output folder
|
72
|
+
rdoc.options << '--webcvs=http://github.com/mislav/will_paginate/tree/master/'
|
73
|
+
end
|
74
|
+
|
75
|
+
### Generating documentation for installed gems
|
76
|
+
|
77
|
+
You can generate documentation for installed gems, which might be more convenient than the
|
78
|
+
`gem rdoc` command with the +RDOCOPT+ environment variable set as described. For instance,
|
79
|
+
to generate docs for "actionpack" and "activerecord" type:
|
80
|
+
|
81
|
+
[sudo] hanna --gems actionpack activerecord
|
82
|
+
|
83
|
+
|
84
|
+
## You can help
|
85
|
+
|
86
|
+
Don't like something? Think you can design better? (You probably can.)
|
87
|
+
|
88
|
+
I think of Hanna as the first RDoc template that's actually _maintainable_. First thing I
|
89
|
+
have done is converted the original HTML template to Haml and Sass, cleaning up and
|
90
|
+
removing the (ridiculous amount of) duplication. Also, the template fragments are now in
|
91
|
+
_separate files_.
|
92
|
+
|
93
|
+
Ultimately, I'd like to lose the frameset. Currently that is far from possible because the
|
94
|
+
whole RDoc HTML Generator is built for frames. Still, that is my goal.
|
95
|
+
|
96
|
+
This is git. Fork it, hack away, tell me about it!
|
97
|
+
|
98
|
+
|
99
|
+
[wiki]: http://github.com/mislav/hanna/wikis/home "Hanna wiki"
|
100
|
+
[gemcutter]: http://gemcutter.org/ "Gemcutter gem server"
|
101
|
+
[wp]: http://github.com/mislav/will_paginate/tree/master/Rakefile
|
102
|
+
[Mislav]: http://mislav.caboo.se/ "Mislav Marohnić"
|
data/Rakefile
ADDED
data/bin/hanna
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
if ARGV.size == 1 and ARGV.first == '-h'
|
3
|
+
puts <<-HELP
|
4
|
+
Hanna -- a better RDoc template
|
5
|
+
Synopsis:
|
6
|
+
hanna [options] [file names...]
|
7
|
+
[sudo] hanna --gems [gem names...]
|
8
|
+
|
9
|
+
Example usage:
|
10
|
+
|
11
|
+
hanna lib/**/*.rb
|
12
|
+
|
13
|
+
Hanna passes all arguments to RDoc. To find more about RDoc options, see
|
14
|
+
"rdoc -h". Default options are:
|
15
|
+
|
16
|
+
-o doc --inline-source --charset=UTF-8
|
17
|
+
|
18
|
+
The second form, with the "--gems" argument, serves the same purpose as
|
19
|
+
the "gem rdoc" command: it generates documentation for installed gems.
|
20
|
+
When no gem names are given, "hanna --gems" will install docs for EACH of
|
21
|
+
the gems, which can, uh, take a little while.
|
22
|
+
|
23
|
+
HELP
|
24
|
+
exit 0
|
25
|
+
end
|
26
|
+
|
27
|
+
unless RUBY_PLATFORM =~ /(:?mswin|mingw)/
|
28
|
+
require 'pathname'
|
29
|
+
hanna_dir = Pathname.new(__FILE__).realpath.dirname + '../lib'
|
30
|
+
else
|
31
|
+
# windows
|
32
|
+
hanna_dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
33
|
+
end
|
34
|
+
|
35
|
+
$:.unshift(hanna_dir) unless $:.include?(hanna_dir)
|
36
|
+
|
37
|
+
require 'rubygems'
|
38
|
+
require 'hanna/version'
|
39
|
+
Hanna::require_rdoc
|
40
|
+
require 'rdoc/rdoc'
|
41
|
+
|
42
|
+
options = []
|
43
|
+
|
44
|
+
options << '-f' << 'html' << '-T' << 'hanna'
|
45
|
+
options << '--inline-source' << '--charset=UTF-8'
|
46
|
+
|
47
|
+
if ARGV.first == '--gems'
|
48
|
+
require 'rubygems/doc_manager'
|
49
|
+
Gem::DocManager.configured_args = options
|
50
|
+
|
51
|
+
gem_names = ARGV.dup
|
52
|
+
gem_names.shift
|
53
|
+
|
54
|
+
unless gem_names.empty?
|
55
|
+
specs = gem_names.inject([]) do |arr, name|
|
56
|
+
found = Gem::SourceIndex.from_installed_gems.find_name(name)
|
57
|
+
spec = found.sort_by {|s| s.version }.last
|
58
|
+
arr << spec if spec
|
59
|
+
arr
|
60
|
+
end
|
61
|
+
else
|
62
|
+
specs = Gem::SourceIndex.from_installed_gems.inject({}) do |all, pair|
|
63
|
+
full_name, spec = pair
|
64
|
+
if spec.has_rdoc? and (!all[spec.name] or spec.version > all[spec.name].version)
|
65
|
+
all[spec.name] = spec
|
66
|
+
end
|
67
|
+
all
|
68
|
+
end
|
69
|
+
specs = specs.values
|
70
|
+
puts "Hanna is installing documentation for #{specs.size} gem#{specs.size > 1 ? 's' : ''} ..."
|
71
|
+
end
|
72
|
+
|
73
|
+
specs.each do |spec|
|
74
|
+
Gem::DocManager.new(spec).generate_rdoc
|
75
|
+
end
|
76
|
+
else
|
77
|
+
options << '-o' << 'doc' unless ARGV.include?('-o') or ARGV.include?('--op')
|
78
|
+
options.concat ARGV
|
79
|
+
|
80
|
+
RDoc::RDoc.new.document(options)
|
81
|
+
end
|
data/lib/hanna.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'hanna/hanna'
|
data/lib/hanna/hanna.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# = A better RDoc HTML template
|
2
|
+
#
|
3
|
+
# Authors: Mislav Marohnić <mislav.marohnic@gmail.com>
|
4
|
+
# Tony Strauss (http://github.com/DesigningPatterns)
|
5
|
+
# Michael Granger <ged@FaerieMUD.org>, who had maintained the original RDoc template
|
6
|
+
|
7
|
+
require 'haml'
|
8
|
+
require 'sass'
|
9
|
+
require 'rdoc/generator/html'
|
10
|
+
require 'hanna/template_page_patch'
|
11
|
+
|
12
|
+
module RDoc::Generator::HTML::HANNA
|
13
|
+
class << self
|
14
|
+
def dir
|
15
|
+
@dir ||= File.join File.dirname(__FILE__), 'template_files'
|
16
|
+
end
|
17
|
+
|
18
|
+
def read(*names)
|
19
|
+
content = names.inject('') { |all, name| all << File.read(File.join(dir, name)) }
|
20
|
+
extension = names.first =~ /\.(\w+)$/ && $1
|
21
|
+
|
22
|
+
Hanna::TemplateHelpers.silence_warnings do
|
23
|
+
case extension
|
24
|
+
when 'sass'
|
25
|
+
Sass::Engine.new(content)
|
26
|
+
when 'haml'
|
27
|
+
Haml::Engine.new(content, :format => :html4, :filename => names.join(','))
|
28
|
+
else
|
29
|
+
content
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
STYLE = read('styles.sass')
|
36
|
+
|
37
|
+
CLASS_PAGE = read('page.haml')
|
38
|
+
FILE_PAGE = CLASS_PAGE
|
39
|
+
METHOD_LIST = read('method_list.haml', 'sections.haml')
|
40
|
+
|
41
|
+
FR_INDEX_BODY = BODY = read('layout.haml')
|
42
|
+
|
43
|
+
FILE_INDEX = read('file_index.haml')
|
44
|
+
CLASS_INDEX = read('class_index.haml')
|
45
|
+
METHOD_INDEX = read('method_index.haml')
|
46
|
+
|
47
|
+
INDEX = read('index.haml')
|
48
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'hanna/version'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
Rake::RDocTask.class_eval do
|
6
|
+
# don't allow it
|
7
|
+
undef :external=, :template=
|
8
|
+
|
9
|
+
# Create the tasks defined by this task lib.
|
10
|
+
def define
|
11
|
+
@template = 'hanna'
|
12
|
+
options << '--format=html'
|
13
|
+
|
14
|
+
# inline source and UTF-8 are defaults:
|
15
|
+
options << '--inline-source' unless options.include? '--inline-source' or options.include? '-S'
|
16
|
+
options << '--charset=UTF-8' if options.grep(/^(--charset\b|-c\b)/).empty?
|
17
|
+
|
18
|
+
desc "Build the HTML documentation"
|
19
|
+
task name
|
20
|
+
|
21
|
+
desc "Force a rebuild of the RDOC files"
|
22
|
+
task paste("re", name) => [paste("clobber_", name), name]
|
23
|
+
|
24
|
+
desc "Remove rdoc products"
|
25
|
+
task paste("clobber_", name) do
|
26
|
+
rm_r rdoc_dir rescue nil
|
27
|
+
end
|
28
|
+
|
29
|
+
task :clobber => [paste("clobber_", name)]
|
30
|
+
|
31
|
+
directory @rdoc_dir
|
32
|
+
task name => [rdoc_target]
|
33
|
+
file rdoc_target => @rdoc_files + [Rake.application.rakefile] do
|
34
|
+
rm_r @rdoc_dir rescue nil
|
35
|
+
Hanna::require_rdoc
|
36
|
+
require 'rdoc/rdoc'
|
37
|
+
|
38
|
+
RDoc::RDoc.new.document(option_list + @rdoc_files)
|
39
|
+
end
|
40
|
+
return self
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
%h1= values[:list_title]
|
2
|
+
- any_hidden = false
|
3
|
+
|
4
|
+
%ol#index-entries{ :class => 'files' }
|
5
|
+
- for entry in values[:entries]
|
6
|
+
- hide = entry[:name] =~ /\.rb$/
|
7
|
+
- any_hidden = true if hide
|
8
|
+
%li{ :class => hide ? 'other' : nil }= link_to entry[:name], entry[:href]
|
9
|
+
|
10
|
+
- if any_hidden
|
11
|
+
%li
|
12
|
+
%a.show{ :href => '#', :onclick => 'this.parentNode.parentNode.className += " expanded"; this.parentNode.removeChild(this); return false' } show all
|
@@ -0,0 +1,11 @@
|
|
1
|
+
!!! Frameset
|
2
|
+
%html{ "xml:lang" => "en", :lang => "en", :xmlns => "http://www.w3.org/1999/xhtml" }
|
3
|
+
%head
|
4
|
+
%title= values[:title]
|
5
|
+
%meta{ :content => "text/html; charset=#{values[:charset]}", "http-equiv" => "Content-Type" }
|
6
|
+
%frameset{ :cols => "20%, *", :border => "1", :frameborder => "1", :bordercolor => "gray" }
|
7
|
+
%frameset{ :rows => "15%, 35%, 50%" }
|
8
|
+
%frame{ :name => "Files", :title => "Files", :src => "fr_file_index.html" }
|
9
|
+
%frame{ :name => "Classes", :src => "fr_class_index.html" }
|
10
|
+
%frame{ :name => "Methods", :src => "fr_method_index.html" }
|
11
|
+
%frame{ :name => "docwin", :src => values[:initial_page] }=""
|
@@ -0,0 +1,34 @@
|
|
1
|
+
!!! strict
|
2
|
+
- index = values[:list_title]
|
3
|
+
%html{ :lang => "en" }
|
4
|
+
%head
|
5
|
+
%title= values[:title]
|
6
|
+
%meta{ 'http-equiv' => "Content-Type", :content => "text/html; charset=#{values[:charset]}" }
|
7
|
+
%link{ :rel => "stylesheet", :href => values[:style_url], :type => "text/css", :media => "screen" }
|
8
|
+
- unless index
|
9
|
+
:javascript
|
10
|
+
function popupCode(url) {
|
11
|
+
window.open(url, "Code", "resizable=yes,scrollbars=yes,toolbar=no,status=no,height=150,width=400")
|
12
|
+
}
|
13
|
+
|
14
|
+
function toggleCode(id) {
|
15
|
+
var code = document.getElementById(id)
|
16
|
+
|
17
|
+
code.style.display = code.style.display != 'block' ? 'block' : 'none'
|
18
|
+
return true
|
19
|
+
}
|
20
|
+
|
21
|
+
// Make codeblocks hidden by default
|
22
|
+
document.writeln('<' + 'style type="text/css">.method .source pre { display: none }<\/style>')
|
23
|
+
- else
|
24
|
+
%base{ :target => 'docwin' }/
|
25
|
+
|
26
|
+
%body{ :class => index ? 'list' : 'page' }
|
27
|
+
- if index
|
28
|
+
#index= yield
|
29
|
+
- else
|
30
|
+
#wrapper{ :class => values[:classmod] ? 'class' : 'file' }
|
31
|
+
= yield
|
32
|
+
#footer-push
|
33
|
+
#footer
|
34
|
+
= link_to '<strong>Hanna</strong> RDoc template', 'http://github.com/mislav/hanna/tree/master'
|
@@ -0,0 +1,13 @@
|
|
1
|
+
%h1= values[:list_title]
|
2
|
+
|
3
|
+
%script{:type => 'text/javascript'}
|
4
|
+
= read("prototype-1.6.0.3.js")
|
5
|
+
= build_javascript_search_index(values[:entries])
|
6
|
+
= read("method_search.js")
|
7
|
+
%form{:onsubmit => 'return performSearch()'}
|
8
|
+
%input{:type => 'text', :id => 'search', :class => 'untouched', :value => 'Enter search terms...'}
|
9
|
+
%ol#search-results{ :class => 'methods', :style => 'display: none' }
|
10
|
+
|
11
|
+
%ol#index-entries{ :class => 'methods' }
|
12
|
+
- for entry in values[:entries]
|
13
|
+
%li= link_to_method entry[:name], entry[:href]
|
@@ -0,0 +1,37 @@
|
|
1
|
+
- methods = methods_from_sections values[:sections]
|
2
|
+
- unless methods.empty?
|
3
|
+
#method-list
|
4
|
+
%h2 Methods
|
5
|
+
- for type in ['public class', 'protected class', 'public instance', 'protected instance']
|
6
|
+
- unless (list = methods[type]).empty?
|
7
|
+
%h3= type
|
8
|
+
%ol
|
9
|
+
- for method in list
|
10
|
+
- if method[:name].to_s.empty? && method[:callseq]
|
11
|
+
%li= link_to method[:callseq].gsub(/<br\s*\/?>/, "").split(/[\r\n]+/).map{ |s| s.split(/([({]+|\[\{|\s+(#?=>|→)\s+)/).first.sub(/^[A-Za-z0-9_:]+\./, "").sub(/\s+=\s+.*/, "=").strip }.uniq.join("<br />\n"), '#' + method[:aref]
|
12
|
+
- else
|
13
|
+
%li= link_to method[:name], '#' + method[:aref]
|
14
|
+
|
15
|
+
- if values[:requires] or values[:toc] or values[:includes]
|
16
|
+
#context
|
17
|
+
- if values[:requires]
|
18
|
+
#requires
|
19
|
+
%h2 Required files
|
20
|
+
%ol
|
21
|
+
- for req in values[:requires]
|
22
|
+
%li= link_to req[:name], req[:aref]
|
23
|
+
|
24
|
+
- if values[:toc]
|
25
|
+
#contents
|
26
|
+
%h2 Contents
|
27
|
+
%ol
|
28
|
+
- for item in values[:toc]
|
29
|
+
%li= link_to values[:secname], values[:href]
|
30
|
+
|
31
|
+
- if values[:includes]
|
32
|
+
#includes
|
33
|
+
%h2 Included modules
|
34
|
+
%ol
|
35
|
+
- for inc in values[:includes]
|
36
|
+
%li= link_to inc[:name], inc[:aref]
|
37
|
+
|
@@ -0,0 +1,63 @@
|
|
1
|
+
$(document).observe('dom:loaded', function() {
|
2
|
+
// Setup search-during-typing.
|
3
|
+
new Form.Element.Observer('search', 0.3, function(element, value) {
|
4
|
+
performSearch();
|
5
|
+
});
|
6
|
+
|
7
|
+
// Remove the default search box value when the user puts the focus on
|
8
|
+
// the search box for the first time.
|
9
|
+
var search_box = $('search');
|
10
|
+
if ($F('search') == 'Enter search terms...') {
|
11
|
+
search_box.observe('focus', function() {
|
12
|
+
if (search_box.hasClassName('untouched')) {
|
13
|
+
search_box.removeClassName('untouched');
|
14
|
+
search_box.value = '';
|
15
|
+
}
|
16
|
+
});
|
17
|
+
} else {
|
18
|
+
search_box.removeClassName('untouched');
|
19
|
+
}
|
20
|
+
|
21
|
+
search_box.insert({
|
22
|
+
after: new Element('span', { 'class': 'clear_button' }).update('x').observe('click', function(e) {
|
23
|
+
e.stopPropagation()
|
24
|
+
search_box.setValue('')
|
25
|
+
search_box.focus()
|
26
|
+
})
|
27
|
+
})
|
28
|
+
});
|
29
|
+
|
30
|
+
function searchInIndex(query) {
|
31
|
+
var i;
|
32
|
+
var results = [];
|
33
|
+
query = query.toLowerCase();
|
34
|
+
for (i = 0; i < search_index.length; i++) {
|
35
|
+
if (search_index[i].method.indexOf(query) != -1) {
|
36
|
+
results.push(search_index[i]);
|
37
|
+
}
|
38
|
+
}
|
39
|
+
return results;
|
40
|
+
}
|
41
|
+
|
42
|
+
function buildHtmlForResults(results) {
|
43
|
+
var html = "";
|
44
|
+
var i;
|
45
|
+
for (i = 0; i < results.length; i++) {
|
46
|
+
html += '<li>' + results[i].html + '</li>';
|
47
|
+
}
|
48
|
+
return html;
|
49
|
+
}
|
50
|
+
|
51
|
+
function performSearch() {
|
52
|
+
var query = $F('search');
|
53
|
+
if (query == '') {
|
54
|
+
$('index-entries').show();
|
55
|
+
$('search-results').hide();
|
56
|
+
} else {
|
57
|
+
var results = searchInIndex(query);
|
58
|
+
$('search-results').update(buildHtmlForResults(results));
|
59
|
+
$('index-entries').hide();
|
60
|
+
$('search-results').show();
|
61
|
+
}
|
62
|
+
return false;
|
63
|
+
}
|