docubot 0.6.1 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
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