hanna-nouveau 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+ group :development do
9
+ gem "bundler", "~> 1.0.0"
10
+ gem "jeweler", "~> 1.5.1"
11
+ gem "rcov", ">= 0"
12
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,18 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ git (1.2.5)
5
+ jeweler (1.5.2)
6
+ bundler (~> 1.0.0)
7
+ git (>= 1.2.5)
8
+ rake
9
+ rake (0.8.7)
10
+ rcov (0.9.9)
11
+
12
+ PLATFORMS
13
+ ruby
14
+
15
+ DEPENDENCIES
16
+ bundler (~> 1.0.0)
17
+ jeweler (~> 1.5.1)
18
+ rcov
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2009 Mislav Marohnić
2
+ Copyright (c) 2010, 2011 Erik Hollensbe
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
5
+ this software and associated documentation files (the "Software"), to deal in
6
+ the Software without restriction, including without limitation the rights to
7
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8
+ the Software, and to permit persons to whom the Software is furnished to do so,
9
+ subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,108 @@
1
+ = Hanna Nouveau — a better RDoc template, now for RDoc 2.5 and 3.0.
2
+
3
+ Based on the original Hanna by Mislav.
4
+
5
+ Hanna is an RDoc generator that scales. It's implemented in Haml, making the
6
+ sources clean and readable. It's built with simplicity, beauty and ease of
7
+ browsing in mind. (See more in {the
8
+ wiki}[http://github.com/mislav/hanna/wikis/home].)
9
+
10
+ Hanna gem is available from http://rubygems.org:
11
+
12
+ gem install hanna
13
+
14
+ The template was created by {Mislav}[http://mislav.caboo.se/] and since then
15
+ has seen contributions from:
16
+
17
+ 1. {Tony Strauss}[http://github.com/DesigningPatterns], who participated from
18
+ the early start and made tons of fixes and enhancements to the template;
19
+ 2. {Hongli Lai}[http://blog.phusion.nl/] with the search filter for methods.
20
+ 3. {Erik Hollensbe}[http://github.com/erikh] a serious refactoring and up to
21
+ date with RDoc 2.5.x.
22
+ 4. {James Tucker}[http://github.com/raggi] minor cleanups for Erik.
23
+
24
+ == Usage
25
+
26
+ rdoc -o doc -f hanna lib/*.rb
27
+
28
+ An alternative is to set the `RDOCOPT` environment variable:
29
+
30
+ RDOCOPT="-f hanna"
31
+
32
+ This will make RDoc always use Hanna unless it is explicitly overridden.
33
+
34
+ == Integrating with RubyGems
35
+
36
+ Another neat trick is to put the following line in your .gemrc, this will make
37
+ RubyGems use Hanna for all rdoc generation:
38
+
39
+ rdoc: -f hanna
40
+
41
+ This will make RubyGems use Hanna when generating documentation for installed
42
+ gems.
43
+
44
+ == Rake task
45
+
46
+ For repeated generation of API docs, it's better to set up a Rake task. Simply
47
+ add the hanna format argument to your RDoc::Task options:
48
+
49
+ require 'hanna'
50
+ require 'rdoc/task'
51
+ RDoc::Task.new do |rdoc|
52
+ rdoc.options.push '-f', 'hanna'
53
+ end
54
+
55
+ Tip: you can do this in the Rakefile of your Rails project before running
56
+ `rake doc:rails`.
57
+
58
+ Here is an example of a task for the {rdbi
59
+ library}[http://github.com/rdbi/rdbi/tree/master/Rakefile]:
60
+
61
+ require 'hanna'
62
+ require 'rdoc/task'
63
+ RDoc::Task.new do |rdoc|
64
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
65
+
66
+ rdoc.options.push '-f', 'hanna'
67
+ rdoc.main = 'README.rdoc'
68
+ rdoc.rdoc_dir = 'rdoc'
69
+ rdoc.title = "RDBI #{version} Documentation"
70
+ rdoc.rdoc_files.include('README*')
71
+ rdoc.rdoc_files.include('lib/**/*.rb')
72
+ end
73
+
74
+ == Generating documentation for installed gems
75
+
76
+ The gem comes with a RubyGems plugin that overrides the gem default arguments
77
+ to RDoc to use Hanna.
78
+
79
+ To regenerate documentation for a specific gem, first configure RubyGems to
80
+ use Hanna, by adding the following to your .gemrc:
81
+
82
+ rdoc: -f hanna
83
+
84
+ Now to regenerate documentation for an already installed gem:
85
+
86
+ gem rdoc mygem --overwrite
87
+
88
+ To regenerate documentation for all gems:
89
+
90
+ gem rdoc --all --overwrite
91
+
92
+ To easily browse your newly created documentation, use `gem server`.
93
+
94
+ == You can help
95
+
96
+ Don't like something? Think you can design better? (You probably can.)
97
+
98
+ I think of Hanna as the first RDoc template that's actually _maintainable_.
99
+ First thing I have done is converted the original HTML template to Haml and
100
+ Sass, cleaning up and removing the (ridiculous amount of) duplication. Also,
101
+ the template fragments are now in _separate files_.
102
+
103
+ Ultimately, I'd like to lose the frameset. Currently that is far from possible
104
+ because the whole RDoc HTML Generator is built for frames. Still, that is my
105
+ goal.
106
+
107
+ This is git. Fork it, hack away, tell me about it!
108
+
data/Rakefile ADDED
@@ -0,0 +1,55 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gem|
14
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
+ gem.name = "hanna-nouveau"
16
+ gem.homepage = "http://github.com/erikh/hanna-nouveau"
17
+ gem.license = "MIT"
18
+ gem.summary = %Q{A rework of the Hanna generator for RDoc 2.5 and 3.0}
19
+ gem.description = %Q{}
20
+ gem.email = "erik@hollensbe.org"
21
+ gem.authors = ["Erik Hollensbe"]
22
+ # Include your dependencies below. Runtime dependencies are required when using your gem,
23
+ # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
24
+ gem.add_runtime_dependency 'haml'
25
+ gem.add_runtime_dependency 'rdoc'
26
+ gem.add_development_dependency 'jeweler'
27
+ gem.add_development_dependency 'rake'
28
+ end
29
+ Jeweler::RubygemsDotOrgTasks.new
30
+
31
+ require 'rake/testtask'
32
+ Rake::TestTask.new(:test) do |test|
33
+ test.libs << 'lib' << 'test'
34
+ test.pattern = 'test/**/test_*.rb'
35
+ test.verbose = true
36
+ end
37
+
38
+ require 'rcov/rcovtask'
39
+ Rcov::RcovTask.new do |test|
40
+ test.libs << 'test'
41
+ test.pattern = 'test/**/test_*.rb'
42
+ test.verbose = true
43
+ end
44
+
45
+ task :default => :test
46
+
47
+ require 'rake/rdoctask'
48
+ Rake::RDocTask.new do |rdoc|
49
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
50
+
51
+ rdoc.rdoc_dir = 'rdoc'
52
+ rdoc.title = "hanna-nouveau #{version}"
53
+ rdoc.rdoc_files.include('README*')
54
+ rdoc.rdoc_files.include('lib/**/*.rb')
55
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.0
data/hanna.gemspec ADDED
@@ -0,0 +1,21 @@
1
+ Gem::Specification.new do |gem|
2
+ gem.name = 'hanna'
3
+ gem.version = eval(File.read('lib/hanna.rb')[/^\s+VERSION\s+=\s+(.*)$/, 1])
4
+ gem.date = Time.now.strftime('%Y-%m-%d')
5
+
6
+ gem.summary = "An RDoc template that scales"
7
+ gem.description = "Hanna is an RDoc implemented in Haml, making its source clean and maintainable. It's built with simplicity, beauty and ease of browsing in mind."
8
+
9
+ gem.files = Dir['Rakefile', 'lib/**/*', 'README*', 'LICENSE*']
10
+
11
+ gem.add_dependency 'rdoc', '~> 2.5.9'
12
+ gem.add_dependency 'haml', '~> 2.2.8'
13
+ gem.add_dependency 'rake', '~> 0.8.2'
14
+
15
+ gem.email = 'mislav.marohnic@gmail.com'
16
+ gem.homepage = 'http://github.com/mislav/' + gem.name
17
+ gem.authors = ['Mislav Marohnić']
18
+
19
+ gem.has_rdoc = false
20
+ gem.rubyforge_project = nil
21
+ end
data/lib/hanna.rb ADDED
@@ -0,0 +1,323 @@
1
+ # = A better RDoc HTML template
2
+ #
3
+ # Code rewritten by:
4
+ # Erik Hollensbe <erik@hollensbe.org>
5
+ #
6
+ # RubyGems integration properly done by:
7
+ # James Tucker (aka raggi)
8
+ #
9
+ # Original Authors:
10
+ # Mislav Marohnić <mislav.marohnic@gmail.com>
11
+ # Tony Strauss (http://github.com/DesigningPatterns)
12
+ # Michael Granger <ged@FaerieMUD.org>, who had maintained the original RDoc template
13
+
14
+ module Hanna
15
+ VERSION = '0.1.13'
16
+ end
17
+
18
+ require 'pathname'
19
+ require 'haml'
20
+ require 'sass'
21
+ require 'rdoc/rdoc'
22
+ require 'rdoc/generator'
23
+
24
+ module RDoc #:nodoc:
25
+ module Generator #:nodoc:
26
+ end
27
+ end
28
+
29
+ class RDoc::Generator::Hanna
30
+ STYLE = 'styles.sass'
31
+ LAYOUT = 'layout.haml'
32
+
33
+ INDEX_PAGE = 'index.haml'
34
+ CLASS_PAGE = 'page.haml'
35
+ METHOD_LIST_PAGE = 'method_list.haml'
36
+ FILE_PAGE = CLASS_PAGE
37
+ SECTIONS_PAGE = 'sections.haml'
38
+
39
+ FILE_INDEX = 'file_index.haml'
40
+ CLASS_INDEX = 'class_index.haml'
41
+ METHOD_INDEX = 'method_index.haml'
42
+
43
+ CLASS_DIR = 'classes'
44
+ FILE_DIR = 'files'
45
+
46
+ INDEX_OUT = 'index.html'
47
+ FILE_INDEX_OUT = 'fr_file_index.html'
48
+ CLASS_INDEX_OUT = 'fr_class_index.html'
49
+ METHOD_INDEX_OUT = 'fr_method_index.html'
50
+ STYLE_OUT = File.join('css', 'style.css')
51
+
52
+ # EPIC CUT AND PASTE TIEM NAO -- GG
53
+ RDoc::RDoc.add_generator( self )
54
+
55
+ def self::for( options )
56
+ new( options )
57
+ end
58
+
59
+ def initialize( options )
60
+ @options = options
61
+
62
+ @templatedir = Pathname.new File.expand_path('../hanna/template_files', __FILE__)
63
+
64
+ @files = nil
65
+ @classes = nil
66
+ @methods = nil
67
+ @attributes = nil
68
+
69
+ @basedir = Pathname.pwd.expand_path
70
+ end
71
+
72
+ def generate( top_levels )
73
+ @outputdir = Pathname.new( @options.op_dir ).expand_path( @basedir )
74
+
75
+ @files = top_levels.sort
76
+ @classes = RDoc::TopLevel.all_classes_and_modules.sort
77
+ @methods = @classes.map(&:method_list).flatten.sort
78
+ @attributes = @classes.map(&:attributes).flatten.sort
79
+
80
+ # Now actually write the output
81
+ write_static_files
82
+ generate_indexes
83
+ generate_class_files
84
+ generate_file_files
85
+
86
+ rescue StandardError => err
87
+ p [ err.class.name, err.message, err.backtrace.join("\n ") ]
88
+ raise
89
+ end
90
+
91
+ def write_static_files
92
+ css_dir = outjoin('css')
93
+
94
+ unless File.directory?(css_dir)
95
+ FileUtils.mkdir css_dir
96
+ end
97
+
98
+ File.open(File.join(css_dir, 'style.css'), 'w') { |f| f << Sass::Engine.new(File.read(templjoin(STYLE))).to_css }
99
+ end
100
+
101
+ # FIXME refactor
102
+ def generate_indexes
103
+ @main_page_uri = @files.find { |f| f.name == @options.main_page }.path rescue ''
104
+ File.open(outjoin(INDEX_OUT), 'w') { |f| f << haml_file(templjoin(INDEX_PAGE)).to_html(binding) }
105
+
106
+ generate_index(FILE_INDEX_OUT, FILE_INDEX, 'File', { :files => @files})
107
+ generate_index(CLASS_INDEX_OUT, CLASS_INDEX, 'Class', { :classes => @classes })
108
+ generate_index(METHOD_INDEX_OUT, METHOD_INDEX, 'Method', { :methods => @methods, :attributes => @attributes })
109
+ end
110
+
111
+ def generate_index(outfile, templfile, index_name, values)
112
+ values.merge!({
113
+ :stylesheet => STYLE_OUT,
114
+ :list_title => "#{index_name} Index"
115
+ })
116
+
117
+ index = haml_file(templjoin(templfile))
118
+
119
+ File.open(outjoin(outfile), 'w') do |f|
120
+ f << with_layout(values) do
121
+ index.to_html(binding, values)
122
+ end
123
+ end
124
+ end
125
+
126
+ def generate_file_files
127
+ file_page = haml_file(templjoin(FILE_PAGE))
128
+ method_list_page = haml_file(templjoin(METHOD_LIST_PAGE))
129
+
130
+ # FIXME non-Ruby files
131
+ @files.each do |file|
132
+ path = Pathname.new(file.path)
133
+ stylesheet = Pathname.new(STYLE_OUT).relative_path_from(path.dirname)
134
+
135
+ values = {
136
+ :file => file,
137
+ :entry => file,
138
+ :stylesheet => stylesheet,
139
+ :classmod => nil,
140
+ :title => file.base_name,
141
+ :list_title => nil,
142
+ :description => file.description
143
+ }
144
+
145
+ result = with_layout(values) do
146
+ file_page.to_html(binding, :values => values) do
147
+ method_list_page.to_html(binding, values)
148
+ end
149
+ end
150
+
151
+ # FIXME XXX sanity check
152
+ dir = path.dirname
153
+ unless File.directory? dir
154
+ FileUtils.mkdir_p dir
155
+ end
156
+
157
+ File.open(outjoin(file.path), 'w') { |f| f << result }
158
+ end
159
+ end
160
+
161
+ def generate_class_files
162
+ class_page = haml_file(templjoin(CLASS_PAGE))
163
+ method_list_page = haml_file(templjoin(METHOD_LIST_PAGE))
164
+ sections_page = haml_file(templjoin(SECTIONS_PAGE))
165
+ # FIXME refactor
166
+
167
+ @classes.each do |klass|
168
+ outfile = classfile(klass)
169
+ stylesheet = Pathname.new(STYLE_OUT).relative_path_from(outfile.dirname)
170
+
171
+ values = {
172
+ :file => klass.path,
173
+ :entry => klass,
174
+ :stylesheet => stylesheet,
175
+ :classmod => klass.type,
176
+ :title => klass.full_name,
177
+ :list_title => nil,
178
+ :description => klass.description,
179
+ :section => {
180
+ # FIXME linkify
181
+ :classlist => '<ol>' + klass.classes_and_modules.inject('') { |x,y| x << '<li>' + y.name + '</li>' } + '</ol>',
182
+ :constants => klass.constants,
183
+ :aliases => klass.method_list.select { |x| x.is_alias_for },
184
+ :attributes => klass.attributes,
185
+ :method_list => klass.method_list.select { |x| !x.is_alias_for }
186
+ }
187
+ }
188
+
189
+ result = with_layout(values) do
190
+ class_page.to_html(binding, :values => values) do
191
+ method_list_page.to_html(binding, :values => values) +
192
+ sections_page.to_html(binding, :values => values)
193
+ end
194
+ end
195
+
196
+ # FIXME XXX sanity check
197
+ dir = outfile.dirname
198
+ unless File.directory? dir
199
+ FileUtils.mkdir_p dir
200
+ end
201
+
202
+ File.open(outfile, 'w') { |f| f << result }
203
+ end
204
+ end
205
+
206
+ def with_layout(values)
207
+ layout = haml_file(templjoin(LAYOUT))
208
+ layout.to_html(binding, :values => values) { yield }
209
+ end
210
+
211
+ def sanitize_code_blocks(text)
212
+ text.gsub(/<pre>(.+?)<\/pre>/m) do
213
+ code = $1.sub(/^\s*\n/, '')
214
+ indent = code.gsub(/\n[ \t]*\n/, "\n").scan(/^ */).map{ |i| i.size }.min
215
+ code.gsub!(/^#{' ' * indent}/, '') if indent > 0
216
+
217
+ "<pre>#{code}</pre>"
218
+ end
219
+ end
220
+
221
+ # probably should bring in nokogiri/libxml2 to do this right.. not sure if
222
+ # it's worth it.
223
+ def frame_link(content)
224
+ content.gsub(%r!<a href="http://[^>]*>!).each do |tag|
225
+ a_tag, rest = tag.split(' ', 2)
226
+ rest.gsub!(/target="[^"]*"/, '')
227
+ a_tag + ' target="_top" ' + rest
228
+ end
229
+ end
230
+
231
+ def class_dir
232
+ CLASS_DIR
233
+ end
234
+
235
+ def file_dir
236
+ FILE_DIR
237
+ end
238
+
239
+ def h(html)
240
+ CGI::escapeHTML(html)
241
+ end
242
+
243
+ # XXX may my sins be not visited upon my sons.
244
+ def render_class_tree(entries, parent=nil)
245
+ namespaces = { }
246
+
247
+ entries.sort.inject('') do |out, klass|
248
+ unless namespaces[klass.full_name]
249
+ if parent
250
+ text = '<span class="parent">%s::</span>%s' % [parent.full_name, klass.name]
251
+ else
252
+ text = klass.name
253
+ end
254
+
255
+ if klass.document_self
256
+ out << '<li>'
257
+ out << link_to(text, classfile(klass))
258
+ end
259
+
260
+ subentries = @classes.select { |x| x.full_name[/^#{klass.full_name}::/] }
261
+ subentries.each { |x| namespaces[x.full_name] = true }
262
+ out << "\n<ol>" + render_class_tree(subentries, klass) + "\n</ol>"
263
+
264
+ if klass.document_self
265
+ out << '</li>'
266
+ end
267
+ end
268
+
269
+ out
270
+ end
271
+ end
272
+
273
+ def build_javascript_search_index(entries)
274
+ result = "var search_index = [\n"
275
+ entries.each do |entry|
276
+ method_name = entry.name
277
+ module_name = entry.parent_name
278
+ # FIXME link
279
+ html = link_to_method(entry, [classfile(entry.parent), (entry.aref rescue "method-#{entry.html_name}")].join('#'))
280
+ result << " { method: '#{method_name.downcase}', " +
281
+ "module: '#{module_name.downcase}', " +
282
+ "html: '#{html}' },\n"
283
+ end
284
+ result << "]"
285
+ result
286
+ end
287
+
288
+ def link_to(text, url = nil, classname = nil)
289
+ class_attr = classname ? ' class="%s"' % classname : ''
290
+
291
+ if url
292
+ %[<a target="docwin" href="#{url}"#{class_attr}>#{text}</a>]
293
+ elsif classname
294
+ %[<span#{class_attr}>#{text}</span>]
295
+ else
296
+ text
297
+ end
298
+ end
299
+
300
+ # +method_text+ is in the form of "ago (ActiveSupport::TimeWithZone)".
301
+ def link_to_method(entry, url = nil, classname = nil)
302
+ method_name = entry.pretty_name rescue entry.name
303
+ module_name = entry.parent_name rescue entry.name
304
+ link_to %Q(<span class="method_name">#{h method_name}</span> <span class="module_name">(#{h module_name})</span>), url, classname
305
+ end
306
+
307
+ def classfile(klass)
308
+ # FIXME sloooooooow
309
+ Pathname.new(File.join(CLASS_DIR, klass.full_name.split('::')) + '.html')
310
+ end
311
+
312
+ def outjoin(name)
313
+ File.join(@outputdir, name)
314
+ end
315
+
316
+ def templjoin(name)
317
+ File.join(@templatedir, name)
318
+ end
319
+
320
+ def haml_file(file)
321
+ Haml::Engine.new(File.read(file), :format => :html4)
322
+ end
323
+ end