├── lib
└── rdoc
│ ├── generator
│ ├── html
│ │ ├── common.rb
│ │ ├── one_page_html.rb
│ │ └── html.rb
│ ├── xml.rb
│ ├── xml
│ │ ├── rdf.rb
│ │ └── xml.rb
│ ├── html.rb
│ └── shadow_classes.rb
│ └── template.rb
└── test
└── test_rdoc_generator_context.rb
/lib/rdoc/generator/html/common.rb:
--------------------------------------------------------------------------------
1 | #
2 | # The templates require further refactoring. In particular,
3 | # * Some kind of HTML generation library should be used.
4 | #
5 | # Also, all of the templates require some TLC from a designer.
6 | #
7 | # Right now, this file contains some constants that are used by all
8 | # of the templates.
9 | #
10 | module RDoc::Generator::HTML::Common
11 | XHTML_STRICT_PREAMBLE = <<-EOF
12 |
14 | EOF
15 |
16 | XHTML_FRAME_PREAMBLE = <<-EOF
17 |
19 | EOF
20 |
21 | HTML_ELEMENT = <<-EOF
22 |
23 | EOF
24 | end
25 |
--------------------------------------------------------------------------------
/test/test_rdoc_generator_context.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'minitest/unit'
3 | require 'rdoc/generator'
4 | require 'rdoc/stats'
5 | require 'rdoc/code_objects'
6 | require 'rdoc/parser/ruby'
7 |
8 | class TestRDocGeneratorContext < MiniTest::Unit::TestCase
9 |
10 | DATA = <<-DATA
11 | class Foo # :nodoc:
12 | end
13 |
14 | class Bar
15 | end
16 | DATA
17 |
18 | def setup
19 | RDoc::TopLevel.reset
20 | RDoc::Generator::Method.reset
21 |
22 | top_level = RDoc::TopLevel.new 'data.rb'
23 |
24 | @options = RDoc::Options.new
25 | @options.quiet = true
26 | @options.inline_source = true
27 |
28 | stats = RDoc::Stats.new 0
29 |
30 | parser = RDoc::Parser::Ruby.new top_level, 'data.rb', DATA, @options, stats
31 |
32 | @top_levels = []
33 | @top_levels.push parser.scan
34 | end
35 |
36 | def test_class_build_indices
37 | files, classes = RDoc::Generator::Context.build_indices @top_levels, @options
38 |
39 | assert_equal 1, classes.length
40 |
41 | assert_equal 'Bar', classes.first.name
42 |
43 | # HACK complete
44 | end
45 |
46 | end
47 |
48 |
--------------------------------------------------------------------------------
/lib/rdoc/template.rb:
--------------------------------------------------------------------------------
1 | require 'erb'
2 |
3 | module RDoc; end
4 |
5 | ##
6 | # An ERb wrapper that allows nesting of one ERb template inside another.
7 | #
8 | # This TemplatePage operates similarly to RDoc 1.x's TemplatePage, but uses
9 | # ERb instead of a custom template language.
10 | #
11 | # Converting from a RDoc 1.x template to an RDoc 2.x template is fairly easy.
12 | #
13 | # * %blah% becomes <%= values["blah"] %>
14 | # * !INCLUDE! becomes <%= template_include %>
15 | # * HREF:aref:name becomes <%= href values["aref"], values["name"] %>
16 | # * IF:blah becomes <% if values["blah"] then %>
17 | # * IFNOT:blah becomes <% unless values["blah"] then %>
18 | # * ENDIF:blah becomes <% end %>
19 | # * START:blah becomes <% values["blah"].each do |blah| %>
20 | # * END:blah becomes <% end %>
21 | #
22 | # To make nested loops easier to convert, start by converting START statements
23 | # to:
24 | #
25 | # <% values["blah"].each do |blah| $stderr.puts blah.keys %>
26 | #
27 | # So you can see what is being used inside which loop.
28 |
29 | class RDoc::TemplatePage
30 |
31 | ##
32 | # Create a new TemplatePage that will use +templates+.
33 |
34 | def initialize(*templates)
35 | @templates = templates
36 | end
37 |
38 | ##
39 | # Returns "#{name} "
40 |
41 | def href(ref, name)
42 | if ref then
43 | "#{name} "
44 | else
45 | name
46 | end
47 | end
48 |
49 | ##
50 | # Process the template using +values+, writing the result to +io+.
51 |
52 | def write_html_on(io, values)
53 | b = binding
54 | template_include = ""
55 |
56 | @compiled_templates ||= @templates.map do |template|
57 | ERB.new(template)
58 | end
59 |
60 | @compiled_templates.reverse_each do |template|
61 | template_include = template.result(b)
62 | end
63 |
64 | io.write template_include
65 | end
66 |
67 | end
68 |
69 |
--------------------------------------------------------------------------------
/lib/rdoc/generator/xml.rb:
--------------------------------------------------------------------------------
1 | require 'rdoc/generator/html'
2 | require 'rdoc/cache'
3 |
4 | ##
5 | # Generate XML output as one big file
6 |
7 | class RDoc::Generator::XML < RDoc::Generator::HTML
8 |
9 | RDoc::RDoc.add_generator self
10 |
11 | ##
12 | # Standard generator factory
13 |
14 | def self.for(options)
15 | new(options)
16 | end
17 |
18 | def initialize(*args)
19 | super
20 | end
21 |
22 | ##
23 | # Build the initial indices and output objects
24 | # based on an array of TopLevel objects containing
25 | # the extracted information.
26 |
27 | def generate(info)
28 | @info = info
29 | @files = []
30 | @classes = []
31 | @hyperlinks = {}
32 |
33 | build_indices
34 | generate_xml
35 | end
36 |
37 | ##
38 | # Generate:
39 | #
40 | # * a list of File objects for each TopLevel object.
41 | # * a list of Class objects for each first level
42 | # class or module in the TopLevel objects
43 | # * a complete list of all hyperlinkable terms (file,
44 | # class, module, and method names)
45 |
46 | def build_indices
47 | template_cache = RDoc::Cache.instance
48 |
49 | @info.each do |toplevel|
50 | @files << RDoc::Generator::File.new(template_cache, toplevel, @options,
51 | RDoc::Generator::FILE_DIR)
52 | end
53 |
54 | RDoc::TopLevel.all_classes_and_modules.each do |cls|
55 | build_class_list(template_cache, cls, @files[0], RDoc::Generator::CLASS_DIR)
56 | end
57 | end
58 |
59 | def build_class_list(template_cache, from, html_file, class_dir)
60 | @classes << RDoc::Generator::Class.new(template_cache, from, html_file,
61 | class_dir, @options)
62 | from.each_classmodule do |mod|
63 | build_class_list(template_cache, mod, html_file, class_dir)
64 | end
65 | end
66 |
67 | ##
68 | # Generate all the HTML. For the one-file case, we generate
69 | # all the information in to one big hash
70 |
71 | def generate_xml
72 | values = {
73 | :charset => @options.charset,
74 | :files => gen_into(@files),
75 | :classes => gen_into(@classes)
76 | }
77 |
78 | template = RDoc::TemplatePage.new @template::ONE_PAGE
79 |
80 | if @options.op_name
81 | opfile = File.open(@options.op_name, "w")
82 | else
83 | opfile = $stdout
84 | end
85 | template.write_html_on(opfile, values)
86 | end
87 |
88 | def gen_into(list)
89 | res = []
90 | list.each do |item|
91 | res << item.value_hash
92 | end
93 | res
94 | end
95 |
96 | def gen_file_index
97 | gen_an_index(@files, 'Files')
98 | end
99 |
100 | def gen_class_index
101 | gen_an_index(@classes, 'Classes')
102 | end
103 |
104 | def gen_method_index
105 | gen_an_index(RDoc::Generator::HtmlMethod.all_methods, 'Methods')
106 | end
107 |
108 | def gen_an_index(collection, title)
109 | res = []
110 | collection.sort.each do |f|
111 | if f.document_self
112 | res << { :href => f.path, :name => f.index_name }
113 | end
114 | end
115 |
116 | return {
117 | :entries => res,
118 | :list_title => title,
119 | :index_url => main_url,
120 | }
121 | end
122 |
123 | end
124 |
125 |
--------------------------------------------------------------------------------
/lib/rdoc/generator/html/one_page_html.rb:
--------------------------------------------------------------------------------
1 | require 'rdoc/generator/html'
2 | require 'rdoc/generator/html/common'
3 |
4 | module RDoc::Generator::HTML::ONE_PAGE_HTML
5 |
6 | include RDoc::Generator::HTML::Common
7 |
8 | CONTENTS_XML = <<-EOF
9 | <% if defined? classes and classes[:description] then %>
10 | <%= classes[:description] %>
11 | <% end %>
12 |
13 | <% if defined? files and files[:requires] then %>
14 |
Requires:
15 |
16 | <% files[:requires].each do |requires| %>
17 | <% if requires[:aref] then %>
18 | <%= requires[:name] %>
19 | <% end %>
20 | <% unless requires[:aref] then %>
21 | <%= requires[:name] %>
22 | <% end %>
23 | <% end %><%# files[:requires] %>
24 |
25 | <% end %>
26 |
27 | <% if defined? classes and classes[:includes] then %>
28 | Includes
29 |
30 | <% classes[:includes].each do |includes| %>
31 | <% if includes[:aref] then %>
32 | <%= includes[:name] %>
33 | <% end %>
34 | <% unless includes[:aref] then %>
35 | <%= includes[:name] %>
36 | <% end %>
37 | <% end %><%# classes[:includes] %>
38 |
39 | <% end %>
40 |
41 | <% if defined? classes and classes[:sections] then %>
42 | <% classes[:sections].each do |sections| %>
43 | <% if sections[:attributes] then %>
44 | Attributes
45 |
46 | <% sections[:attributes].each do |attributes| %>
47 | <%= attributes[:name] %> <%= attributes[:rw] %> <%= attributes[:a_desc] %>
48 | <% end %><%# sections[:attributes] %>
49 |
50 | <% end %>
51 |
52 | <% if sections[:method_list] then %>
53 | Methods
54 | <% sections[:method_list].each do |method_list| %>
55 | <% if method_list[:methods] then %>
56 | <% method_list[:methods].each do |methods| %>
57 | <%= methods[:type] %> <%= methods[:category] %> method:
58 | <% if methods[:callseq] then %>
59 | <%= methods[:callseq] %>
60 | <% end %>
61 | <% unless methods[:callseq] then %>
62 | <%= methods[:name] %><%= methods[:params] %>
63 | <% end %>
64 |
65 | <% if methods[:m_desc] then %>
66 | <%= methods[:m_desc] %>
67 | <% end %>
68 |
69 | <% if methods[:sourcecode] then %>
70 |
71 | <%= methods[:sourcecode] %>
72 |
73 | <% end %>
74 | <% end %><%# method_list[:methods] %>
75 | <% end %>
76 | <% end %><%# sections[:method_list] %>
77 | <% end %>
78 | <% end %><%# classes[:sections] %>
79 | <% end %>
80 | EOF
81 |
82 | ONE_PAGE = XHTML_STRICT_PREAMBLE + HTML_ELEMENT + %{
83 |
84 | <%= values[:title] %>
85 |
86 |
87 |
88 | <% values[:files].each do |files| %>
89 |
90 |
91 | Path: <%= files[:full_path] %>
92 | Modified: <%= files[:dtm_modified] %>
93 |
94 | } + CONTENTS_XML + %{
95 | <% end %><%# values[:files] %>
96 |
97 | <% if values[:classes] then %>
98 | Classes
99 | <% values[:classes].each do |classes| %>
100 | <% if classes[:parent] then %>
101 | <%= classes[:classmod] %> <%= classes[:full_name] %> < <%= href classes[:par_url], classes[:parent] %>
102 | <% end %>
103 | <% unless classes[:parent] then %>
104 | <%= classes[:classmod] %> <%= classes[:full_name] %>
105 | <% end %>
106 |
107 | <% if classes[:infiles] then %>
108 | (in files
109 | <% classes[:infiles].each do |infiles| %>
110 | <%= href infiles[:full_path_url], infiles[:full_path] %>
111 | <% end %><%# classes[:infiles] %>
112 | )
113 | <% end %>
114 | } + CONTENTS_XML + %{
115 | <% end %><%# values[:classes] %>
116 | <% end %>
117 |
118 |
119 | }
120 |
121 | end
122 |
123 |
--------------------------------------------------------------------------------
/lib/rdoc/generator/xml/rdf.rb:
--------------------------------------------------------------------------------
1 | require 'rdoc/generator/xml'
2 |
3 | module RDoc::Generator::XML::RDF
4 |
5 | CONTENTS_RDF = <<-EOF
6 | <% if defined? classes and classes[:description] then %>
7 |
8 | <%= classes[:description] %>
9 |
10 | <% end %>
11 |
12 | <% if defined? files and files[:requires] then %>
13 | <% files[:requires].each do |requires| %>
14 |
15 | <% end # files[:requires] %>
16 | <% end %>
17 |
18 | <% if defined? classes and classes[:includes] then %>
19 |
20 | <% classes[:includes].each do |includes| %>
21 |
22 | <% end # includes[:includes] %>
23 |
24 | <% end %>
25 |
26 | <% if defined? classes and classes[:sections] then %>
27 | <% classes[:sections].each do |sections| %>
28 | <% if sections[:attributes] then %>
29 | <% sections[:attributes].each do |attributes| %>
30 |
31 |
32 | <% if attributes[:rw] then %>
33 | <%= attributes[:rw] %>
34 | <% end %>
35 | <%= attributes[:a_desc] %>
36 |
37 |
38 | <% end # sections[:attributes] %>
39 | <% end %>
40 |
41 | <% if sections[:method_list] then %>
42 | <% sections[:method_list].each do |method_list| %>
43 | <% if method_list[:methods] then %>
44 | <% method_list[:methods].each do |methods| %>
45 |
46 |
48 | <%= methods[:params] %>
49 | <% if methods[:m_desc] then %>
50 |
51 | <%= methods[:m_desc] %>
52 |
53 | <% end %>
54 | <% if methods[:sourcecode] then %>
55 |
56 | <%= methods[:sourcecode] %>
57 |
58 | <% end %>
59 |
60 |
61 | <% end # method_list[:methods] %>
62 | <% end %>
63 | <% end # sections[:method_list] %>
64 | <% end %>
65 |
66 | <% end # classes[:sections] %>
67 | <% end %>
68 | EOF
69 |
70 | ########################################################################
71 |
72 | ONE_PAGE = %{
73 |
76 |
77 |
78 | <% values[:files].each do |files| %>
79 |
80 | <%= files[:full_path] %>
81 | <%= files[:dtm_modified] %>
82 | } + CONTENTS_RDF + %{
83 |
84 | <% end # values[:files] %>
85 | <% values[:classes].each do |classes| %>
86 | <<%= values[:classmod] %> rd:name="<%= classes[:full_name] %>" rd:id="<%= classes[:full_name] %>">
87 |
88 | <% if classes[:infiles] then %>
89 |
90 | <% classes[:infiles].each do |infiles| %>
91 |
92 |
94 | rdf:about="<%= infiles[:full_path_url] %>"
95 | <% end %>
96 | />
97 |
98 | <% end # classes[:infiles] %>
99 |
100 | <% end %>
101 | <% if classes[:parent] then %>
102 | <%= href classes[:par_url], classes[:parent] %>
103 | <% end %>
104 |
105 | } + CONTENTS_RDF + %{
106 | <%= classes[:classmod] %>>
107 | <% end # values[:classes] %>
108 |
109 |
110 | }
111 |
112 | end
113 |
114 |
--------------------------------------------------------------------------------
/lib/rdoc/generator/xml/xml.rb:
--------------------------------------------------------------------------------
1 | require 'rdoc/generator/xml'
2 |
3 | module RDoc::Generator::XML::XML
4 |
5 | CONTENTS_XML = <<-EOF
6 | <% if defined? classes and classes[:description] then %>
7 |
8 | <%= classes[:description] %>
9 |
10 | <% end %>
11 |
12 | <% if defined? files and files[:requires] then %>
13 |
14 | <% files[:requires].each do |requires| %>
15 |
17 | href="<%= requires[:aref] %>"
18 | <% end %>
19 | />
20 | <% end %><%# files[:requires] %>
21 |
22 | <% end %>
23 | <% if defined? classes and classes[:sections] then %>
24 | <% classes[:sections].each do |sections| %>
25 | <% if sections[:constants] then %>
26 |
27 | <% sections[:constants].each do |constant| %>
28 |
29 | <% if constant[:value] then %>
30 | <%= constant[:value] %>
31 | <% end %>
32 | <%= constant[:a_desc] %>
33 |
34 | <% end %><%# sections[:constants] %>
35 |
36 | <% end %>
37 | <% if sections[:attributes] then %>
38 |
39 | <% sections[:attributes].each do |attributes| %>
40 |
41 | <% if attributes[:rw] then %>
42 | <%= attributes[:rw] %>
43 | <% end %>
44 | <%= attributes[:a_desc] %>
45 |
46 | <% end %><%# sections[:attributes] %>
47 |
48 | <% end %>
49 | <% if sections[:method_list] then %>
50 |
51 | <% sections[:method_list].each do |method_list| %>
52 | <% if method_list[:methods] then %>
53 | <% method_list[:methods].each do |methods| %>
54 |
55 | <%= methods[:params] %>
56 | <% if methods[:m_desc] then %>
57 |
58 | <%= methods[:m_desc] %>
59 |
60 | <% end %>
61 | <% if methods[:sourcecode] then %>
62 |
63 | <%= methods[:sourcecode] %>
64 |
65 | <% end %>
66 |
67 | <% end %><%# method_list[:methods] %>
68 | <% end %>
69 | <% end %><%# sections[:method_list] %>
70 |
71 | <% end %>
72 | <% end %><%# classes[:sections] %>
73 | <% end %>
74 | <% if defined? classes and classes[:includes] then %>
75 |
76 | <% classes[:includes].each do |includes| %>
77 |
79 | href="<%= includes[:aref] %>"
80 | <% end %>
81 | />
82 | <% end %><%# classes[:includes] %>
83 |
84 | <% end %>
85 |
86 | EOF
87 |
88 | ONE_PAGE = %{
89 |
90 |
91 | <% values[:files].each do |files| %>
92 |
93 |
94 | <%= files[:full_path] %>
95 | <%= files[:dtm_modified] %>
96 |
97 | } + CONTENTS_XML + %{
98 |
99 | <% end %><%# values[:files] %>
100 |
101 |
102 | <% values[:classes].each do |classes| %>
103 | <<%= classes[:classmod] %> name="<%= classes[:full_name] %>" id="<%= classes[:full_name] %>">
104 |
105 | <% if classes[:infiles] then %>
106 |
107 | <% classes[:infiles].each do |infiles| %>
108 | <%= href infiles[:full_path_url], infiles[:full_path] %>
109 | <% end %><%# classes[:infiles] %>
110 |
111 | <% end %>
112 | <% if classes[:parent] then %>
113 | <%= href classes[:par_url], classes[:parent] %>
114 | <% end %>
115 |
116 | } + CONTENTS_XML + %{
117 | <%= classes[:classmod] %>>
118 | <% end %><%# values[:classes] %>
119 |
120 |
121 | }
122 |
123 | end
124 |
--------------------------------------------------------------------------------
/lib/rdoc/generator/html.rb:
--------------------------------------------------------------------------------
1 | require 'fileutils'
2 |
3 | require 'rdoc/generator'
4 | require 'rdoc/markup/to_html'
5 | require 'rdoc/cache'
6 |
7 | ##
8 | # We're responsible for generating all the HTML files from the object tree
9 | # defined in code_objects.rb. We generate:
10 | #
11 | # [files] an html file for each input file given. These
12 | # input files appear as objects of class
13 | # TopLevel
14 | #
15 | # [classes] an html file for each class or module encountered.
16 | # These classes are not grouped by file: if a file
17 | # contains four classes, we'll generate an html
18 | # file for the file itself, and four html files
19 | # for the individual classes.
20 | #
21 | # [indices] we generate three indices for files, classes,
22 | # and methods. These are displayed in a browser
23 | # like window with three index panes across the
24 | # top and the selected description below
25 | #
26 | # Method descriptions appear in whatever entity (file, class, or module) that
27 | # contains them.
28 | #
29 | # We generate files in a structure below a specified subdirectory, normally
30 | # +doc+.
31 | #
32 | # opdir
33 | # |
34 | # |___ files
35 | # | |__ per file summaries
36 | # |
37 | # |___ classes
38 | # |__ per class/module descriptions
39 | #
40 | # HTML is generated using the Template class.
41 |
42 | class RDoc::Generator::HTML
43 |
44 | RDoc::RDoc.add_generator self
45 |
46 | include RDoc::Generator::MarkUp
47 |
48 | ##
49 | # Generator may need to return specific subclasses depending on the
50 | # options they are passed. Because of this we create them using a factory
51 |
52 | def self.for(options)
53 | RDoc::Generator::AllReferences.reset
54 | RDoc::Generator::Method.reset
55 |
56 | if options.all_one_file
57 | RDoc::Generator::HTMLInOne.new options
58 | else
59 | new options
60 | end
61 | end
62 |
63 | class << self
64 | protected :new
65 | end
66 |
67 | ##
68 | # Set up a new HTML generator. Basically all we do here is load up the
69 | # correct output temlate
70 |
71 | def initialize(options) #:not-new:
72 | @options = options
73 | load_html_template
74 | end
75 |
76 | ##
77 | # Build the initial indices and output objects
78 | # based on an array of TopLevel objects containing
79 | # the extracted information.
80 |
81 | def generate(toplevels)
82 | @toplevels = toplevels
83 | @files = []
84 | @classes = []
85 | @template_cache = RDoc::Cache.instance
86 |
87 | write_style_sheet
88 | gen_sub_directories
89 | build_indices
90 | generate_html
91 | end
92 |
93 | private
94 |
95 | ##
96 | # Load up the HTML template specified in the options. If the template name
97 | # contains a slash, use it literally.
98 | #
99 | # If the template is not a path, first look for it in rdoc's HTML template
100 | # directory. Perhaps this behavior should be reversed (first try to include
101 | # the template and, only if that fails, try to include it in the default
102 | # template directory). One danger with reversing the behavior, however, is
103 | # that if something like require 'html' could load up an unrelated file in
104 | # the standard library or in a gem.
105 |
106 | def load_html_template
107 | template = @options.template
108 |
109 | unless template =~ %r{/|\\} then
110 | generator_name = @options.generator.name.sub(/^RDoc::Generator::/, '')
111 | template = File.join('rdoc', 'generator', generator_name.downcase,
112 | template)
113 | end
114 |
115 | begin
116 | require template
117 |
118 | @template = self.class.const_get @options.template.upcase
119 | @options.template_class = @template
120 | rescue LoadError => e
121 | # The template did not exist in the default template directory, so
122 | # see if require can find the template elsewhere (in a gem, for
123 | # instance).
124 | if(e.message[template] && template != @options.template)
125 | template = @options.template
126 | retry
127 | end
128 |
129 | $stderr.puts "Could not find HTML template '#{template}': #{e.message}"
130 | exit 99
131 | end
132 | end
133 |
134 | ##
135 | # Write out the style sheet used by the main frames
136 |
137 | def write_style_sheet
138 | return unless @template.constants.include? :STYLE or
139 | @template.constants.include? 'STYLE'
140 |
141 | template = RDoc::TemplatePage.new @template::STYLE
142 |
143 | unless @options.css then
144 | open RDoc::Generator::CSS_NAME, 'w' do |f|
145 | values = {}
146 |
147 | if @template.constants.include? :FONTS or
148 | @template.constants.include? 'FONTS' then
149 | values[:fonts] = @template::FONTS
150 | end
151 |
152 | template.write_html_on(f, values)
153 | end
154 | end
155 | end
156 |
157 | ##
158 | # See the comments at the top for a description of the directory structure
159 |
160 | def gen_sub_directories
161 | FileUtils.mkdir_p RDoc::Generator::FILE_DIR
162 | FileUtils.mkdir_p RDoc::Generator::CLASS_DIR
163 | rescue
164 | $stderr.puts $!.message
165 | exit 1
166 | end
167 |
168 | def build_indices
169 | @files, @classes = RDoc::Generator::Context.build_indices(@toplevels,
170 | @options,
171 | @template_cache)
172 | end
173 |
174 | ##
175 | # Generate all the HTML
176 |
177 | def generate_html
178 | @main_url = main_url
179 | @sorted_files = @files.sort
180 | @sorted_classes = @classes.sort
181 | @sorted_methods = RDoc::Generator::Method.all_methods.sort
182 |
183 | # the individual descriptions for files and classes
184 | gen_into(@files)
185 | gen_into(@classes)
186 |
187 | # and the index files
188 | gen_file_index
189 | gen_class_index
190 | gen_method_index
191 | gen_main_index
192 |
193 | # this method is defined in the template file
194 | values = {
195 | :title_suffix => CGI.escapeHTML("[#{@options.title}]"),
196 | :charset => @options.charset,
197 | :style_url => style_url('', @options.css),
198 | }
199 |
200 | @template.write_extra_pages(values) if
201 | @template.respond_to?(:write_extra_pages)
202 | end
203 |
204 | def gen_into(list)
205 | #
206 | # The file, class, and method lists technically should be regenerated
207 | # for every output file, in order that the relative links be correct
208 | # (we are worried here about frameless templates, which need this
209 | # information for every generated page). Doing this is a bit slow,
210 | # however. For a medium-sized gem, this increased rdoc's runtime by
211 | # about 5% (using the 'time' command-line utility). While this is not
212 | # necessarily a problem, I do not want to pessimize rdoc for large
213 | # projects, however, and so we only regenerate the lists when the
214 | # directory of the output file changes, which seems like a reasonable
215 | # optimization.
216 | #
217 | file_list = {}
218 | class_list = {}
219 | method_list = {}
220 | prev_op_dir = nil
221 |
222 | # Extra optimization: sort the list based on item directories, to minimize
223 | # the number of times the directory changes. This reduces runtime by about
224 | # 8% on RDoc's own sources.
225 | sorted_list = list.sort do |a, b|
226 | File.dirname(a.path) <=> File.dirname(b.path)
227 | end
228 |
229 | sorted_list.each do |item|
230 | next unless item.document_self
231 |
232 | op_file = item.path
233 | op_dir = File.dirname(op_file)
234 |
235 | if(op_dir != prev_op_dir)
236 | file_list = index_to_links op_file, @sorted_files
237 | class_list = index_to_links op_file, @sorted_classes
238 | method_list = index_to_links op_file, @sorted_methods
239 | end
240 | prev_op_dir = op_dir
241 |
242 | FileUtils.mkdir_p op_dir
243 |
244 | open op_file, 'w' do |io|
245 | item.write_on io, file_list, class_list, method_list
246 | end
247 | end
248 | end
249 |
250 | def gen_file_index
251 | gen_an_index @files, 'Files', @template::FILE_INDEX, "fr_file_index.html"
252 | end
253 |
254 | def gen_class_index
255 | gen_an_index(@classes, 'Classes', @template::CLASS_INDEX,
256 | "fr_class_index.html")
257 | end
258 |
259 | def gen_method_index
260 | gen_an_index(RDoc::Generator::Method.all_methods, 'Methods',
261 | @template::METHOD_INDEX, "fr_method_index.html")
262 | end
263 |
264 | def gen_an_index(collection, title, template, filename)
265 | template = RDoc::TemplatePage.new @template::FR_INDEX_BODY, template
266 | res = []
267 | collection.sort.each do |f|
268 | if f.document_self
269 | res << { :href => f.path, :name => f.index_name }
270 | end
271 | end
272 |
273 | values = {
274 | :entries => res,
275 | :title => CGI.escapeHTML("#{title} [#{@options.title}]"),
276 | :list_title => CGI.escapeHTML(title),
277 | :index_url => @main_url,
278 | :charset => @options.charset,
279 | :style_url => style_url('', @options.css),
280 | }
281 |
282 | open filename, 'w' do |f|
283 | template.write_html_on(f, values)
284 | end
285 | end
286 |
287 | ##
288 | # The main index page is mostly a template frameset, but includes the
289 | # initial page. If the --main option was given, we use this as
290 | # our main page, otherwise we use the first file specified on the command
291 | # line.
292 |
293 | def gen_main_index
294 | if @template.const_defined? :FRAMELESS then
295 | #
296 | # If we're using a template without frames, then just redirect
297 | # to it from index.html.
298 | #
299 | # One alternative to this, expanding the main page's template into
300 | # index.html, is tricky because the relative URLs will be different
301 | # (since index.html is located in at the site's root,
302 | # rather than within a files or a classes subdirectory).
303 | #
304 | open 'index.html', 'w' do |f|
305 | f.puts(%{})
307 | f.puts(%{})
309 | f.puts(%{})
310 | f.puts(%{#{CGI.escapeHTML(@options.title)} })
311 | f.puts(%{ })
312 | f.puts(%{})
313 | f.puts(%{})
314 | f.puts(%{})
315 | end
316 | else
317 | main = RDoc::TemplatePage.new @template::INDEX
318 |
319 | open 'index.html', 'w' do |f|
320 | style_url = style_url '', @options.css
321 |
322 | classes = @classes.sort.map { |klass| klass.value_hash }
323 |
324 | values = {
325 | :initial_page => @main_url,
326 | :style_url => style_url('', @options.css),
327 | :title => CGI.escapeHTML(@options.title),
328 | :charset => @options.charset,
329 | :classes => classes,
330 | }
331 |
332 | values[:inline_source] = @options.inline_source
333 |
334 | main.write_html_on f, values
335 | end
336 | end
337 | end
338 |
339 | def index_to_links(output_path, sorted_collection)
340 | result = sorted_collection.map do |f|
341 | next unless f.document_self
342 | { :href => RDoc::Markup::ToHtml.gen_relative_url(output_path, f.path),
343 | :name => f.index_name }
344 | end
345 | result.compact!
346 | result
347 | end
348 |
349 | ##
350 | # Returns the url of the main page
351 |
352 | def main_url
353 | main_page = @options.main_page
354 |
355 | #
356 | # If a main page has been specified (--main), then search for it
357 | # in the AllReferences array. This allows either files or classes
358 | # to be used for the main page.
359 | #
360 | if main_page then
361 | main_page_ref = RDoc::Generator::AllReferences[main_page]
362 |
363 | if main_page_ref then
364 | return main_page_ref.path
365 | else
366 | $stderr.puts "Could not find main page #{main_page}"
367 | end
368 | end
369 |
370 | #
371 | # No main page has been specified, so just use the README.
372 | #
373 | @files.each do |file|
374 | return file.path if file.name =~ /^README/
375 | end
376 |
377 | #
378 | # There's no README (shame! shame!). Just use the first file
379 | # that will be documented.
380 | #
381 | @files.each do |file|
382 | return file.path if file.document_self
383 | end
384 |
385 | #
386 | # There are no files to be documented... Something seems very wrong.
387 | #
388 | raise RDoc::Error, "Couldn't find anything to document (perhaps :stopdoc: has been used in all classes)!"
389 | end
390 | private :main_url
391 |
392 | end
393 |
394 | class RDoc::Generator::HTMLInOne < RDoc::Generator::HTML
395 |
396 | def initialize(*args)
397 | super
398 | end
399 |
400 | ##
401 | # Build the initial indices and output objects
402 | # based on an array of TopLevel objects containing
403 | # the extracted information.
404 |
405 | def generate(info)
406 | @toplevels = info
407 | @hyperlinks = {}
408 |
409 | build_indices
410 | generate_xml
411 | end
412 |
413 | ##
414 | # Generate:
415 | #
416 | # * a list of RDoc::Generator::File objects for each TopLevel object.
417 | # * a list of RDoc::Generator::Class objects for each first level
418 | # class or module in the TopLevel objects
419 | # * a complete list of all hyperlinkable terms (file,
420 | # class, module, and method names)
421 |
422 | def build_indices
423 | @files, @classes = RDoc::Generator::Context.build_indices(@toplevels,
424 | @options)
425 | end
426 |
427 | ##
428 | # Generate all the HTML. For the one-file case, we generate
429 | # all the information in to one big hash
430 |
431 | def generate_xml
432 | values = {
433 | :charset => @options.charset,
434 | :files => gen_into(@files),
435 | :classes => gen_into(@classes),
436 | :title => CGI.escapeHTML(@options.title),
437 | }
438 |
439 | template = RDoc::TemplatePage.new @template::ONE_PAGE
440 |
441 | opfile = if @options.op_name then
442 | open @options.op_name, 'w'
443 | else
444 | $stdout
445 | end
446 | template.write_html_on(opfile, values)
447 | end
448 |
449 | def gen_into(list)
450 | res = []
451 | list.each do |item|
452 | res << item.value_hash
453 | end
454 | res
455 | end
456 | end
457 |
--------------------------------------------------------------------------------
/lib/rdoc/generator/html/html.rb:
--------------------------------------------------------------------------------
1 | require 'rdoc/generator/html'
2 | require 'rdoc/generator/html/common'
3 |
4 | ##
5 | # = CSS2 RDoc HTML template
6 | #
7 | # This is a template for RDoc that uses XHTML 1.0 Strict and dictates a
8 | # bit more of the appearance of the output to cascading stylesheets than the
9 | # default. It was designed for clean inline code display, and uses DHTMl to
10 | # toggle the visibility of each method's source with each click on the
11 | # '[source]' link.
12 | #
13 | # This template *also* forms the basis of the frameless template.
14 | #
15 | # == Authors
16 | #
17 | # * Michael Granger
18 | #
19 | # Copyright (c) 2002, 2003 The FaerieMUD Consortium. Some rights reserved.
20 | #
21 | # This work is licensed under the Creative Commons Attribution License. To
22 | # view a copy of this license, visit
23 | # http://creativecommons.org/licenses/by/1.0/ or send a letter to Creative
24 | # Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
25 |
26 | module RDoc::Generator::HTML::HTML
27 |
28 | include RDoc::Generator::HTML::Common
29 |
30 | FONTS = "Verdana,Arial,Helvetica,sans-serif"
31 |
32 | STYLE = <<-EOF
33 | body {
34 | font-family: #{FONTS};
35 | font-size: 90%;
36 | margin: 0;
37 | margin-left: 40px;
38 | padding: 0;
39 | background: white;
40 | color: black;
41 | }
42 |
43 | h1, h2, h3, h4 {
44 | margin: 0;
45 | background: transparent;
46 | }
47 |
48 | h1 {
49 | font-size: 150%;
50 | }
51 |
52 | h2,h3,h4 {
53 | margin-top: 1em;
54 | }
55 |
56 | :link, :visited {
57 | background: #eef;
58 | color: #039;
59 | text-decoration: none;
60 | }
61 |
62 | :link:hover, :visited:hover {
63 | background: #039;
64 | color: #eef;
65 | }
66 |
67 | /* Override the base stylesheet's Anchor inside a table cell */
68 | td > :link, td > :visited {
69 | background: transparent;
70 | color: #039;
71 | text-decoration: none;
72 | }
73 |
74 | /* and inside a section title */
75 | .section-title > :link, .section-title > :visited {
76 | background: transparent;
77 | color: #eee;
78 | text-decoration: none;
79 | }
80 |
81 | /* === Structural elements =================================== */
82 |
83 | .index {
84 | margin: 0;
85 | margin-left: -40px;
86 | padding: 0;
87 | font-size: 90%;
88 | }
89 |
90 | .index :link, .index :visited {
91 | margin-left: 0.7em;
92 | }
93 |
94 | .index .section-bar {
95 | margin-left: 0px;
96 | padding-left: 0.7em;
97 | background: #ccc;
98 | font-size: small;
99 | }
100 |
101 | #classHeader, #fileHeader {
102 | width: auto;
103 | color: white;
104 | padding: 0.5em 1.5em 0.5em 1.5em;
105 | margin: 0;
106 | margin-left: -40px;
107 | border-bottom: 3px solid #006;
108 | }
109 |
110 | #classHeader :link, #fileHeader :link,
111 | #classHeader :visited, #fileHeader :visited {
112 | background: inherit;
113 | color: white;
114 | }
115 |
116 | #classHeader td, #fileHeader td {
117 | background: inherit;
118 | color: white;
119 | }
120 |
121 | #fileHeader {
122 | background: #057;
123 | }
124 |
125 | #classHeader {
126 | background: #048;
127 | }
128 |
129 | .class-name-in-header {
130 | font-size: 180%;
131 | font-weight: bold;
132 | }
133 |
134 | #bodyContent {
135 | padding: 0 1.5em 0 1.5em;
136 | }
137 |
138 | #description {
139 | padding: 0.5em 1.5em;
140 | background: #efefef;
141 | border: 1px dotted #999;
142 | }
143 |
144 | #description h1, #description h2, #description h3,
145 | #description h4, #description h5, #description h6 {
146 | color: #125;
147 | background: transparent;
148 | }
149 |
150 | #validator-badges {
151 | text-align: center;
152 | }
153 |
154 | #validator-badges img {
155 | border: 0;
156 | }
157 |
158 | #copyright {
159 | color: #333;
160 | background: #efefef;
161 | font: 0.75em sans-serif;
162 | margin-top: 5em;
163 | margin-bottom: 0;
164 | padding: 0.5em 2em;
165 | }
166 |
167 | /* === Classes =================================== */
168 |
169 | table.header-table {
170 | color: white;
171 | font-size: small;
172 | }
173 |
174 | .type-note {
175 | font-size: small;
176 | color: #dedede;
177 | }
178 |
179 | .section-bar {
180 | color: #333;
181 | border-bottom: 1px solid #999;
182 | margin-left: -20px;
183 | }
184 |
185 | .section-title {
186 | background: #79a;
187 | color: #eee;
188 | padding: 3px;
189 | margin-top: 2em;
190 | margin-left: -30px;
191 | border: 1px solid #999;
192 | }
193 |
194 | .top-aligned-row {
195 | vertical-align: top
196 | }
197 |
198 | .bottom-aligned-row {
199 | vertical-align: bottom
200 | }
201 |
202 | #diagram img {
203 | border: 0;
204 | }
205 |
206 | /* --- Context section classes ----------------------- */
207 |
208 | .context-row { }
209 |
210 | .context-item-name {
211 | font-family: monospace;
212 | font-weight: bold;
213 | color: black;
214 | }
215 |
216 | .context-item-value {
217 | font-size: small;
218 | color: #448;
219 | }
220 |
221 | .context-item-desc {
222 | color: #333;
223 | padding-left: 2em;
224 | }
225 |
226 | /* --- Method classes -------------------------- */
227 |
228 | .method-detail {
229 | background: #efefef;
230 | padding: 0;
231 | margin-top: 0.5em;
232 | margin-bottom: 1em;
233 | border: 1px dotted #ccc;
234 | }
235 |
236 | .method-heading {
237 | color: black;
238 | background: #ccc;
239 | border-bottom: 1px solid #666;
240 | padding: 0.2em 0.5em 0 0.5em;
241 | }
242 |
243 | .method-signature {
244 | color: black;
245 | background: inherit;
246 | }
247 |
248 | .method-name {
249 | font-weight: bold;
250 | }
251 |
252 | .method-args {
253 | font-style: italic;
254 | }
255 |
256 | .method-description {
257 | padding: 0 0.5em 0 0.5em;
258 | }
259 |
260 | /* --- Source code sections -------------------- */
261 |
262 | :link.source-toggle, :visited.source-toggle {
263 | font-size: 90%;
264 | }
265 |
266 | div.method-source-code {
267 | background: #262626;
268 | color: #ffdead;
269 | margin: 1em;
270 | padding: 0.5em;
271 | border: 1px dashed #999;
272 | overflow: auto;
273 | }
274 |
275 | div.method-source-code pre {
276 | color: #ffdead;
277 | }
278 |
279 | /* --- Ruby keyword styles --------------------- */
280 |
281 | .standalone-code {
282 | background: #221111;
283 | color: #ffdead;
284 | overflow: auto;
285 | }
286 |
287 | .ruby-constant {
288 | color: #7fffd4;
289 | background: transparent;
290 | }
291 |
292 | .ruby-keyword {
293 | color: #00ffff;
294 | background: transparent;
295 | }
296 |
297 | .ruby-ivar {
298 | color: #eedd82;
299 | background: transparent;
300 | }
301 |
302 | .ruby-operator {
303 | color: #00ffee;
304 | background: transparent;
305 | }
306 |
307 | .ruby-identifier {
308 | color: #ffdead;
309 | background: transparent;
310 | }
311 |
312 | .ruby-node {
313 | color: #ffa07a;
314 | background: transparent;
315 | }
316 |
317 | .ruby-comment {
318 | color: #b22222;
319 | font-weight: bold;
320 | background: transparent;
321 | }
322 |
323 | .ruby-regexp {
324 | color: #ffa07a;
325 | background: transparent;
326 | }
327 |
328 | .ruby-value {
329 | color: #7fffd4;
330 | background: transparent;
331 | }
332 | EOF
333 |
334 |
335 | #####################################################################
336 | ### H E A D E R T E M P L A T E
337 | #####################################################################
338 |
339 | HEADER = XHTML_STRICT_PREAMBLE + HTML_ELEMENT + <<-EOF
340 |
341 | <%= values[:title] %>
342 |
343 |
344 |
345 |
376 |
377 |
378 |
379 | EOF
380 |
381 | #####################################################################
382 | ### F O O T E R T E M P L A T E
383 | #####################################################################
384 |
385 | FOOTER = <<-EOF
386 |
389 |
390 |
391 |