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.
Files changed (97) hide show
  1. data/bin/docubot +125 -125
  2. data/lib/docubot.rb +43 -43
  3. data/lib/docubot/bundle.rb +182 -182
  4. data/lib/docubot/converter.rb +28 -28
  5. data/lib/docubot/converters/haml.rb +9 -9
  6. data/lib/docubot/converters/markdown.rb +14 -14
  7. data/lib/docubot/index.rb +67 -67
  8. data/lib/docubot/link_tree.rb +111 -111
  9. data/lib/docubot/metasection.rb +61 -61
  10. data/lib/docubot/page.rb +167 -167
  11. data/lib/docubot/shells/default/0_License.md +59 -59
  12. data/lib/docubot/shells/default/Appendix/Glossary.md +4 -4
  13. data/lib/docubot/shells/default/Appendix/Index_Page.md +8 -8
  14. data/lib/docubot/shells/default/Appendix/Table of Contents.md +4 -4
  15. data/lib/docubot/shells/docubot-help/0_License.md +20 -20
  16. data/lib/docubot/shells/docubot-help/1_Getting_Started.md +47 -47
  17. data/lib/docubot/shells/docubot-help/2_Basic_Concepts/4 Adding Images.md +1 -1
  18. data/lib/docubot/shells/docubot-help/2_Basic_Concepts/index.md +5 -5
  19. data/lib/docubot/shells/docubot-help/3_Advanced_Topics/Controlling Glossary.md +2 -2
  20. data/lib/docubot/shells/docubot-help/3_Advanced_Topics/Controlling Indexing.md +9 -9
  21. data/lib/docubot/shells/docubot-help/3_Advanced_Topics/Controlling the Table of Contents.md +5 -5
  22. data/lib/docubot/shells/docubot-help/4_Appendix/Glossary.md +4 -4
  23. data/lib/docubot/shells/docubot-help/4_Appendix/Index_Page.md +5 -5
  24. data/lib/docubot/shells/docubot-help/4_Appendix/Table of Contents.md +7 -7
  25. data/lib/docubot/shells/docubot-help/index.txt +7 -7
  26. data/lib/docubot/snippet.rb +19 -19
  27. data/lib/docubot/snippets/glossary.rb +7 -7
  28. data/lib/docubot/snippets/index_entries.rb +6 -6
  29. data/lib/docubot/templates/_root/glossary.css +3 -3
  30. data/lib/docubot/templates/_root/glossary.js +57 -57
  31. data/lib/docubot/templates/index.haml +14 -14
  32. data/lib/docubot/templates/toc.haml +2 -2
  33. data/lib/docubot/templates/top.haml +32 -32
  34. data/lib/docubot/writers/chm/hhc.erb +1 -1
  35. data/lib/docubot/writers/chm/hhk.erb +2 -2
  36. data/lib/docubot/writers/html.rb +87 -87
  37. data/spec/_all.rb +12 -12
  38. data/spec/_helper.rb +16 -16
  39. data/spec/bundle.rb +339 -339
  40. data/spec/command.rb +3 -3
  41. data/spec/converters.rb +2 -2
  42. data/spec/global.rb +27 -27
  43. data/spec/glossary.rb +94 -94
  44. data/spec/index.rb +2 -2
  45. data/spec/page.rb +80 -80
  46. data/spec/samples/attributes/defaults.haml +34 -34
  47. data/spec/samples/attributes/explicit1.haml +42 -42
  48. data/spec/samples/attributes/explicit2.haml +41 -41
  49. data/spec/samples/attributes/hidden.haml +39 -39
  50. data/spec/samples/attributes/index.md +8 -8
  51. data/spec/samples/collisions/page1.md +2 -2
  52. data/spec/samples/collisions/page1.textile +2 -2
  53. data/spec/samples/collisions/page2.haml +3 -3
  54. data/spec/samples/collisions/page2.html +2 -2
  55. data/spec/samples/collisions/page2.txt +2 -2
  56. data/spec/samples/collisions/page3.md +2 -2
  57. data/spec/samples/files/index.md +1 -1
  58. data/spec/samples/glossary/Glossary.txt +4 -4
  59. data/spec/samples/glossary/Some Page.md +2 -2
  60. data/spec/samples/glossary/_glossary/Simple Term.md +2 -2
  61. data/spec/samples/glossary/_glossary/complex.haml +8 -8
  62. data/spec/samples/glossary/_glossary/project_x.md +3 -3
  63. data/spec/samples/hierarchy/1/1.1/1.1.1/index.haml +1 -1
  64. data/spec/samples/hierarchy/1/1.1/1.1.1/page.haml +3 -3
  65. data/spec/samples/hierarchy/1/1.1/page.haml +3 -3
  66. data/spec/samples/hierarchy/1/page.haml +3 -3
  67. data/spec/samples/hierarchy/2/2.1/2.1.1/page.haml +3 -3
  68. data/spec/samples/hierarchy/2/2.1/index.haml +1 -1
  69. data/spec/samples/hierarchy/2/2.1/page.haml +3 -3
  70. data/spec/samples/hierarchy/2/page.haml +3 -3
  71. data/spec/samples/hierarchy/main.haml +1 -1
  72. data/spec/samples/hierarchy/toc.md +1 -1
  73. data/spec/samples/links/index.txt +11 -11
  74. data/spec/samples/links/root.md +13 -13
  75. data/spec/samples/links/sub1/inner1.md +11 -11
  76. data/spec/samples/links/sub2.md +4 -4
  77. data/spec/samples/links/sub2/inner2.md +7 -7
  78. data/spec/samples/simplest/HTML.html +9 -9
  79. data/spec/samples/simplest/Haml.haml +12 -12
  80. data/spec/samples/simplest/Markdown.md +9 -9
  81. data/spec/samples/simplest/Text.txt +9 -9
  82. data/spec/samples/simplest/Textile.textile +9 -9
  83. data/spec/samples/templates/_templates/doubler.haml +6 -6
  84. data/spec/samples/templates/_templates/page.haml +1 -1
  85. data/spec/samples/templates/goaway.txt +2 -2
  86. data/spec/samples/templates/onepara_html.html +2 -2
  87. data/spec/samples/templates/onepara_md.md +4 -4
  88. data/spec/samples/templates/twopara_haml.haml +6 -6
  89. data/spec/samples/templates/twopara_textile.textile +5 -5
  90. data/spec/samples/titles/3_renamed.txt +1 -1
  91. data/spec/samples/titles/index.txt +1 -1
  92. data/spec/templates.rb +43 -43
  93. data/spec/toc.rb +128 -128
  94. data/spec/writer/chm.rb +2 -2
  95. data/spec/writer/html.rb +2 -2
  96. metadata +56 -25
  97. data/bin/docubot.bat +0 -1
@@ -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
@@ -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