docubot 0.6.1 → 0.6.2
Sign up to get free protection for your applications and to get access to all the features.
- 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/metasection.rb
CHANGED
@@ -1,61 +1,61 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
class DocuBot::MetaSection; end
|
3
|
-
module DocuBot::MetaSection::Castable
|
4
|
-
def as_boolean
|
5
|
-
self == 'true'
|
6
|
-
end
|
7
|
-
def as_list
|
8
|
-
return [] if self.nil?
|
9
|
-
# Replace commas inside quoted strings with unlikely-to-be-used Unicode
|
10
|
-
# FIXME: This doesnt work for the sadistic case of "hello """, "world"
|
11
|
-
csv = self.gsub( /("(?:[^",]|"")+),/, '\\1⥸' )
|
12
|
-
csv.split(/\s*,\s*/).map do |str|
|
13
|
-
# Put real commas back, unquote, undouble internal quotes
|
14
|
-
str[/^".*"$/] ? str[1..-2].gsub('⥸',',').gsub('""','"') : str
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
class DocuBot::MetaSection
|
20
|
-
META_SEPARATOR = /^\+\+\+\s*$/ # Sort of like +++ATH0
|
21
|
-
NIL_CASTABLE = nil.extend( Castable )
|
22
|
-
attr_reader :__contents__
|
23
|
-
|
24
|
-
def initialize( attrs={}, file_path=nil )
|
25
|
-
@attrs = {}
|
26
|
-
attrs.each{ |key,value| self[key]=value }
|
27
|
-
if file_path && File.exists?( file_path )
|
28
|
-
parts = IO.read( file_path ).split( META_SEPARATOR, 2 )
|
29
|
-
if parts.length > 1
|
30
|
-
parts.first.scan(/.+/) do |line|
|
31
|
-
next if line =~ /^\s*#/
|
32
|
-
next unless line.include?(':')
|
33
|
-
attribute, value = line.split(':',2).map{ |str| str.strip }
|
34
|
-
self[attribute] = value
|
35
|
-
end
|
36
|
-
end
|
37
|
-
@__contents__ = parts.last && parts.last.strip
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def has_key?( key )
|
42
|
-
@attrs.has_key?( key )
|
43
|
-
end
|
44
|
-
|
45
|
-
def []( attribute )
|
46
|
-
@attrs.has_key?( attribute ) ? @attrs[attribute] : NIL_CASTABLE
|
47
|
-
end
|
48
|
-
|
49
|
-
def []=( attribute, value )
|
50
|
-
@attrs[attribute.to_s] = value.extend(Castable)
|
51
|
-
end
|
52
|
-
|
53
|
-
def method_missing( method, *args )
|
54
|
-
key=method.to_s
|
55
|
-
case key[-1..-1] # the last character of the method name
|
56
|
-
when '=' then self[key[0..-2]] = args.first
|
57
|
-
else self[key]
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
end
|
1
|
+
# encoding: UTF-8
|
2
|
+
class DocuBot::MetaSection; end
|
3
|
+
module DocuBot::MetaSection::Castable
|
4
|
+
def as_boolean
|
5
|
+
self == 'true'
|
6
|
+
end
|
7
|
+
def as_list
|
8
|
+
return [] if self.nil?
|
9
|
+
# Replace commas inside quoted strings with unlikely-to-be-used Unicode
|
10
|
+
# FIXME: This doesnt work for the sadistic case of "hello """, "world"
|
11
|
+
csv = self.gsub( /("(?:[^",]|"")+),/, '\\1⥸' )
|
12
|
+
csv.split(/\s*,\s*/).map do |str|
|
13
|
+
# Put real commas back, unquote, undouble internal quotes
|
14
|
+
str[/^".*"$/] ? str[1..-2].gsub('⥸',',').gsub('""','"') : str
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class DocuBot::MetaSection
|
20
|
+
META_SEPARATOR = /^\+\+\+\s*$/ # Sort of like +++ATH0
|
21
|
+
NIL_CASTABLE = nil.extend( Castable )
|
22
|
+
attr_reader :__contents__
|
23
|
+
|
24
|
+
def initialize( attrs={}, file_path=nil )
|
25
|
+
@attrs = {}
|
26
|
+
attrs.each{ |key,value| self[key]=value }
|
27
|
+
if file_path && File.exists?( file_path )
|
28
|
+
parts = IO.read( file_path ).split( META_SEPARATOR, 2 )
|
29
|
+
if parts.length > 1
|
30
|
+
parts.first.scan(/.+/) do |line|
|
31
|
+
next if line =~ /^\s*#/
|
32
|
+
next unless line.include?(':')
|
33
|
+
attribute, value = line.split(':',2).map{ |str| str.strip }
|
34
|
+
self[attribute] = value
|
35
|
+
end
|
36
|
+
end
|
37
|
+
@__contents__ = parts.last && parts.last.strip
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def has_key?( key )
|
42
|
+
@attrs.has_key?( key )
|
43
|
+
end
|
44
|
+
|
45
|
+
def []( attribute )
|
46
|
+
@attrs.has_key?( attribute ) ? @attrs[attribute] : NIL_CASTABLE
|
47
|
+
end
|
48
|
+
|
49
|
+
def []=( attribute, value )
|
50
|
+
@attrs[attribute.to_s] = value.extend(Castable)
|
51
|
+
end
|
52
|
+
|
53
|
+
def method_missing( method, *args )
|
54
|
+
key=method.to_s
|
55
|
+
case key[-1..-1] # the last character of the method name
|
56
|
+
when '=' then self[key[0..-2]] = args.first
|
57
|
+
else self[key]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
data/lib/docubot/page.rb
CHANGED
@@ -1,167 +1,167 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
require 'yaml'
|
3
|
-
require 'nokogiri'
|
4
|
-
|
5
|
-
class DocuBot::Page
|
6
|
-
AUTO_ID_ELEMENTS = %w[ h1 h2 h3 h4 h5 h6 legend caption dt ].join(',')
|
7
|
-
|
8
|
-
attr_reader :type, :folder, :file, :meta, :nokodoc, :bundle
|
9
|
-
|
10
|
-
def self.title( source_path )
|
11
|
-
# File#basename might return the same string
|
12
|
-
title = File.basename( source_path ).dup
|
13
|
-
title.sub!(/\.[^.\s]+$/,'') unless File.directory?( source_path )
|
14
|
-
title.gsub!( '_', ' ' )
|
15
|
-
title.sub!( /^\d+\s/, '' )
|
16
|
-
title
|
17
|
-
end
|
18
|
-
|
19
|
-
def initialize( bundle, source_path, title=nil )
|
20
|
-
puts "#{self.class}.new( #{source_path.inspect}, #{title.inspect}, #{type.inspect} )" if $DEBUG
|
21
|
-
title ||= self.class.title( source_path )
|
22
|
-
@bundle = bundle
|
23
|
-
@file = source_path
|
24
|
-
if File.directory?( @file )
|
25
|
-
@folder = @file
|
26
|
-
@file = Dir[ source_path/'index.*' ][0]
|
27
|
-
# Directories without an index.* file now have nil @file
|
28
|
-
else
|
29
|
-
@folder = File.dirname( @file )
|
30
|
-
end
|
31
|
-
@type = File.extname( @file )[ 1..-1 ] if @file
|
32
|
-
@meta = DocuBot::MetaSection.new( {'title'=>title}, @file )
|
33
|
-
@raw = @meta.__contents__
|
34
|
-
@raw = nil if @raw && @raw.empty?
|
35
|
-
|
36
|
-
create_nokodoc
|
37
|
-
end
|
38
|
-
|
39
|
-
def create_nokodoc
|
40
|
-
# Directories with no index.* file will not have any @raw
|
41
|
-
# Pages with metasection only will also not have any @raw
|
42
|
-
html = if @raw && !@raw.empty?
|
43
|
-
html = DocuBot::process_snippets( self, @raw )
|
44
|
-
html = DocuBot::convert_to_html( self, html, @type )
|
45
|
-
end
|
46
|
-
@nokodoc = Nokogiri::HTML::DocumentFragment.parse(html || "")
|
47
|
-
auto_id
|
48
|
-
auto_section
|
49
|
-
@nokodoc
|
50
|
-
end
|
51
|
-
|
52
|
-
def nokodoc
|
53
|
-
@nokodoc ||= create_nokodoc
|
54
|
-
end
|
55
|
-
|
56
|
-
# Add IDs to elements that don't have them
|
57
|
-
def auto_id
|
58
|
-
# ...but only if a toc entry might reference one, or requested.
|
59
|
-
if @meta['auto-id'].as_boolean || @meta.toc.as_list.any?{ |toc| !toc['#'] }
|
60
|
-
@nokodoc.css( AUTO_ID_ELEMENTS ).each do |node|
|
61
|
-
next if node.has_attribute?('id')
|
62
|
-
# Strip off the unwanted leading '#'
|
63
|
-
node['id'] = DocuBot.id_from_text(node.inner_text)[1..-1]
|
64
|
-
end
|
65
|
-
dirty_doc
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
# Wrap siblings of headers in <div class='section'>
|
70
|
-
def auto_section
|
71
|
-
return if @meta['auto-section']==false
|
72
|
-
|
73
|
-
#TODO: Make this a generic nokogiri call on any node (like body) where you can pass in a hierarchy of elements and a wrapper
|
74
|
-
stack = []
|
75
|
-
@nokodoc.children.each do |node|
|
76
|
-
# non-matching nodes will get level of 0
|
77
|
-
level = node.name[ /h([1-6])/i, 1 ].to_i
|
78
|
-
level = 99 if level == 0
|
79
|
-
|
80
|
-
stack.pop while (top=stack.last) && top[:level]>=level
|
81
|
-
stack.last[:div].add_child( node ) if stack.last
|
82
|
-
if level<99
|
83
|
-
div = Nokogiri::XML::Node.new('div',@nokodoc)
|
84
|
-
div.set_attribute( 'class', 'section' )
|
85
|
-
node.add_next_sibling(div)
|
86
|
-
stack << { :div=>div, :level=>level }
|
87
|
-
end
|
88
|
-
end
|
89
|
-
dirty_doc
|
90
|
-
end
|
91
|
-
|
92
|
-
def []( key )
|
93
|
-
@meta[key]
|
94
|
-
end
|
95
|
-
|
96
|
-
def method_missing( method, *args )
|
97
|
-
key=method.to_s
|
98
|
-
case key[-1..-1] # the last character of the method name
|
99
|
-
when '?' then @meta.has_key?( key[0..-2] )
|
100
|
-
when '!','=' then super
|
101
|
-
else
|
102
|
-
# warn "Unknown attribute #{key.inspect} asked for on #{@file || @folder}" unless @meta.has_key?( key )
|
103
|
-
@meta[ key ]
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
def html_path
|
108
|
-
@html_path ||= @file ? @file.sub( /[^.]+$/, 'html' ) : ( @folder / 'index.html' )
|
109
|
-
end
|
110
|
-
|
111
|
-
# Call this if the source generated by the converter would change
|
112
|
-
# THIS DESTROYS ANY CUSTOM CHANGES YOU HAVE MADE TO THE NOKODOC
|
113
|
-
def dirty_source
|
114
|
-
@nokodoc = nil
|
115
|
-
end
|
116
|
-
|
117
|
-
# Call this after modifying the structure of the nokodoc for the page
|
118
|
-
def dirty_doc
|
119
|
-
@content_html = nil
|
120
|
-
end
|
121
|
-
|
122
|
-
# Call this if the HTML generated by the page template needs to change
|
123
|
-
# e.g. for a glossary page.
|
124
|
-
def dirty_template
|
125
|
-
# to_html doesn't cache anything at this point
|
126
|
-
# so nothing needs to be done here
|
127
|
-
end
|
128
|
-
|
129
|
-
def content_html
|
130
|
-
@content_html ||= nokodoc.to_html
|
131
|
-
end
|
132
|
-
|
133
|
-
def children
|
134
|
-
@bundle.toc.children(html_path).map{ |node| node.page }.uniq.compact
|
135
|
-
end
|
136
|
-
|
137
|
-
def leaf?
|
138
|
-
@leaf ||= !children.any?{ |page| page != self }
|
139
|
-
end
|
140
|
-
|
141
|
-
def depth
|
142
|
-
@depth ||= html_path.scan('/').length
|
143
|
-
end
|
144
|
-
|
145
|
-
def root
|
146
|
-
@root ||= "../" * depth
|
147
|
-
end
|
148
|
-
|
149
|
-
#TODO: cache this is people keep calling to_html and it's a problem
|
150
|
-
def to_html
|
151
|
-
@meta.template ||= leaf? ? 'page' : 'section'
|
152
|
-
|
153
|
-
master_templates = DocuBot::TEMPLATE_DIR
|
154
|
-
source_templates = @bundle.source / '_templates'
|
155
|
-
tmpl = source_templates / "#{template}.haml"
|
156
|
-
tmpl = master_templates / "#{template}.haml" unless File.exists?( tmpl )
|
157
|
-
tmpl = master_templates / "page.haml" unless File.exists?( tmpl )
|
158
|
-
tmpl = IO.read( tmpl )
|
159
|
-
haml = Haml::Engine.new( tmpl, DocuBot::Writer::HAML_OPTIONS )
|
160
|
-
haml.render( Object.new, :contents=>content_html, :page=>self, :global=>@bundle.global, :root=>root )
|
161
|
-
end
|
162
|
-
|
163
|
-
def inspect
|
164
|
-
"<#{self.class} '#{self.title}' #{@file ? "@file=#{@file.inspect}" : "@folder=#{@folder.inspect}"}>"
|
165
|
-
end
|
166
|
-
|
167
|
-
end
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'yaml'
|
3
|
+
require 'nokogiri'
|
4
|
+
|
5
|
+
class DocuBot::Page
|
6
|
+
AUTO_ID_ELEMENTS = %w[ h1 h2 h3 h4 h5 h6 legend caption dt ].join(',')
|
7
|
+
|
8
|
+
attr_reader :type, :folder, :file, :meta, :nokodoc, :bundle
|
9
|
+
|
10
|
+
def self.title( source_path )
|
11
|
+
# File#basename might return the same string
|
12
|
+
title = File.basename( source_path ).dup
|
13
|
+
title.sub!(/\.[^.\s]+$/,'') unless File.directory?( source_path )
|
14
|
+
title.gsub!( '_', ' ' )
|
15
|
+
title.sub!( /^\d+\s/, '' )
|
16
|
+
title
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize( bundle, source_path, title=nil )
|
20
|
+
puts "#{self.class}.new( #{source_path.inspect}, #{title.inspect}, #{type.inspect} )" if $DEBUG
|
21
|
+
title ||= self.class.title( source_path )
|
22
|
+
@bundle = bundle
|
23
|
+
@file = source_path
|
24
|
+
if File.directory?( @file )
|
25
|
+
@folder = @file
|
26
|
+
@file = Dir[ source_path/'index.*' ][0]
|
27
|
+
# Directories without an index.* file now have nil @file
|
28
|
+
else
|
29
|
+
@folder = File.dirname( @file )
|
30
|
+
end
|
31
|
+
@type = File.extname( @file )[ 1..-1 ] if @file
|
32
|
+
@meta = DocuBot::MetaSection.new( {'title'=>title}, @file )
|
33
|
+
@raw = @meta.__contents__
|
34
|
+
@raw = nil if @raw && @raw.empty?
|
35
|
+
|
36
|
+
create_nokodoc
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_nokodoc
|
40
|
+
# Directories with no index.* file will not have any @raw
|
41
|
+
# Pages with metasection only will also not have any @raw
|
42
|
+
html = if @raw && !@raw.empty?
|
43
|
+
html = DocuBot::process_snippets( self, @raw )
|
44
|
+
html = DocuBot::convert_to_html( self, html, @type )
|
45
|
+
end
|
46
|
+
@nokodoc = Nokogiri::HTML::DocumentFragment.parse(html || "")
|
47
|
+
auto_id
|
48
|
+
auto_section
|
49
|
+
@nokodoc
|
50
|
+
end
|
51
|
+
|
52
|
+
def nokodoc
|
53
|
+
@nokodoc ||= create_nokodoc
|
54
|
+
end
|
55
|
+
|
56
|
+
# Add IDs to elements that don't have them
|
57
|
+
def auto_id
|
58
|
+
# ...but only if a toc entry might reference one, or requested.
|
59
|
+
if @meta['auto-id'].as_boolean || @meta.toc.as_list.any?{ |toc| !toc['#'] }
|
60
|
+
@nokodoc.css( AUTO_ID_ELEMENTS ).each do |node|
|
61
|
+
next if node.has_attribute?('id')
|
62
|
+
# Strip off the unwanted leading '#'
|
63
|
+
node['id'] = DocuBot.id_from_text(node.inner_text)[1..-1]
|
64
|
+
end
|
65
|
+
dirty_doc
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Wrap siblings of headers in <div class='section'>
|
70
|
+
def auto_section
|
71
|
+
return if @meta['auto-section']==false
|
72
|
+
|
73
|
+
#TODO: Make this a generic nokogiri call on any node (like body) where you can pass in a hierarchy of elements and a wrapper
|
74
|
+
stack = []
|
75
|
+
@nokodoc.children.each do |node|
|
76
|
+
# non-matching nodes will get level of 0
|
77
|
+
level = node.name[ /h([1-6])/i, 1 ].to_i
|
78
|
+
level = 99 if level == 0
|
79
|
+
|
80
|
+
stack.pop while (top=stack.last) && top[:level]>=level
|
81
|
+
stack.last[:div].add_child( node ) if stack.last
|
82
|
+
if level<99
|
83
|
+
div = Nokogiri::XML::Node.new('div',@nokodoc)
|
84
|
+
div.set_attribute( 'class', 'section' )
|
85
|
+
node.add_next_sibling(div)
|
86
|
+
stack << { :div=>div, :level=>level }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
dirty_doc
|
90
|
+
end
|
91
|
+
|
92
|
+
def []( key )
|
93
|
+
@meta[key]
|
94
|
+
end
|
95
|
+
|
96
|
+
def method_missing( method, *args )
|
97
|
+
key=method.to_s
|
98
|
+
case key[-1..-1] # the last character of the method name
|
99
|
+
when '?' then @meta.has_key?( key[0..-2] )
|
100
|
+
when '!','=' then super
|
101
|
+
else
|
102
|
+
# warn "Unknown attribute #{key.inspect} asked for on #{@file || @folder}" unless @meta.has_key?( key )
|
103
|
+
@meta[ key ]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def html_path
|
108
|
+
@html_path ||= @file ? @file.sub( /[^.]+$/, 'html' ) : ( @folder / 'index.html' )
|
109
|
+
end
|
110
|
+
|
111
|
+
# Call this if the source generated by the converter would change
|
112
|
+
# THIS DESTROYS ANY CUSTOM CHANGES YOU HAVE MADE TO THE NOKODOC
|
113
|
+
def dirty_source
|
114
|
+
@nokodoc = nil
|
115
|
+
end
|
116
|
+
|
117
|
+
# Call this after modifying the structure of the nokodoc for the page
|
118
|
+
def dirty_doc
|
119
|
+
@content_html = nil
|
120
|
+
end
|
121
|
+
|
122
|
+
# Call this if the HTML generated by the page template needs to change
|
123
|
+
# e.g. for a glossary page.
|
124
|
+
def dirty_template
|
125
|
+
# to_html doesn't cache anything at this point
|
126
|
+
# so nothing needs to be done here
|
127
|
+
end
|
128
|
+
|
129
|
+
def content_html
|
130
|
+
@content_html ||= nokodoc.to_html
|
131
|
+
end
|
132
|
+
|
133
|
+
def children
|
134
|
+
@bundle.toc.children(html_path).map{ |node| node.page }.uniq.compact
|
135
|
+
end
|
136
|
+
|
137
|
+
def leaf?
|
138
|
+
@leaf ||= !children.any?{ |page| page != self }
|
139
|
+
end
|
140
|
+
|
141
|
+
def depth
|
142
|
+
@depth ||= html_path.scan('/').length
|
143
|
+
end
|
144
|
+
|
145
|
+
def root
|
146
|
+
@root ||= "../" * depth
|
147
|
+
end
|
148
|
+
|
149
|
+
#TODO: cache this is people keep calling to_html and it's a problem
|
150
|
+
def to_html
|
151
|
+
@meta.template ||= leaf? ? 'page' : 'section'
|
152
|
+
|
153
|
+
master_templates = DocuBot::TEMPLATE_DIR
|
154
|
+
source_templates = @bundle.source / '_templates'
|
155
|
+
tmpl = source_templates / "#{template}.haml"
|
156
|
+
tmpl = master_templates / "#{template}.haml" unless File.exists?( tmpl )
|
157
|
+
tmpl = master_templates / "page.haml" unless File.exists?( tmpl )
|
158
|
+
tmpl = IO.read( tmpl )
|
159
|
+
haml = Haml::Engine.new( tmpl, DocuBot::Writer::HAML_OPTIONS )
|
160
|
+
haml.render( Object.new, :contents=>content_html, :page=>self, :global=>@bundle.global, :root=>root )
|
161
|
+
end
|
162
|
+
|
163
|
+
def inspect
|
164
|
+
"<#{self.class} '#{self.title}' #{@file ? "@file=#{@file.inspect}" : "@folder=#{@folder.inspect}"}>"
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|