docubot 0.0.1 → 0.2
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/bin/docubot +83 -14
- data/lib/docubot.rb +33 -10
- data/lib/docubot/bundle.rb +68 -0
- data/lib/docubot/converter.rb +13 -9
- data/lib/docubot/converters/haml.rb +9 -0
- data/lib/docubot/converters/html.rb +5 -0
- data/lib/docubot/converters/markdown.rb +12 -4
- data/lib/docubot/converters/raw_code.rb +5 -0
- data/lib/docubot/converters/textile.rb +10 -0
- data/lib/docubot/glossary.rb +44 -0
- data/lib/docubot/index.rb +66 -0
- data/lib/docubot/page.rb +142 -18
- data/lib/docubot/shells/default/0_License.md +60 -0
- data/lib/docubot/shells/default/Appendix/Glossary.md +5 -0
- data/lib/docubot/shells/default/Appendix/Index_Page.md +8 -0
- data/lib/docubot/shells/default/Appendix/Table of Contents.md +5 -0
- data/lib/docubot/shells/default/_static/logo.png +0 -0
- data/lib/docubot/shells/default/index.txt +3 -0
- data/lib/docubot/shells/docubot-help/0_License.md +21 -0
- data/lib/docubot/shells/docubot-help/1_Getting_Started.md +48 -0
- data/lib/docubot/templates/default/page.haml b/data/lib/docubot/shells/docubot-help/2_Basic_Concepts/0 The → Metasection.md +0 -0
- data/lib/docubot/shells/docubot-help/2_Basic_Concepts/1 Interpage Links.md +1 -0
- data/lib/docubot/shells/docubot-help/2_Basic_Concepts/2 Generating the Output.md +1 -0
- data/lib/docubot/shells/docubot-help/2_Basic_Concepts/3 Customizing Templates.md +1 -0
- data/lib/docubot/shells/docubot-help/2_Basic_Concepts/4 Adding Images.md +2 -0
- data/lib/docubot/shells/docubot-help/2_Basic_Concepts/index.md +6 -0
- data/lib/docubot/shells/docubot-help/3_Advanced_Topics/Controlling Glossary.md +3 -0
- data/lib/docubot/shells/docubot-help/3_Advanced_Topics/Controlling Indexing.md +10 -0
- data/lib/docubot/shells/docubot-help/3_Advanced_Topics/Controlling the Table of Contents.md +7 -0
- data/lib/docubot/shells/docubot-help/3_Advanced_Topics/Switching Page Templates.md +1 -0
- data/lib/docubot/shells/docubot-help/4_Appendix/Glossary.md +5 -0
- data/lib/docubot/shells/docubot-help/4_Appendix/Index_Page.md +6 -0
- data/lib/docubot/shells/docubot-help/4_Appendix/Table of Contents.md +7 -0
- data/lib/docubot/shells/docubot-help/_glossary/Template.md +1 -0
- data/lib/docubot/shells/docubot-help/_static/glider.png +0 -0
- data/lib/docubot/shells/docubot-help/index.txt +8 -0
- data/lib/docubot/shells/nvphysx/0_License.md +3 -0
- data/lib/docubot/shells/nvphysx/1_Getting_Started.haml +51 -0
- data/lib/docubot/shells/nvphysx/Appendix/Glossary.md +7 -0
- data/lib/docubot/shells/nvphysx/_glossary/APEX.md +1 -0
- data/lib/docubot/shells/nvphysx/_glossary/NVIDIA.md +1 -0
- data/lib/docubot/shells/nvphysx/_glossary/PhysX.textile +3 -0
- data/lib/docubot/shells/nvphysx/_static/NVBadge_3D.png +0 -0
- data/lib/docubot/shells/nvphysx/_static/PhysXbyNV_Black.png +0 -0
- data/lib/docubot/shells/nvphysx/_templates/_root/bg_green_bar_revised.gif +0 -0
- data/lib/docubot/shells/nvphysx/_templates/_root/close.png +0 -0
- data/lib/docubot/shells/nvphysx/_templates/_root/common.css +264 -0
- data/lib/docubot/shells/nvphysx/_templates/_root/glossary.css +4 -0
- data/lib/docubot/shells/nvphysx/_templates/_root/glossary.js +24 -0
- data/lib/docubot/shells/nvphysx/_templates/_root/nvdevtools.js +31 -0
- data/lib/docubot/shells/nvphysx/_templates/_root/nvidia-logo.gif +0 -0
- data/lib/docubot/shells/nvphysx/_templates/_root/right-sidebar.png +0 -0
- data/lib/docubot/shells/nvphysx/_templates/top.haml +28 -0
- data/lib/docubot/shells/nvphysx/index.txt +5 -0
- data/lib/docubot/snippet.rb +4 -3
- data/lib/docubot/snippets/glossary.rb +6 -3
- data/lib/docubot/snippets/index_entries.rb +7 -0
- data/lib/docubot/templates/_root/common.css +107 -0
- data/lib/docubot/templates/_root/toc.css +5 -0
- data/lib/docubot/templates/_root/toc.js +4 -0
- data/lib/docubot/templates/glossary.haml +5 -0
- data/lib/docubot/templates/index.haml +14 -0
- data/lib/docubot/templates/page.haml +1 -0
- data/lib/docubot/templates/section.haml +10 -0
- data/lib/docubot/templates/toc.haml +10 -0
- data/lib/docubot/templates/top.haml +25 -0
- data/lib/docubot/writer.rb +24 -0
- data/lib/docubot/writers/chm.rb +73 -0
- data/lib/docubot/writers/chm/hhc.erb +27 -0
- data/lib/docubot/writers/chm/hhk.erb +28 -0
- data/lib/docubot/writers/chm/hhp.erb +23 -0
- data/lib/docubot/writers/html.rb +75 -0
- data/test/all.rb +2 -0
- data/test/site1/A Slight Change of Heart/3_more_crap.haml +17 -0
- data/test/site1/appendices/gkheadftw.html +2 -0
- data/test/site1/appendices/index.md +2 -0
- data/test/site1/preamble.haml +4 -0
- data/test/site1/raw.textile +10 -0
- metadata +88 -14
- data/lib/docubot/generator.rb +0 -35
- data/lib/docubot/section.rb +0 -16
- data/lib/docubot/template.rb +0 -13
- data/lib/docubot/templates/default/section.haml +0 -0
- data/test/site1/A Slight Change of Heart/3_more_crap.md +0 -5
- data/test/site1/raw.md +0 -3
data/bin/docubot
CHANGED
|
@@ -1,36 +1,105 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
1
|
+
#!/usr/bin/env ruby -KU
|
|
2
|
+
# encoding: UTF-8
|
|
3
|
+
|
|
4
|
+
# Hack to allow the binary to be run without the gem installed
|
|
5
|
+
$: << File.join( File.dirname( __FILE__ ), '..', 'lib' )
|
|
2
6
|
|
|
3
7
|
require 'rubygems'
|
|
4
8
|
require 'docubot'
|
|
5
9
|
|
|
6
10
|
USAGE = <<ENDUSAGE
|
|
7
11
|
Usage:
|
|
8
|
-
docubot [-
|
|
12
|
+
docubot [-h] [create [-s shell] [-f]] directory [-w writer] [-o output_file]
|
|
9
13
|
ENDUSAGE
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
HELP = <<ENDHELP
|
|
16
|
+
-h, --help Show this help.
|
|
17
|
+
create Create a starter directory filled with example files;
|
|
18
|
+
also copies the template for easy modification, if desired.
|
|
19
|
+
-s, --shell The shell to copy from.
|
|
20
|
+
Available shells: #{DocuBot::SHELLS.join(', ')}
|
|
21
|
+
-f, --force Force create over an existing directory,
|
|
22
|
+
deleting any existing files.
|
|
23
|
+
-w, --writer The output type to create [Defaults to 'chm']
|
|
24
|
+
Available writers: #{DocuBot::Writer::INSTALLED_WRITERS.join(', ')}
|
|
25
|
+
-o, --output The file or folder (depending on the writer) to create.
|
|
26
|
+
[Default value depends on the writer chosen.]
|
|
27
|
+
|
|
28
|
+
ENDHELP
|
|
29
|
+
|
|
30
|
+
ARGS = { :shell=>'default', :writer=>'chm' }
|
|
12
31
|
UNFLAGGED_ARGS = [ :directory ]
|
|
13
32
|
next_arg = UNFLAGGED_ARGS.first
|
|
14
33
|
ARGV.each{ |arg|
|
|
15
34
|
case arg
|
|
16
35
|
when '-h','--help'
|
|
17
36
|
ARGS[:help] = true
|
|
18
|
-
when '
|
|
19
|
-
|
|
37
|
+
when 'create'
|
|
38
|
+
ARGS[:create] = true
|
|
39
|
+
when '-f','--force'
|
|
40
|
+
ARGS[:force] = true
|
|
41
|
+
when '-s','--shell'
|
|
42
|
+
next_arg = :shell
|
|
43
|
+
when '-w','--writer'
|
|
44
|
+
next_arg = :writer
|
|
45
|
+
when '-o','--output'
|
|
46
|
+
next_arg = :output
|
|
20
47
|
else
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
48
|
+
if next_arg
|
|
49
|
+
ARGS[next_arg] = arg
|
|
50
|
+
UNFLAGGED_ARGS.delete( next_arg )
|
|
51
|
+
end
|
|
52
|
+
next_arg = UNFLAGGED_ARGS.first
|
|
26
53
|
end
|
|
27
54
|
}
|
|
28
55
|
|
|
29
56
|
if ARGS[:help] or !ARGS[:directory]
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
57
|
+
puts USAGE
|
|
58
|
+
puts HELP if ARGS[:help]
|
|
59
|
+
exit
|
|
33
60
|
end
|
|
34
61
|
|
|
35
|
-
|
|
62
|
+
if ARGS[:create]
|
|
63
|
+
require 'fileutils'
|
|
64
|
+
|
|
65
|
+
unless DocuBot::SHELLS.include?( ARGS[:shell] )
|
|
66
|
+
puts " Error: '#{ARGS[:shell]}' is not a valid shell.",
|
|
67
|
+
" Available shells: #{DocuBot::SHELLS.join(', ')}",
|
|
68
|
+
" (Shells are installed in #{DocuBot::DIR/'shells'})", ""
|
|
69
|
+
exit 1
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
if File.exist?( ARGS[:directory] )
|
|
73
|
+
if ARGS[:force]
|
|
74
|
+
# TODO: confirmation?
|
|
75
|
+
# TODO: May be able to just use :force=>true for cp_r
|
|
76
|
+
FileUtils.rm_rf( ARGS[:directory] )
|
|
77
|
+
else
|
|
78
|
+
puts " Error: directory '#{ARGS[:directory]}' already exists.",
|
|
79
|
+
" Use the --force option to forcibly overwrite.", ""
|
|
80
|
+
exit 1
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
dest = File.expand_path( ARGS[:directory] )
|
|
85
|
+
src = File.expand_path( DocuBot::SHELL_DIR/ARGS[:shell] )
|
|
86
|
+
puts " Creating: #{dest}",
|
|
87
|
+
" as copy of: #{src}", ""
|
|
88
|
+
|
|
89
|
+
# Copy template files first so that the shell can overwrite if it wants
|
|
90
|
+
FileUtils.mkdir_p( dest )
|
|
91
|
+
FileUtils.cp_r( DocuBot::TEMPLATE_DIR, dest/'_templates' )
|
|
92
|
+
|
|
93
|
+
Dir.chdir src do
|
|
94
|
+
Dir['**/*.*'].each do |file|
|
|
95
|
+
dest_file = dest/file
|
|
96
|
+
dest_dir = File.dirname(dest_file)
|
|
97
|
+
FileUtils.mkdir_p( dest_dir )
|
|
98
|
+
FileUtils.cp( file, dest_file )
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
else
|
|
102
|
+
bundle = DocuBot::Bundle.new( ARGS[:directory] )
|
|
103
|
+
bundle.write( ARGS[:writer], ARGS[:output] )
|
|
104
|
+
end
|
|
36
105
|
|
data/lib/docubot.rb
CHANGED
|
@@ -1,14 +1,37 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
# Assume all files read in are UTF-8 format
|
|
3
|
+
if Object.const_defined? "Encoding"
|
|
4
|
+
Encoding.default_external = Encoding.default_internal = 'UTF-8'
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
# Wicked monkey patch to avoid File.join verbosity everywhere
|
|
8
|
+
class String
|
|
9
|
+
def / ( other )
|
|
10
|
+
File.join( self, other )
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
require 'fileutils'
|
|
15
|
+
|
|
16
|
+
module FileUtils
|
|
17
|
+
def self.win_path( path )
|
|
18
|
+
path.gsub( '/', '\\' )
|
|
7
19
|
end
|
|
8
20
|
end
|
|
21
|
+
|
|
22
|
+
module DocuBot
|
|
23
|
+
VERSION = '0.2'
|
|
24
|
+
DIR = File.expand_path( File.dirname( __FILE__ ) )
|
|
25
|
+
|
|
26
|
+
TEMPLATE_DIR = DIR / 'docubot/templates'
|
|
27
|
+
SHELL_DIR = DIR / 'docubot/shells'
|
|
28
|
+
Dir.chdir( SHELL_DIR ){ SHELLS = Dir['*'] }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
require 'docubot/snippet'
|
|
9
32
|
require 'docubot/converter'
|
|
10
|
-
require 'docubot/
|
|
33
|
+
require 'docubot/writer'
|
|
11
34
|
require 'docubot/page'
|
|
12
|
-
require 'docubot/
|
|
13
|
-
require 'docubot/
|
|
14
|
-
require 'docubot/
|
|
35
|
+
require 'docubot/glossary'
|
|
36
|
+
require 'docubot/index'
|
|
37
|
+
require 'docubot/bundle'
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
class DocuBot::Bundle
|
|
3
|
+
attr_reader :toc, :extras, :glossary, :index, :source
|
|
4
|
+
|
|
5
|
+
def initialize( source_directory )
|
|
6
|
+
@source = File.expand_path( source_directory )
|
|
7
|
+
raise "DocuBot cannot find directory #{@source}. Exiting." unless File.exists?( @source )
|
|
8
|
+
@extras = []
|
|
9
|
+
@glossary = DocuBot::Glossary.new( self, @source/'_glossary' )
|
|
10
|
+
@index = DocuBot::Index.new( self )
|
|
11
|
+
Dir.chdir( @source ) do
|
|
12
|
+
@toc = DocuBot::Page.new( ".", "Table of Contents" )
|
|
13
|
+
@toc.bundle = self
|
|
14
|
+
@toc.meta['glossary'] = @glossary
|
|
15
|
+
@toc.meta['index'] = @index
|
|
16
|
+
pages_by_path = { '.'=>@toc }
|
|
17
|
+
|
|
18
|
+
files_and_folders = Dir[ '**/*' ]
|
|
19
|
+
files_and_folders.reject!{ |f| File.basename(f) =~ /^index\.[^.]+$/ || File.basename(f) == '_static' || File.basename(f) == '_glossary' }
|
|
20
|
+
files_and_folders.reject!{ |f| f =~ /\b_templates\b/ }
|
|
21
|
+
files_and_folders.each do |item|
|
|
22
|
+
extension = File.extname( item )[ 1..-1 ]
|
|
23
|
+
item_is_page = File.directory?(item) || DocuBot::Converter.by_type[extension]
|
|
24
|
+
if item_is_page
|
|
25
|
+
parent = pages_by_path[ File.dirname( item ) ]
|
|
26
|
+
page = DocuBot::Page.new( item )
|
|
27
|
+
page.bundle = self
|
|
28
|
+
pages_by_path[ item ] = page
|
|
29
|
+
parent << page if parent
|
|
30
|
+
if item =~ /\b_glossary\b/
|
|
31
|
+
@glossary << page
|
|
32
|
+
end
|
|
33
|
+
@index.process_page( page )
|
|
34
|
+
|
|
35
|
+
# TODO: Move this bloat elsewhere.
|
|
36
|
+
if page.toc?
|
|
37
|
+
html = page.to_html
|
|
38
|
+
page.toc.scan /[a-z][\w.:-]*/ do |id|
|
|
39
|
+
# TODO: Maybe a lightweight HTML parser would be faster here? (Certainly more robust.)
|
|
40
|
+
if title = html[/\bid *= *['"]#{id}['"][^>]*>([^<]+)/,1]
|
|
41
|
+
page << DocuBot::SubLink.new( page, title.strip, id )
|
|
42
|
+
else
|
|
43
|
+
warn "Could not find requested toc anchor '##{id}' on #{page.html_path}"
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
else
|
|
49
|
+
# TODO: Anything better needed?
|
|
50
|
+
@extras << item
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def write( writer_type, destination=nil)
|
|
57
|
+
writer = DocuBot::Writer.by_type[ writer_type.to_s.downcase ]
|
|
58
|
+
if writer
|
|
59
|
+
writer.new( self ).write( destination )
|
|
60
|
+
unless @glossary.missing_terms.empty?
|
|
61
|
+
warn "The following glossary terms were never defined:\n#{@glossary.missing_terms.map{|t|t.inspect}.join(', ')}"
|
|
62
|
+
end
|
|
63
|
+
else
|
|
64
|
+
raise "Unknown writer '#{writer_type}'; available types: #{DocuBot::Writer::INSTALLED_WRITERS.join ', '}"
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
end
|
data/lib/docubot/converter.rb
CHANGED
|
@@ -1,21 +1,25 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
1
2
|
module DocuBot
|
|
2
3
|
module Converter
|
|
3
|
-
|
|
4
|
-
def
|
|
5
|
-
types.each{ |type|
|
|
4
|
+
@by_type = {}
|
|
5
|
+
def self.to_convert( *types, &block )
|
|
6
|
+
types.each{ |type| @by_type[type.to_s] = block }
|
|
6
7
|
end
|
|
7
8
|
def self.by_type
|
|
8
|
-
|
|
9
|
+
@by_type
|
|
9
10
|
end
|
|
10
11
|
end
|
|
11
12
|
|
|
12
|
-
def self.convert_to_html( source, type )
|
|
13
|
-
converter = DocuBot::Converter.by_type[ type.to_s ]
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
def self.convert_to_html( page, source, type )
|
|
14
|
+
if converter = DocuBot::Converter.by_type[ type.to_s ]
|
|
15
|
+
puts "Converting #{type}: #{source.inspect[0..60]}" if $DEBUG
|
|
16
|
+
converter[ page, source ]
|
|
17
|
+
else
|
|
18
|
+
raise "No converter found for type #{type}"
|
|
19
|
+
end
|
|
16
20
|
end
|
|
17
21
|
end
|
|
18
22
|
|
|
19
|
-
Dir[
|
|
23
|
+
Dir[ DocuBot::DIR/'docubot/converters/*.rb' ].each do |converter|
|
|
20
24
|
require converter
|
|
21
25
|
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
require 'rubygems'
|
|
3
|
+
require 'haml'
|
|
4
|
+
options = { :format=>:html4, :ugly=>true }
|
|
5
|
+
options.merge!( :encoding=>'utf-8' ) if Object.const_defined? "Encoding"
|
|
6
|
+
|
|
7
|
+
DocuBot::Converter.to_convert :haml do |page, source|
|
|
8
|
+
Haml::Engine.new( source, options ).render( page, :page=>page, :global=>page.bundle.toc, :root=>page.root )
|
|
9
|
+
end
|
|
@@ -1,6 +1,14 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
1
2
|
require 'rubygems'
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
begin
|
|
4
|
+
require 'bluecloth'
|
|
5
|
+
DocuBot::Converter.to_convert :md, :markdown do |page, source|
|
|
6
|
+
# BlueCloth 2.0.5 takes UTF-8 source and returns ASCII-8BIT
|
|
7
|
+
result = BlueCloth.new(source).to_html
|
|
8
|
+
result.encode!( 'UTF-8', :undef=>:replace ) if Object.const_defined? "Encoding"
|
|
9
|
+
result
|
|
10
|
+
end
|
|
11
|
+
rescue LoadError
|
|
12
|
+
warn "Unable to load bluecloth gem; *.markdown/*.md markup will not be recognized as a page."
|
|
6
13
|
end
|
|
14
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
require 'rubygems'
|
|
3
|
+
begin
|
|
4
|
+
require 'redcloth'
|
|
5
|
+
DocuBot::Converter.to_convert :textile, :rc do |page, source|
|
|
6
|
+
RedCloth.new(source,[:no_span_caps]).to_html
|
|
7
|
+
end
|
|
8
|
+
rescue LoadError
|
|
9
|
+
warn "Unable to load RedCloth gem; *.textile/*.rc markup will not be recognized as a page."
|
|
10
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
class DocuBot::Glossary
|
|
3
|
+
attr_accessor :bundle
|
|
4
|
+
def initialize( bundle, dir )
|
|
5
|
+
@entries = {}
|
|
6
|
+
@downcased = {}
|
|
7
|
+
@bundle = bundle
|
|
8
|
+
@missing = []
|
|
9
|
+
# .directory? also ensures that the path exists
|
|
10
|
+
if File.directory?( dir )
|
|
11
|
+
@directory = File.expand_path( dir )
|
|
12
|
+
# Dir[ dir/'*' ].each do |item|
|
|
13
|
+
# page = DocuBot::Page.new( item )
|
|
14
|
+
# page.bundle = @bundle
|
|
15
|
+
# self << page
|
|
16
|
+
# end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
def []=( term, definition )
|
|
20
|
+
@entries[ term ] = definition
|
|
21
|
+
@downcased[ term.downcase ] = term
|
|
22
|
+
end
|
|
23
|
+
def []( term )
|
|
24
|
+
@entries[ @downcased[ term.downcase ] ]
|
|
25
|
+
end
|
|
26
|
+
def each
|
|
27
|
+
@entries.each{ |term,defn| yield term, defn }
|
|
28
|
+
end
|
|
29
|
+
def add_missing_term( term )
|
|
30
|
+
@missing << term.downcase
|
|
31
|
+
# File.open( @directory/"#{term}.md", "w" ){ |f| f << "<span class='todo'>TODO: define #{term}</span>" } if @directory
|
|
32
|
+
end
|
|
33
|
+
def missing_terms
|
|
34
|
+
# Terms may have been defined after being first seen
|
|
35
|
+
@missing.reject{ |term| self[term] }.uniq
|
|
36
|
+
end
|
|
37
|
+
def <<( page )
|
|
38
|
+
#TODO: perhaps don't serialize the page here, but wait until some #write call gives us a template so we can use that?
|
|
39
|
+
self[ page.title ] = page.to_html
|
|
40
|
+
end
|
|
41
|
+
def to_js
|
|
42
|
+
"$glossaryTerms = {#{@entries.map{ |term,defn| "'#{term.downcase.gsub("'","\\\\'")}':'#{defn.gsub("'","\\\\'").gsub(/[\r\n]/,'\\n')}'" }.join(",\n")}};"
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
# The index links keywords to a particular page.
|
|
4
|
+
#
|
|
5
|
+
# Keywords are added to the index by:
|
|
6
|
+
# * Having a "keywords" entry in the metasection, e.g.
|
|
7
|
+
# keywords: Rigid Bodies, Dynamic, Physical Mesh
|
|
8
|
+
# * Surrounding a word or phrase on the page with two @ characters, e.g.
|
|
9
|
+
# The purpose of a @@physical mesh@@ is to...
|
|
10
|
+
# * Having an "index: headings" entry in the metasection, causing each
|
|
11
|
+
# heading on the page to be added to the index.
|
|
12
|
+
# * Having an "index: definitions" entry in the metasection, causing each
|
|
13
|
+
# <dt>...</dt> on the page to be added to the index.
|
|
14
|
+
# (May be combined with the above as "index: headings definitions".)
|
|
15
|
+
#
|
|
16
|
+
# As shown above, terms may be referenced in title or lowercase.
|
|
17
|
+
# Names with capital letters will be favored over lowercase in the index.
|
|
18
|
+
class DocuBot::Index
|
|
19
|
+
attr_reader :entries
|
|
20
|
+
def initialize( bundle )
|
|
21
|
+
@bundle = bundle
|
|
22
|
+
@entries = Hash.new{|h,k|h[k]=[]} # key points to array of DocuBot::Pages
|
|
23
|
+
@downcased = {}
|
|
24
|
+
#TODO: support links to sub-sections instead of just pages
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Run through the 'keywords' and 'index' meta attribute for a page and add entries
|
|
28
|
+
# Note: in-content @@keyword@@ marks are processed by snippets/index_entries.rb
|
|
29
|
+
def process_page( page )
|
|
30
|
+
page.keywords.split(/,\s*/).each{ |key| add( key, page ) } if page.keywords?
|
|
31
|
+
|
|
32
|
+
html = page.to_html
|
|
33
|
+
unless page['no-index'] && page['no-index'].include?( 'headings' )
|
|
34
|
+
#TODO: Fix the regex to use a backreference to ensure the correct closing tag, once 1.8x support is not necessary
|
|
35
|
+
html.scan( %r{<h[1-6][^>]*>(.+?)</h[1-6]>}im ){ |captures| add( captures.first, page ) }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
unless page['no-index'] && page['no-index'].include?( 'definitions' )
|
|
39
|
+
html.scan( %r{<dt[^>]*>(.+?)</dt>}im ){ |captures| add captures.first, page }
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def add( term, page )
|
|
44
|
+
term.strip!
|
|
45
|
+
term.gsub!(/<[^>]+>/,'')
|
|
46
|
+
down = term.downcase
|
|
47
|
+
if existing = @downcased[ down ]
|
|
48
|
+
# The existing entry might be early-arriving all-lowercase.
|
|
49
|
+
# If the new term has more capital letters, it wins.
|
|
50
|
+
if term.scan(/[A-Z]/).length > existing.scan(/[A-Z]/).length
|
|
51
|
+
@downcased[ down ] = term
|
|
52
|
+
@entries[ term ] = @entries[ existing ]
|
|
53
|
+
@entries.delete( existing )
|
|
54
|
+
else
|
|
55
|
+
term = existing
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
@entries[ term ] << page
|
|
59
|
+
@entries[ term ].uniq!
|
|
60
|
+
@downcased[ down ] = term
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def each
|
|
64
|
+
@entries.each{ |term, pages| yield term, pages }
|
|
65
|
+
end
|
|
66
|
+
end
|
data/lib/docubot/page.rb
CHANGED
|
@@ -1,33 +1,157 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
1
2
|
require 'yaml'
|
|
2
3
|
class DocuBot::Page
|
|
3
|
-
META_SEPARATOR =
|
|
4
|
+
META_SEPARATOR = /^\+\+\+\s*$/ # Sort of like +++ATH0
|
|
4
5
|
|
|
5
|
-
attr_reader :
|
|
6
|
+
attr_reader :pages, :type, :folder, :file, :meta
|
|
7
|
+
attr_accessor :parent, :bundle
|
|
6
8
|
|
|
7
|
-
def
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
def initialize( source_path, title=nil, type=nil )
|
|
10
|
+
puts "#{self.class}.new( #{source_path.inspect}, #{title.inspect}, #{type.inspect} )" if $DEBUG
|
|
11
|
+
title ||= File.basename( source_path ).sub( /\.[^.]+$/, '' ).gsub( '_', ' ' ).sub( /^\d+\s/, '' )
|
|
12
|
+
@meta = { 'title'=>title }
|
|
13
|
+
@pages = []
|
|
14
|
+
@file = source_path
|
|
15
|
+
if File.directory?( @file )
|
|
16
|
+
@folder = @file
|
|
17
|
+
# WILL SET @file TO NIL FOR DIRECTORIES WITHOUT AN INDEX.* FILE
|
|
18
|
+
@file = Dir[ source_path/'index.*' ][0]
|
|
19
|
+
else
|
|
20
|
+
@folder = File.dirname( @file )
|
|
21
|
+
end
|
|
12
22
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
23
|
+
# Directories without an index file have no @file
|
|
24
|
+
if @file
|
|
25
|
+
@type = type || File.extname( @file )[ 1..-1 ]
|
|
26
|
+
parts = IO.read( @file ).split( META_SEPARATOR, 2 )
|
|
27
|
+
|
|
28
|
+
if parts.length > 1
|
|
29
|
+
# Make YAML friendler to n00bs
|
|
30
|
+
yaml = YAML.load( parts.first.gsub( /^\t/, ' ' ) )
|
|
31
|
+
@meta.merge!( yaml )
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Raw markup, untransformed
|
|
35
|
+
@raw = parts.last
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
def []( key )
|
|
39
|
+
@meta[key]
|
|
19
40
|
end
|
|
20
41
|
|
|
21
42
|
def method_missing( method, *args )
|
|
22
43
|
key=method.to_s
|
|
23
|
-
case key[-1..-1]
|
|
44
|
+
case key[-1..-1] # the last character of the method name
|
|
24
45
|
when '?' then @meta.has_key?( key[0..-2] )
|
|
25
|
-
when '
|
|
46
|
+
#when '=' then @meta[ key[0..-2] ] = args[0]
|
|
47
|
+
when '!','=' then super
|
|
26
48
|
else @meta[ key ]
|
|
27
49
|
end
|
|
28
50
|
end
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
51
|
+
def ancestors
|
|
52
|
+
page = self
|
|
53
|
+
anc = []
|
|
54
|
+
anc.unshift( page ) while page = page.parent
|
|
55
|
+
anc
|
|
56
|
+
end
|
|
57
|
+
def sections
|
|
58
|
+
@pages.reject{ |e| e.pages.empty? }
|
|
59
|
+
end
|
|
60
|
+
def leafs
|
|
61
|
+
@pages.select{ |e| e.pages.empty? }
|
|
62
|
+
end
|
|
63
|
+
def every_leaf
|
|
64
|
+
(leafs + sub_sections.map{ |sub| sub.every_leaf }).flatten
|
|
65
|
+
end
|
|
66
|
+
def every_section
|
|
67
|
+
(sub_sections + sub_sections.map{ |sub| sub.every_section }).flatten
|
|
68
|
+
end
|
|
69
|
+
def descendants
|
|
70
|
+
(@pages + @pages.map{ |page| page.descendants }).flatten
|
|
71
|
+
end
|
|
72
|
+
alias_method :every_page, :descendants
|
|
73
|
+
def <<( entry )
|
|
74
|
+
@pages << entry
|
|
75
|
+
entry.parent = self
|
|
76
|
+
end
|
|
77
|
+
def leaf?
|
|
78
|
+
@pages.empty? || @pages.all?{ |x| x.is_a?(DocuBot::SubLink) }
|
|
79
|
+
end
|
|
80
|
+
def depth
|
|
81
|
+
@_depth ||= @file ? @file.count('/') : @folder.count('/') + 1
|
|
82
|
+
end
|
|
83
|
+
def root
|
|
84
|
+
@_root ||= "../" * depth
|
|
85
|
+
end
|
|
86
|
+
def html_path
|
|
87
|
+
@file ? @file.sub( /[^.]+$/, 'html' ) : ( @folder / 'index.html' )
|
|
88
|
+
end
|
|
89
|
+
def to_html
|
|
90
|
+
return @cached_html if @cached_html
|
|
91
|
+
|
|
92
|
+
contents = if @raw
|
|
93
|
+
# Directories with no index.* file will not have any @raw
|
|
94
|
+
html = DocuBot::convert_to_html( self, @raw, @type )
|
|
95
|
+
DocuBot::process_snippets( self, html )
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
@meta['template'] ||= leaf? ? 'page' : 'section'
|
|
99
|
+
|
|
100
|
+
master_templates = DocuBot::TEMPLATE_DIR
|
|
101
|
+
source_templates = @bundle.source / '_templates'
|
|
102
|
+
|
|
103
|
+
haml = source_templates / "#{template}.haml"
|
|
104
|
+
haml = master_templates / "#{template}.haml" unless File.exists?( haml )
|
|
105
|
+
haml = master_templates / "page.haml" unless File.exists?( haml )
|
|
106
|
+
haml = Haml::Engine.new( IO.read( haml ), DocuBot::Writer::HAML_OPTIONS )
|
|
107
|
+
contents = haml.render( Object.new, :contents=>contents, :page=>self, :global=>@bundle.toc, :root=>root )
|
|
108
|
+
|
|
109
|
+
@cached_html = contents
|
|
110
|
+
end
|
|
111
|
+
def to_html!
|
|
112
|
+
@cached_html=nil
|
|
113
|
+
to_html
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
class DocuBot::SubLink
|
|
118
|
+
attr_reader :page, :title, :id
|
|
119
|
+
def initialize( page, title, id )
|
|
120
|
+
@page, @title, @id = page, title, id
|
|
121
|
+
end
|
|
122
|
+
def html_path
|
|
123
|
+
"#{@page.html_path}##{@id}"
|
|
124
|
+
end
|
|
125
|
+
def leaf?
|
|
126
|
+
true
|
|
127
|
+
end
|
|
128
|
+
def pages
|
|
129
|
+
[]
|
|
130
|
+
end
|
|
131
|
+
alias_method :descendants, :pages
|
|
132
|
+
def depth
|
|
133
|
+
@page.depth
|
|
134
|
+
end
|
|
135
|
+
def parent
|
|
136
|
+
@page
|
|
137
|
+
end
|
|
138
|
+
def parent=( page )
|
|
139
|
+
@page = page
|
|
140
|
+
end
|
|
141
|
+
def to_html
|
|
142
|
+
""
|
|
143
|
+
end
|
|
144
|
+
def ancestors
|
|
145
|
+
@page.ancestors
|
|
146
|
+
end
|
|
147
|
+
alias_method :to_html!, :to_html
|
|
148
|
+
def method_missing(*args)
|
|
149
|
+
nil
|
|
150
|
+
end
|
|
151
|
+
def hide
|
|
152
|
+
false
|
|
153
|
+
end
|
|
154
|
+
def sublink?
|
|
155
|
+
true
|
|
32
156
|
end
|
|
33
157
|
end
|