docubot 0.6.1 → 0.6.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 +125 -125
- data/lib/docubot.rb +43 -43
- data/lib/docubot/bundle.rb +182 -182
- data/lib/docubot/converter.rb +28 -28
- data/lib/docubot/converters/haml.rb +9 -9
- data/lib/docubot/converters/markdown.rb +14 -14
- data/lib/docubot/index.rb +67 -67
- data/lib/docubot/link_tree.rb +111 -111
- data/lib/docubot/metasection.rb +61 -61
- data/lib/docubot/page.rb +167 -167
- data/lib/docubot/shells/default/0_License.md +59 -59
- data/lib/docubot/shells/default/Appendix/Glossary.md +4 -4
- data/lib/docubot/shells/default/Appendix/Index_Page.md +8 -8
- data/lib/docubot/shells/default/Appendix/Table of Contents.md +4 -4
- data/lib/docubot/shells/docubot-help/0_License.md +20 -20
- data/lib/docubot/shells/docubot-help/1_Getting_Started.md +47 -47
- data/lib/docubot/shells/docubot-help/2_Basic_Concepts/4 Adding Images.md +1 -1
- data/lib/docubot/shells/docubot-help/2_Basic_Concepts/index.md +5 -5
- data/lib/docubot/shells/docubot-help/3_Advanced_Topics/Controlling Glossary.md +2 -2
- data/lib/docubot/shells/docubot-help/3_Advanced_Topics/Controlling Indexing.md +9 -9
- data/lib/docubot/shells/docubot-help/3_Advanced_Topics/Controlling the Table of Contents.md +5 -5
- data/lib/docubot/shells/docubot-help/4_Appendix/Glossary.md +4 -4
- data/lib/docubot/shells/docubot-help/4_Appendix/Index_Page.md +5 -5
- data/lib/docubot/shells/docubot-help/4_Appendix/Table of Contents.md +7 -7
- data/lib/docubot/shells/docubot-help/index.txt +7 -7
- data/lib/docubot/snippet.rb +19 -19
- data/lib/docubot/snippets/glossary.rb +7 -7
- data/lib/docubot/snippets/index_entries.rb +6 -6
- data/lib/docubot/templates/_root/glossary.css +3 -3
- data/lib/docubot/templates/_root/glossary.js +57 -57
- data/lib/docubot/templates/index.haml +14 -14
- data/lib/docubot/templates/toc.haml +2 -2
- data/lib/docubot/templates/top.haml +32 -32
- data/lib/docubot/writers/chm/hhc.erb +1 -1
- data/lib/docubot/writers/chm/hhk.erb +2 -2
- data/lib/docubot/writers/html.rb +87 -87
- data/spec/_all.rb +12 -12
- data/spec/_helper.rb +16 -16
- data/spec/bundle.rb +339 -339
- data/spec/command.rb +3 -3
- data/spec/converters.rb +2 -2
- data/spec/global.rb +27 -27
- data/spec/glossary.rb +94 -94
- data/spec/index.rb +2 -2
- data/spec/page.rb +80 -80
- data/spec/samples/attributes/defaults.haml +34 -34
- data/spec/samples/attributes/explicit1.haml +42 -42
- data/spec/samples/attributes/explicit2.haml +41 -41
- data/spec/samples/attributes/hidden.haml +39 -39
- data/spec/samples/attributes/index.md +8 -8
- data/spec/samples/collisions/page1.md +2 -2
- data/spec/samples/collisions/page1.textile +2 -2
- data/spec/samples/collisions/page2.haml +3 -3
- data/spec/samples/collisions/page2.html +2 -2
- data/spec/samples/collisions/page2.txt +2 -2
- data/spec/samples/collisions/page3.md +2 -2
- data/spec/samples/files/index.md +1 -1
- data/spec/samples/glossary/Glossary.txt +4 -4
- data/spec/samples/glossary/Some Page.md +2 -2
- data/spec/samples/glossary/_glossary/Simple Term.md +2 -2
- data/spec/samples/glossary/_glossary/complex.haml +8 -8
- data/spec/samples/glossary/_glossary/project_x.md +3 -3
- data/spec/samples/hierarchy/1/1.1/1.1.1/index.haml +1 -1
- data/spec/samples/hierarchy/1/1.1/1.1.1/page.haml +3 -3
- data/spec/samples/hierarchy/1/1.1/page.haml +3 -3
- data/spec/samples/hierarchy/1/page.haml +3 -3
- data/spec/samples/hierarchy/2/2.1/2.1.1/page.haml +3 -3
- data/spec/samples/hierarchy/2/2.1/index.haml +1 -1
- data/spec/samples/hierarchy/2/2.1/page.haml +3 -3
- data/spec/samples/hierarchy/2/page.haml +3 -3
- data/spec/samples/hierarchy/main.haml +1 -1
- data/spec/samples/hierarchy/toc.md +1 -1
- data/spec/samples/links/index.txt +11 -11
- data/spec/samples/links/root.md +13 -13
- data/spec/samples/links/sub1/inner1.md +11 -11
- data/spec/samples/links/sub2.md +4 -4
- data/spec/samples/links/sub2/inner2.md +7 -7
- data/spec/samples/simplest/HTML.html +9 -9
- data/spec/samples/simplest/Haml.haml +12 -12
- data/spec/samples/simplest/Markdown.md +9 -9
- data/spec/samples/simplest/Text.txt +9 -9
- data/spec/samples/simplest/Textile.textile +9 -9
- data/spec/samples/templates/_templates/doubler.haml +6 -6
- data/spec/samples/templates/_templates/page.haml +1 -1
- data/spec/samples/templates/goaway.txt +2 -2
- data/spec/samples/templates/onepara_html.html +2 -2
- data/spec/samples/templates/onepara_md.md +4 -4
- data/spec/samples/templates/twopara_haml.haml +6 -6
- data/spec/samples/templates/twopara_textile.textile +5 -5
- data/spec/samples/titles/3_renamed.txt +1 -1
- data/spec/samples/titles/index.txt +1 -1
- data/spec/templates.rb +43 -43
- data/spec/toc.rb +128 -128
- data/spec/writer/chm.rb +2 -2
- data/spec/writer/html.rb +2 -2
- metadata +56 -25
- data/bin/docubot.bat +0 -1
data/lib/docubot/converter.rb
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
# encoding: UTF-8
|
|
2
|
-
module DocuBot
|
|
3
|
-
module Converter
|
|
4
|
-
@by_type = {}
|
|
5
|
-
def self.to_convert( *types, &block )
|
|
6
|
-
types.each{ |type| @by_type[type.to_s] = block }
|
|
7
|
-
end
|
|
8
|
-
def self.by_type
|
|
9
|
-
@by_type
|
|
10
|
-
end
|
|
11
|
-
def self.types
|
|
12
|
-
@by_type.keys
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def self.convert_to_html( page, source, type )
|
|
17
|
-
if converter = DocuBot::Converter.by_type[ type.to_s ]
|
|
18
|
-
puts "Converting #{type}: #{source.inspect[0..60]}" if $DEBUG
|
|
19
|
-
converter[ page, source ]
|
|
20
|
-
else
|
|
21
|
-
raise "No converter found for type #{type}"
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
Dir[ DocuBot::DIR/'docubot/converters/*.rb' ].each do |converter|
|
|
27
|
-
require converter
|
|
28
|
-
end
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
module DocuBot
|
|
3
|
+
module Converter
|
|
4
|
+
@by_type = {}
|
|
5
|
+
def self.to_convert( *types, &block )
|
|
6
|
+
types.each{ |type| @by_type[type.to_s] = block }
|
|
7
|
+
end
|
|
8
|
+
def self.by_type
|
|
9
|
+
@by_type
|
|
10
|
+
end
|
|
11
|
+
def self.types
|
|
12
|
+
@by_type.keys
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.convert_to_html( page, source, type )
|
|
17
|
+
if converter = DocuBot::Converter.by_type[ type.to_s ]
|
|
18
|
+
puts "Converting #{type}: #{source.inspect[0..60]}" if $DEBUG
|
|
19
|
+
converter[ page, source ]
|
|
20
|
+
else
|
|
21
|
+
raise "No converter found for type #{type}"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
Dir[ DocuBot::DIR/'docubot/converters/*.rb' ].each do |converter|
|
|
27
|
+
require converter
|
|
28
|
+
end
|
|
@@ -1,9 +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.global, :root=>page.root )
|
|
9
|
-
end
|
|
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.global, :root=>page.root )
|
|
9
|
+
end
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
# encoding: UTF-8
|
|
2
|
-
require 'rubygems'
|
|
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."
|
|
13
|
-
end
|
|
14
|
-
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
require 'rubygems'
|
|
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."
|
|
13
|
+
end
|
|
14
|
+
|
data/lib/docubot/index.rb
CHANGED
|
@@ -1,67 +1,67 @@
|
|
|
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
|
-
# FIXME: This is substantially slower (but way more correct) than regexp only.
|
|
33
|
-
unless page['no-index'] && page['no-index'].include?( 'headings' )
|
|
34
|
-
%w[h1 h2 h3 h4 h5 h6].each do |hn|
|
|
35
|
-
page.nokodoc.css(hn).each{ |head| add( head.inner_text, page ) }
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
unless page['no-index'] && page['no-index'].include?( 'definitions' )
|
|
40
|
-
page.nokodoc.css("dt").each{ |defn| add( defn.inner_text, page ) }
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def add( term, page )
|
|
45
|
-
term.strip!
|
|
46
|
-
term.gsub!(/<[^>]+>/,'')
|
|
47
|
-
down = term.downcase
|
|
48
|
-
if existing = @downcased[ down ]
|
|
49
|
-
# The existing entry might be early-arriving all-lowercase.
|
|
50
|
-
# If the new term has more capital letters, it wins.
|
|
51
|
-
if term.scan(/[A-Z]/).length > existing.scan(/[A-Z]/).length
|
|
52
|
-
@downcased[ down ] = term
|
|
53
|
-
@entries[ term ] = @entries[ existing ]
|
|
54
|
-
@entries.delete( existing )
|
|
55
|
-
else
|
|
56
|
-
term = existing
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
@entries[ term ] << page
|
|
60
|
-
@entries[ term ].uniq!
|
|
61
|
-
@downcased[ down ] = term
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def each
|
|
65
|
-
@entries.each{ |term, pages| yield term, pages }
|
|
66
|
-
end
|
|
67
|
-
end
|
|
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
|
+
# FIXME: This is substantially slower (but way more correct) than regexp only.
|
|
33
|
+
unless page['no-index'] && page['no-index'].include?( 'headings' )
|
|
34
|
+
%w[h1 h2 h3 h4 h5 h6].each do |hn|
|
|
35
|
+
page.nokodoc.css(hn).each{ |head| add( head.inner_text, page ) }
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
unless page['no-index'] && page['no-index'].include?( 'definitions' )
|
|
40
|
+
page.nokodoc.css("dt").each{ |defn| add( defn.inner_text, page ) }
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def add( term, page )
|
|
45
|
+
term.strip!
|
|
46
|
+
term.gsub!(/<[^>]+>/,'')
|
|
47
|
+
down = term.downcase
|
|
48
|
+
if existing = @downcased[ down ]
|
|
49
|
+
# The existing entry might be early-arriving all-lowercase.
|
|
50
|
+
# If the new term has more capital letters, it wins.
|
|
51
|
+
if term.scan(/[A-Z]/).length > existing.scan(/[A-Z]/).length
|
|
52
|
+
@downcased[ down ] = term
|
|
53
|
+
@entries[ term ] = @entries[ existing ]
|
|
54
|
+
@entries.delete( existing )
|
|
55
|
+
else
|
|
56
|
+
term = existing
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
@entries[ term ] << page
|
|
60
|
+
@entries[ term ].uniq!
|
|
61
|
+
@downcased[ down ] = term
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def each
|
|
65
|
+
@entries.each{ |term, pages| yield term, pages }
|
|
66
|
+
end
|
|
67
|
+
end
|
data/lib/docubot/link_tree.rb
CHANGED
|
@@ -1,111 +1,111 @@
|
|
|
1
|
-
# encoding: UTF-8
|
|
2
|
-
module DocuBot::LinkTree; end
|
|
3
|
-
|
|
4
|
-
class DocuBot::LinkTree::Node
|
|
5
|
-
attr_accessor :title, :link, :page, :parent
|
|
6
|
-
|
|
7
|
-
def initialize( title=nil, link=nil, page=nil )
|
|
8
|
-
@title,@link,@page = title,link,page
|
|
9
|
-
@children = []
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def anchor
|
|
13
|
-
@link[/#(.+)/,1]
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def file
|
|
17
|
-
@link.sub(/#.+/,'')
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def leaf?
|
|
21
|
-
!@children.any?{ |node| node.page != @page }
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
# Add a new link underneath a link to its logical parent
|
|
25
|
-
def add_to_link_hierarchy( title, link, page=nil )
|
|
26
|
-
node = DocuBot::LinkTree::Node.new( title, link, page )
|
|
27
|
-
parent_link = if node.anchor
|
|
28
|
-
node.file
|
|
29
|
-
elsif File.basename(link)=='index.html'
|
|
30
|
-
File.dirname(File.dirname(link))/'index.html'
|
|
31
|
-
else
|
|
32
|
-
(File.dirname(link) / 'index.html')
|
|
33
|
-
end
|
|
34
|
-
#puts "Adding #{title.inspect} (#{link}) to hierarchy under #{parent_link}"
|
|
35
|
-
parent = descendants.find{ |n| n.link==parent_link } || self
|
|
36
|
-
parent << node
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def <<( node )
|
|
40
|
-
node.parent = self
|
|
41
|
-
@children << node
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def []( child_index )
|
|
45
|
-
@children[child_index]
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def children( parent_link=nil, &block )
|
|
49
|
-
if parent_link
|
|
50
|
-
root = find( parent_link )
|
|
51
|
-
root ? root.children( &block ) : []
|
|
52
|
-
else
|
|
53
|
-
@children
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def descendants
|
|
58
|
-
( @children + @children.map{ |child| child.descendants } ).flatten
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def find( link )
|
|
62
|
-
# TODO: this is eminently cachable
|
|
63
|
-
descendants.find{ |node| node.link==link }
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def depth
|
|
67
|
-
# Cached assuming no one is going to shuffle the nodes after placement
|
|
68
|
-
@depth ||= ancestors.length
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def ancestors
|
|
72
|
-
# Cached assuming no one is going to shuffle the nodes after placement
|
|
73
|
-
return @ancestors if @ancestors
|
|
74
|
-
@ancestors = []
|
|
75
|
-
node = self
|
|
76
|
-
@ancestors << node while node = node.parent
|
|
77
|
-
@ancestors.reverse!
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def to_s
|
|
81
|
-
"#{@title} (#{@link}) - #{@page && @page.title}"
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def to_txt( depth=0 )
|
|
85
|
-
indent = " "*depth
|
|
86
|
-
[
|
|
87
|
-
indent+to_s,
|
|
88
|
-
children.map{|kid|kid.to_txt(depth+1)}
|
|
89
|
-
].flatten.join("\n")
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
class DocuBot::LinkTree::Root < DocuBot::LinkTree::Node
|
|
94
|
-
undef_method :title
|
|
95
|
-
undef_method :link
|
|
96
|
-
undef_method :page
|
|
97
|
-
attr_reader :bundle
|
|
98
|
-
def initialize( bundle )
|
|
99
|
-
@bundle = bundle
|
|
100
|
-
@children = []
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
def <<( node )
|
|
104
|
-
node.parent = nil
|
|
105
|
-
@children << node
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
def to_s
|
|
109
|
-
"(Table of Contents)"
|
|
110
|
-
end
|
|
111
|
-
end
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
module DocuBot::LinkTree; end
|
|
3
|
+
|
|
4
|
+
class DocuBot::LinkTree::Node
|
|
5
|
+
attr_accessor :title, :link, :page, :parent
|
|
6
|
+
|
|
7
|
+
def initialize( title=nil, link=nil, page=nil )
|
|
8
|
+
@title,@link,@page = title,link,page
|
|
9
|
+
@children = []
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def anchor
|
|
13
|
+
@link[/#(.+)/,1]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def file
|
|
17
|
+
@link.sub(/#.+/,'')
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def leaf?
|
|
21
|
+
!@children.any?{ |node| node.page != @page }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Add a new link underneath a link to its logical parent
|
|
25
|
+
def add_to_link_hierarchy( title, link, page=nil )
|
|
26
|
+
node = DocuBot::LinkTree::Node.new( title, link, page )
|
|
27
|
+
parent_link = if node.anchor
|
|
28
|
+
node.file
|
|
29
|
+
elsif File.basename(link)=='index.html'
|
|
30
|
+
File.dirname(File.dirname(link))/'index.html'
|
|
31
|
+
else
|
|
32
|
+
(File.dirname(link) / 'index.html')
|
|
33
|
+
end
|
|
34
|
+
#puts "Adding #{title.inspect} (#{link}) to hierarchy under #{parent_link}"
|
|
35
|
+
parent = descendants.find{ |n| n.link==parent_link } || self
|
|
36
|
+
parent << node
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def <<( node )
|
|
40
|
+
node.parent = self
|
|
41
|
+
@children << node
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def []( child_index )
|
|
45
|
+
@children[child_index]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def children( parent_link=nil, &block )
|
|
49
|
+
if parent_link
|
|
50
|
+
root = find( parent_link )
|
|
51
|
+
root ? root.children( &block ) : []
|
|
52
|
+
else
|
|
53
|
+
@children
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def descendants
|
|
58
|
+
( @children + @children.map{ |child| child.descendants } ).flatten
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def find( link )
|
|
62
|
+
# TODO: this is eminently cachable
|
|
63
|
+
descendants.find{ |node| node.link==link }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def depth
|
|
67
|
+
# Cached assuming no one is going to shuffle the nodes after placement
|
|
68
|
+
@depth ||= ancestors.length
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def ancestors
|
|
72
|
+
# Cached assuming no one is going to shuffle the nodes after placement
|
|
73
|
+
return @ancestors if @ancestors
|
|
74
|
+
@ancestors = []
|
|
75
|
+
node = self
|
|
76
|
+
@ancestors << node while node = node.parent
|
|
77
|
+
@ancestors.reverse!
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def to_s
|
|
81
|
+
"#{@title} (#{@link}) - #{@page && @page.title}"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def to_txt( depth=0 )
|
|
85
|
+
indent = " "*depth
|
|
86
|
+
[
|
|
87
|
+
indent+to_s,
|
|
88
|
+
children.map{|kid|kid.to_txt(depth+1)}
|
|
89
|
+
].flatten.join("\n")
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
class DocuBot::LinkTree::Root < DocuBot::LinkTree::Node
|
|
94
|
+
undef_method :title
|
|
95
|
+
undef_method :link
|
|
96
|
+
undef_method :page
|
|
97
|
+
attr_reader :bundle
|
|
98
|
+
def initialize( bundle )
|
|
99
|
+
@bundle = bundle
|
|
100
|
+
@children = []
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def <<( node )
|
|
104
|
+
node.parent = nil
|
|
105
|
+
@children << node
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def to_s
|
|
109
|
+
"(Table of Contents)"
|
|
110
|
+
end
|
|
111
|
+
end
|