webgen 0.5.8 → 0.5.9

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 (142) hide show
  1. data/COPYING +4 -0
  2. data/ChangeLog +1037 -0
  3. data/Rakefile +5 -6
  4. data/THANKS +1 -0
  5. data/VERSION +1 -1
  6. data/bin/webgen +1 -1
  7. data/data/webgen/passive_sources/images/generated_by_webgen.png +0 -0
  8. data/data/webgen/passive_sources/images/webgen_logo.png +0 -0
  9. data/data/webgen/passive_sources/stylesheets/coderay-default.css +129 -0
  10. data/data/webgen/passive_sources/templates/atom_feed.template +38 -0
  11. data/data/webgen/passive_sources/templates/rss_feed.template +28 -0
  12. data/data/webgen/passive_sources/templates/sitemap.template +21 -0
  13. data/data/webgen/resources.yaml +2 -1
  14. data/data/webgen/website_skeleton/Rakefile +5 -1
  15. data/doc/contentprocessor/builder.page +2 -2
  16. data/doc/contentprocessor/erb.page +5 -2
  17. data/doc/contentprocessor/erubis.page +2 -2
  18. data/doc/contentprocessor/head.page +21 -0
  19. data/doc/contentprocessor/tidy.page +14 -0
  20. data/doc/extensions.page +1 -1
  21. data/doc/faq.page +2 -2
  22. data/doc/manual.page +108 -43
  23. data/doc/reference_configuration.page +83 -5
  24. data/doc/reference_metainfo.page +24 -4
  25. data/doc/reference_website_styles.page +2 -2
  26. data/doc/sourcehandler/feed.page +11 -13
  27. data/doc/sourcehandler/metainfo.page +10 -3
  28. data/doc/sourcehandler/page.page +4 -4
  29. data/doc/sourcehandler/sitemap.page +8 -7
  30. data/doc/tag/coderay.page +6 -2
  31. data/doc/tag/includefile.page +1 -1
  32. data/doc/tag/menu.page +3 -0
  33. data/lib/webgen/cli/apply_command.rb +1 -1
  34. data/lib/webgen/cli/utils.rb +2 -2
  35. data/lib/webgen/common.rb +0 -9
  36. data/lib/webgen/contentprocessor.rb +18 -3
  37. data/lib/webgen/contentprocessor/blocks.rb +67 -36
  38. data/lib/webgen/contentprocessor/builder.rb +5 -2
  39. data/lib/webgen/contentprocessor/erb.rb +4 -2
  40. data/lib/webgen/contentprocessor/erubis.rb +5 -2
  41. data/lib/webgen/contentprocessor/haml.rb +6 -2
  42. data/lib/webgen/contentprocessor/head.rb +64 -0
  43. data/lib/webgen/contentprocessor/maruku.rb +3 -1
  44. data/lib/webgen/contentprocessor/rdiscount.rb +2 -0
  45. data/lib/webgen/contentprocessor/rdoc.rb +2 -0
  46. data/lib/webgen/contentprocessor/redcloth.rb +2 -0
  47. data/lib/webgen/contentprocessor/sass.rb +5 -3
  48. data/lib/webgen/contentprocessor/tags.rb +40 -24
  49. data/lib/webgen/contentprocessor/tidy.rb +38 -0
  50. data/lib/webgen/context.rb +13 -4
  51. data/lib/webgen/context/render.rb +32 -0
  52. data/lib/webgen/context/tags.rb +20 -0
  53. data/lib/webgen/default_config.rb +15 -4
  54. data/lib/webgen/deprecated.rb +38 -4
  55. data/lib/webgen/error.rb +135 -0
  56. data/lib/webgen/node.rb +48 -40
  57. data/lib/webgen/output.rb +5 -3
  58. data/lib/webgen/output/filesystem.rb +4 -4
  59. data/lib/webgen/page.rb +4 -4
  60. data/lib/webgen/path.rb +161 -58
  61. data/lib/webgen/source.rb +9 -6
  62. data/lib/webgen/source/filesystem.rb +1 -1
  63. data/lib/webgen/source/stacked.rb +13 -5
  64. data/lib/webgen/source/tararchive.rb +6 -2
  65. data/lib/webgen/sourcehandler.rb +100 -54
  66. data/lib/webgen/sourcehandler/base.rb +58 -24
  67. data/lib/webgen/sourcehandler/copy.rb +6 -5
  68. data/lib/webgen/sourcehandler/directory.rb +3 -9
  69. data/lib/webgen/sourcehandler/feed.rb +25 -50
  70. data/lib/webgen/sourcehandler/fragment.rb +10 -8
  71. data/lib/webgen/sourcehandler/memory.rb +9 -10
  72. data/lib/webgen/sourcehandler/metainfo.rb +9 -9
  73. data/lib/webgen/sourcehandler/page.rb +6 -5
  74. data/lib/webgen/sourcehandler/sitemap.rb +22 -22
  75. data/lib/webgen/sourcehandler/template.rb +6 -6
  76. data/lib/webgen/sourcehandler/virtual.rb +19 -17
  77. data/lib/webgen/tag/base.rb +27 -27
  78. data/lib/webgen/tag/breadcrumbtrail.rb +3 -3
  79. data/lib/webgen/tag/coderay.rb +19 -8
  80. data/lib/webgen/tag/executecommand.rb +4 -3
  81. data/lib/webgen/tag/langbar.rb +2 -2
  82. data/lib/webgen/tag/link.rb +8 -7
  83. data/lib/webgen/tag/menu.rb +2 -2
  84. data/lib/webgen/tag/metainfo.rb +1 -1
  85. data/lib/webgen/tag/relocatable.rb +17 -21
  86. data/lib/webgen/tag/tikz.rb +7 -10
  87. data/lib/webgen/tree.rb +7 -7
  88. data/lib/webgen/version.rb +1 -1
  89. data/lib/webgen/website.rb +32 -2
  90. data/misc/default.css +8 -2
  91. data/misc/default.template +2 -2
  92. data/misc/logo.svg +313 -0
  93. data/misc/style.page +1 -1
  94. data/test/helper.rb +18 -2
  95. data/test/test_cli.rb +104 -0
  96. data/test/test_common_sitemap.rb +1 -1
  97. data/test/test_contentprocessor.rb +8 -2
  98. data/test/test_contentprocessor_blocks.rb +17 -8
  99. data/test/test_contentprocessor_builder.rb +13 -2
  100. data/test/test_contentprocessor_erb.rb +9 -3
  101. data/test/test_contentprocessor_erubis.rb +9 -3
  102. data/test/test_contentprocessor_fragments.rb +12 -11
  103. data/test/test_contentprocessor_haml.rb +11 -2
  104. data/test/test_contentprocessor_head.rb +44 -0
  105. data/test/test_contentprocessor_maruku.rb +5 -1
  106. data/test/test_contentprocessor_rdiscount.rb +4 -0
  107. data/test/test_contentprocessor_rdoc.rb +4 -0
  108. data/test/test_contentprocessor_redcloth.rb +5 -1
  109. data/test/test_contentprocessor_sass.rb +8 -2
  110. data/test/test_contentprocessor_tags.rb +22 -7
  111. data/test/test_contentprocessor_tidy.rb +34 -0
  112. data/test/test_context.rb +39 -0
  113. data/test/test_error.rb +85 -0
  114. data/test/test_node.rb +57 -21
  115. data/test/test_page.rb +23 -5
  116. data/test/test_path.rb +120 -64
  117. data/test/test_source_filesystem.rb +1 -1
  118. data/test/test_source_stacked.rb +19 -6
  119. data/test/test_sourcehandler_base.rb +63 -50
  120. data/test/test_sourcehandler_copy.rb +6 -6
  121. data/test/test_sourcehandler_directory.rb +8 -12
  122. data/test/test_sourcehandler_feed.rb +15 -7
  123. data/test/test_sourcehandler_fragment.rb +6 -5
  124. data/test/test_sourcehandler_main.rb +39 -0
  125. data/test/test_sourcehandler_memory.rb +4 -4
  126. data/test/test_sourcehandler_metainfo.rb +20 -11
  127. data/test/test_sourcehandler_page.rb +10 -10
  128. data/test/test_sourcehandler_sitemap.rb +24 -5
  129. data/test/test_sourcehandler_template.rb +18 -15
  130. data/test/test_sourcehandler_virtual.rb +9 -5
  131. data/test/test_tag_base.rb +6 -29
  132. data/test/test_tag_coderay.rb +16 -3
  133. data/test/test_tag_executecommand.rb +2 -2
  134. data/test/test_tag_link.rb +5 -4
  135. data/test/test_tag_menu.rb +15 -15
  136. data/test/test_tag_metainfo.rb +1 -0
  137. data/test/test_tag_relocatable.rb +3 -2
  138. data/test/test_tag_tikz.rb +5 -5
  139. data/test/test_tree.rb +8 -8
  140. data/test/test_website.rb +15 -0
  141. metadata +21 -14
  142. data/test/test_common.rb +0 -18
@@ -17,20 +17,27 @@ module Webgen::Source
17
17
  # source, it is discarded and not used.
18
18
  class Stacked
19
19
 
20
- # Return the stack of Webgen::Source objects.
20
+ # Return the stack of mount point to Webgen::Source object maps.
21
21
  attr_reader :stack
22
22
 
23
+ # Specifies whether the result of #paths calls should be cached (default: +false+). If caching
24
+ # is activated, new maps cannot be added to the stacked source anymore!
25
+ attr_accessor :cache_paths
26
+
23
27
  # Create a new stack. The optional +map+ parameter can be used to provide initial mappings of
24
- # mount points to source objects (see #add for details).
25
- def initialize(map = {})
28
+ # mount points to source objects (see #add for details). You cannot add other maps after a call
29
+ # to #paths if +cache_paths+ is +true+
30
+ def initialize(map = {}, cache_paths = false)
26
31
  @stack = []
32
+ @cache_paths = cache_paths
27
33
  add(map)
28
34
  end
29
35
 
30
36
  # Add all mappings found in +maps+ to the stack. The parameter +maps+ should be an array of
31
- # two-element arrays which contain an absolute directoriy (ie. starting with a slash) and a
32
- # source object.
37
+ # two-element arrays which contain an absolute directory (ie. starting and ending with a slash)
38
+ # and a source object.
33
39
  def add(maps)
40
+ raise "Cannot add new maps since caching is activated for this source" if defined?(@paths) && @cache_paths
34
41
  maps.each do |mp, source|
35
42
  raise "Invalid mount point specified: #{mp}" unless mp =~ /^\//
36
43
  @stack << [mp, source]
@@ -41,6 +48,7 @@ module Webgen::Source
41
48
  # returned by later source objects are not used if a prior source object has returned the same
42
49
  # path.
43
50
  def paths
51
+ return @paths if defined?(@paths) && @cache_paths
44
52
  @paths = Set.new
45
53
  @stack.each do |mp, source|
46
54
  source.paths.each do |path|
@@ -5,7 +5,11 @@ require 'webgen/websiteaccess'
5
5
  require 'webgen/path'
6
6
  require 'open-uri'
7
7
  require 'zlib'
8
- require 'archive/tar/minitar'
8
+ begin
9
+ require 'archive/tar/minitar'
10
+ rescue LoadError
11
+ raise Webgen::LoadError.new('archive/tar/minitar', self.class.name, context.dest_node.alcn, 'archive-tar-minitar')
12
+ end
9
13
 
10
14
  module Webgen
11
15
 
@@ -24,7 +28,7 @@ module Webgen
24
28
 
25
29
  # Create a new tar archive path object for the entry +entry+.
26
30
  def initialize(path, data, mtime, uri)
27
- super(path) { StringIO.new(data.to_s) }
31
+ super(path) {|mode| StringIO.new(data.to_s, mode) }
28
32
  @uri = uri
29
33
  @mtime = mtime
30
34
  WebsiteAccess.website.cache[[:tararchive_path, @uri, path]] = @mtime if WebsiteAccess.website
@@ -28,6 +28,7 @@ module Webgen
28
28
  # * collects all source paths using the source classes
29
29
  # * creates nodes using the source handler classes
30
30
  # * writes changed nodes out using an output class
31
+ # * deletes old nodes
31
32
  class Main
32
33
 
33
34
  include WebsiteAccess
@@ -35,88 +36,121 @@ module Webgen
35
36
 
36
37
  def initialize #:nodoc:
37
38
  website.blackboard.add_service(:create_nodes, method(:create_nodes))
39
+ website.blackboard.add_service(:create_nodes_from_paths, method(:create_nodes_from_paths))
38
40
  website.blackboard.add_service(:source_paths, method(:find_all_source_paths))
39
41
  website.blackboard.add_listener(:node_meta_info_changed?, method(:meta_info_changed?))
42
+
43
+ website.blackboard.add_listener(:before_node_deleted) do |node|
44
+ website.blackboard.invoke(:output_instance).delete(node.path)
45
+ end if website.config['output.do_deletion']
40
46
  end
41
47
 
42
- # Render the nodes provided in the +tree+. Before the actual rendering is done, the sources
43
- # are checked (nodes for deleted sources are deleted, nodes for new and changed sources).
44
- def render(tree)
48
+ # Render the current website. Before the actual rendering is done, the sources are checked for
49
+ # changes, i.e. nodes for deleted sources are deleted, nodes for new and changed sources are
50
+ # updated.
51
+ def render
45
52
  begin
46
53
  website.logger.mark_new_cycle if website.logger
47
54
 
48
55
  puts "Updating tree..."
49
56
  time = Benchmark.measure do
50
57
  website.cache.reset_volatile_cache
51
- update_tree(tree)
58
+ update_tree
52
59
  end
53
60
  puts "...done in " + ('%2.4f' % time.real) + ' seconds'
54
61
 
55
- if !tree.root
62
+ if !website.tree.root
56
63
  puts 'No source files found - maybe not a webgen website?'
57
64
  return nil
58
65
  end
59
66
 
60
67
  puts "Writing changed nodes..."
61
68
  time = Benchmark.measure do
62
- write_tree(tree)
69
+ write_tree
63
70
  end
64
71
  puts "...done in " + ('%2.4f' % time.real) + ' seconds'
65
- end while tree.node_access[:alcn].any? {|name,node| node.flagged?(:created) || node.flagged?(:reinit)}
72
+ end while website.tree.node_access[:alcn].any? {|name,node| node.flagged?(:created) || node.flagged?(:reinit)}
66
73
  :success
74
+ rescue Webgen::Error
75
+ raise
76
+ rescue Exception => e
77
+ raise Webgen::Error.new(e)
67
78
  end
68
79
 
69
80
  #######
70
81
  private
71
82
  #######
72
83
 
73
- # Update the +tree+ by creating/reinitializing all needed nodes.
74
- def update_tree(tree)
84
+ # Update the <tt>website.tree</tt> by creating/reinitializing all needed nodes.
85
+ def update_tree
75
86
  unused_paths = Set.new
87
+ referenced_nodes = Set.new
88
+ all_but_passive_paths = Set.new(find_all_source_paths.select {|name, path| !path.passive?}.collect {|name, path| name})
76
89
  begin
77
- used_paths = Set.new(find_all_source_paths.keys) - unused_paths
90
+ used_paths = all_but_passive_paths - unused_paths
78
91
  paths_to_use = Set.new
79
92
  nodes_to_delete = Set.new
93
+ passive_nodes = Set.new
94
+
95
+ website.tree.node_access[:alcn].each do |alcn, node|
96
+ next if node == website.tree.dummy_root
97
+
98
+ begin
99
+ used_paths.delete(node.node_info[:src])
80
100
 
81
- tree.node_access[:alcn].each do |alcn, node|
82
- next if node == tree.dummy_root
83
- used_paths.delete(node.node_info[:src])
84
-
85
- deleted = !find_all_source_paths.include?(node.node_info[:src])
86
- if deleted
87
- nodes_to_delete << node
88
- #TODO: delete output path
89
- elsif (!node.flagged?(:created) && find_all_source_paths[node.node_info[:src]].changed?) || node.meta_info_changed?
90
- node.flag(:reinit)
91
- paths_to_use << node.node_info[:src]
92
- elsif node.changed?
93
- # nothing to be done here
101
+ src_path = find_all_source_paths[node.node_info[:src]]
102
+ if !src_path
103
+ nodes_to_delete << node
104
+ elsif (!node.flagged?(:created) && src_path.changed?) || node.meta_info_changed?
105
+ node.flag(:reinit)
106
+ paths_to_use << node.node_info[:src]
107
+ elsif node.changed?
108
+ # nothing to be done here but method node.changed? has to be called
109
+ end
110
+
111
+ if src_path && src_path.passive?
112
+ passive_nodes << node
113
+ elsif src_path
114
+ referenced_nodes += node.node_info[:used_meta_info_nodes] + node.node_info[:used_nodes]
115
+ end
116
+ rescue Webgen::Error => e
117
+ e.alcn = node.alcn unless e.alcn
118
+ raise
119
+ rescue Exception => e
120
+ raise Webgen::Error.new(e, nil, node.alcn)
94
121
  end
95
122
  end
96
123
 
97
- nodes_to_delete.each {|node| tree.delete_node(node)}
124
+ # add unused passive nodes to node_to_delete set
125
+ unreferenced_passive_nodes, other_passive_nodes = passive_nodes.partition do |pnode|
126
+ !referenced_nodes.include?(pnode.alcn)
127
+ end
128
+ refs = other_passive_nodes.collect {|n| (n.node_info[:used_meta_info_nodes] + n.node_info[:used_nodes]).to_a}.flatten
129
+ unreferenced_passive_nodes.each {|n| nodes_to_delete << n if !refs.include?(n.alcn)}
130
+
131
+ nodes_to_delete.each {|node| website.tree.delete_node(node)}
98
132
  used_paths.merge(paths_to_use)
99
- paths = create_nodes_from_paths(tree, used_paths.to_a.sort)
133
+ paths = create_nodes_from_paths(used_paths.to_a.sort)
100
134
  unused_paths.merge(used_paths - paths)
101
- tree.node_access[:alcn].each {|name, node| tree.delete_node(node) if node.flagged?(:reinit)}
135
+ website.tree.node_access[:alcn].each {|name, node| website.tree.delete_node(node) if node.flagged?(:reinit)}
102
136
  website.cache.reset_volatile_cache
103
137
  end until used_paths.empty?
104
138
  end
105
139
 
106
- # Write out all changed nodes of the +tree+.
107
- def write_tree(tree)
140
+ # Write out all changed nodes of the <tt>website.tree</tt>.
141
+ def write_tree
108
142
  output = website.blackboard.invoke(:output_instance)
109
143
 
110
- tree.node_access[:alcn].select do |name, node|
111
- use_node = (node != tree.dummy_root && node.flagged?(:dirty))
144
+ website.tree.node_access[:alcn].select do |name, node|
145
+ use_node = (node != website.tree.dummy_root && node.flagged?(:dirty))
112
146
  node.unflag(:dirty_meta_info)
113
147
  node.unflag(:created)
114
148
  node.unflag(:dirty)
115
149
  use_node
116
150
  end.sort.each do |name, node|
117
- next if node['no_output'] || !(content = node.content)
118
-
119
151
  begin
152
+ next if node['no_output'] || !(content = node.content)
153
+
120
154
  puts " "*4 + name, :verbose
121
155
  type = if node.is_directory?
122
156
  :directory
@@ -126,8 +160,11 @@ module Webgen
126
160
  :file
127
161
  end
128
162
  output.write(node.path, content, type)
129
- rescue
130
- raise RuntimeError, "Error while processing <#{node.absolute_lcn}>: #{$!.message}", $!.backtrace
163
+ rescue Webgen::Error => e
164
+ e.alcn = node.alcn unless e.alcn
165
+ raise
166
+ rescue Exception => e
167
+ raise Webgen::RenderError.new(e, nil, node.alcn)
131
168
  end
132
169
  end
133
170
  end
@@ -135,9 +172,15 @@ module Webgen
135
172
  # Return a hash with all source paths.
136
173
  def find_all_source_paths
137
174
  if !defined?(@paths)
138
- source = Webgen::Source::Stacked.new(website.config['sources'].collect do |mp, name, *args|
139
- [mp, constant(name).new(*args)]
140
- end)
175
+ active_source = Webgen::Source::Stacked.new(website.config['sources'].collect do |mp, name, *args|
176
+ [mp, constant(name).new(*args)]
177
+ end)
178
+ passive_source = Webgen::Source::Stacked.new(website.config['passive_sources'].collect do |mp, name, *args|
179
+ [mp, constant(name).new(*args)]
180
+ end, true)
181
+ passive_source.paths.each {|path| path.passive = true }
182
+ source = Webgen::Source::Stacked.new([['/', active_source], ['/', passive_source]])
183
+
141
184
  @paths = {}
142
185
  source.paths.each do |path|
143
186
  if !(website.config['sourcehandler.ignore'].any? {|pat| File.fnmatch(pat, path, File::FNM_CASEFOLD|File::FNM_DOTMATCH)})
@@ -155,13 +198,13 @@ module Webgen
155
198
 
156
199
  options = (website.config['sourcehandler.casefold'] ? File::FNM_CASEFOLD : 0) |
157
200
  (website.config['sourcehandler.use_hidden_files'] ? File::FNM_DOTMATCH : 0)
158
- find_all_source_paths.values_at(*paths).select do |path|
201
+ find_all_source_paths.values_at(*paths).compact.select do |path|
159
202
  patterns.any? {|pat| File.fnmatch(pat, path, options)}
160
203
  end
161
204
  end
162
205
 
163
- # Use the source handlers to create nodes for the +paths+ in the +tree+.
164
- def create_nodes_from_paths(tree, paths)
206
+ # Use the source handlers to create nodes for the +paths+ in the <tt>website.tree</tt>.
207
+ def create_nodes_from_paths(paths)
165
208
  used_paths = Set.new
166
209
  website.config['sourcehandler.invoke'].sort.each do |priority, shns|
167
210
  shns.each do |shn|
@@ -169,36 +212,39 @@ module Webgen
169
212
  handler_paths = paths_for_handler(shn, paths)
170
213
  used_paths.merge(handler_paths)
171
214
  handler_paths.sort {|a,b| a.path.length <=> b.path.length}.each do |path|
172
- parent_dir = path.directory.split('/').collect {|p| Path.new(p).cn}.join('/')
173
- parent_dir += '/' if path != '/' && parent_dir == ''
174
- create_nodes(tree, parent_dir, path, sh)
215
+ if !website.tree[path.parent_path]
216
+ used_paths.merge(create_nodes_from_paths([path.parent_path]))
217
+ end
218
+ create_nodes(path, sh)
175
219
  end
176
220
  end
177
221
  end
178
222
  used_paths
179
223
  end
180
224
 
181
- # Prepare everything to create nodes under the absolute lcn path +parent_path_name+ in the
182
- # +tree from the +path+ using the +source_handler+. If a block is given, the actual creation
183
- # of the nodes is deferred to it. After the nodes are created, it is also checked if they have
184
- # all needed properties.
185
- def create_nodes(tree, parent_path_name, path, source_handler) #:yields: parent, path
186
- if !(parent = tree[parent_path_name])
187
- raise "The specified parent path <#{parent_path_name}> does not exist"
188
- end
225
+ # Prepare everything to create from the +path+ using the +source_handler+. If a block is
226
+ # given, the actual creation of the nodes is deferred to it. Otherwise the #create_node method
227
+ # of the +source_handler+ is used. After the nodes are created, it is also checked if they
228
+ # have all needed properties.
229
+ def create_nodes(path, source_handler) #:yields: path
189
230
  path = path.dup
190
231
  path.meta_info = default_meta_info(path, source_handler.class.name)
191
232
  (website.cache[:sourcehandler_path_mi] ||= {})[[path.path, source_handler.class.name]] = path.meta_info.dup
192
- website.blackboard.dispatch_msg(:before_node_created, parent, path)
233
+ website.blackboard.dispatch_msg(:before_node_created, path)
193
234
  *nodes = if block_given?
194
- yield(parent, path)
235
+ yield(path)
195
236
  else
196
- source_handler.create_node(parent, path)
237
+ source_handler.create_node(path)
197
238
  end
198
239
  nodes.flatten.compact.each do |node|
199
240
  website.blackboard.dispatch_msg(:after_node_created, node)
200
241
  end
201
242
  nodes
243
+ rescue Webgen::Error => e
244
+ e.alcn = path unless e.alcn
245
+ raise
246
+ rescue Exception => e
247
+ raise Webgen::NodeCreationError.new(e, source_handler.class.name, path)
202
248
  end
203
249
 
204
250
  # Return the default meta info for the pair of +path+ and +sh_name+.
@@ -19,7 +19,7 @@ module Webgen::SourceHandler
19
19
  #
20
20
  # The paths that are handled by a source handler are specified via path patterns (see
21
21
  # below). During a webgen run the #create_node method for each source paths that matches a
22
- # specified path pattern is called. And when it is time to write out the node, the the #content
22
+ # specified path pattern is called. And when it is time to write out the node, the #content
23
23
  # method is called to retrieve the rendered content.
24
24
  #
25
25
  # A source handler must not take any parameters on initialization and when this module is not
@@ -35,6 +35,7 @@ module Webgen::SourceHandler
35
35
  # It also provides other utility methods:
36
36
  # * #page_from_path
37
37
  # * #content
38
+ # * #parent_node
38
39
  #
39
40
  # == Nodes Created for Paths
40
41
  #
@@ -109,10 +110,11 @@ module Webgen::SourceHandler
109
110
  # class SimpleCopy
110
111
  #
111
112
  # include Webgen::SourceHandler::Base
113
+ # include Webgen::WebsiteAccess
112
114
  #
113
- # def create_node(parent, path)
115
+ # def create_node(path)
114
116
  # path.ext += '.copied'
115
- # super(parent, path)
117
+ # super(path)
116
118
  # end
117
119
  #
118
120
  # def content(node)
@@ -151,7 +153,8 @@ module Webgen::SourceHandler
151
153
  when :year, :month, :day
152
154
  ctime = path.meta_info['created_at']
153
155
  if !ctime.kind_of?(Time)
154
- raise "Invalid meta info 'created_at' for #{path}, needed because of used output path style"
156
+ raise Webgen::NodeCreationError.new("Invalid meta info 'created_at', needed because of used output path style",
157
+ self.class.name, path)
155
158
  end
156
159
  ctime.send(part).to_s.rjust(2, '0')
157
160
  when Symbol then path.send(part)
@@ -181,36 +184,49 @@ module Webgen::SourceHandler
181
184
  if OutputPathHelpers.public_instance_methods(false).map(&:to_s).include?(method)
182
185
  name = send(method, parent, path, use_lang_part)
183
186
  name += '/' if path.path =~ /\/$/ && name !~ /\/$/
184
- if (node = node_exists?(parent, path, name)) && node.lang == path.meta_info['lang']
185
- name = node.path
186
- elsif node
187
+ if (node = node_exists?(path, name)) && node.lang != path.meta_info['lang']
187
188
  name = send(method, parent, path, (path.meta_info['lang'].nil? ? false : true))
188
189
  name += '/' if path.path =~ /\/$/ && name !~ /\/$/
189
190
  end
190
191
  name
191
192
  else
192
- raise "Unknown method for creating output path: #{method}"
193
+ raise Webgen::NodeCreationError.new("Unknown method for creating output path: #{path.meta_info['output_path']}",
194
+ self.class.name, path)
193
195
  end
194
196
  end
195
197
 
196
- # Check if the node alcn and output path which would be created by #create_node exists. The
198
+ # Check if the node alcn and output path which would be created by #create_node exist. The
197
199
  # +output_path+ to check for can individually be set.
198
- def node_exists?(parent, path, output_path = self.output_path(parent, path))
199
- parent.tree[Webgen::Node.absolute_name(parent, path.lcn, :alcn)] || (!path.meta_info['no_output'] && parent.tree[output_path, :path])
200
+ def node_exists?(path, output_path = self.output_path(parent_node(path), path))
201
+ Webgen::WebsiteAccess.website.tree[path.alcn] || (!path.meta_info['no_output'] && Webgen::WebsiteAccess.website.tree[output_path, :path])
200
202
  end
201
203
 
202
- # Create a node under +parent+ from +path+ if it does not already exists or needs to be
203
- # re-initialized. The found node or the newly created node is returned afterwards. +nil+ is
204
- # returned if no node can be created (e.g. when <tt>path.meta_info['draft']</tt> is set). Some
205
- # additional node information like <tt>:src</tt> and <tt>:processor</tt> is set and the meta
206
- # information is checked for validness. The created/re-initialized node is yielded if a block is
207
- # given.
208
- def create_node(parent, path, output_path = self.output_path(parent, path))
204
+ # Create a node from +path+ if it does not already exists or re-initalize an already existing
205
+ # node. The found node or the newly created node is returned afterwards. +nil+ is returned if no
206
+ # node can be created (e.g. when <tt>path.meta_info['draft']</tt> is set).
207
+ #
208
+ # The +options+ parameter can be used for providing the optional parameters:
209
+ #
210
+ # [<tt>:parent</tt>] The parent node under which the new node should be created. If this is not
211
+ # specified (the usual case), the parent node is determined by the
212
+ # #parent_node method.
213
+ #
214
+ # [<tt>:output_path</tt>] The output path that should be used for the node. If this is not
215
+ # specified (the usual case), the output path is determined via the
216
+ # #output_path method.
217
+ #
218
+ # Some additional node information like <tt>:src</tt> and <tt>:processor</tt> is set and the
219
+ # meta information is checked for validness. The created/re-initialized node is yielded if a
220
+ # block is given.
221
+ def create_node(path, options = {})
209
222
  return nil if path.meta_info['draft']
210
- node = node_exists?(parent, path, output_path)
223
+ parent = options[:parent] || parent_node(path)
224
+ output_path = options[:output_path] || self.output_path(parent, path)
225
+ node = node_exists?(path, output_path)
226
+
211
227
  if node && (node.node_info[:src] != path.source_path || node.node_info[:processor] != self.class.name)
212
- log(:warn) { "Node already exists: source = #{path.source_path} | path = #{node.path} | alcn = #{node.absolute_lcn}"}
213
- return node
228
+ log(:warn) { "Node already exists: source = #{path.source_path} | path = #{node.path} | alcn = #{node.alcn}"}
229
+ return node #TODO: think! should nil be returned?
214
230
  elsif !node
215
231
  node = Webgen::Node.new(parent, output_path, path.cn, path.meta_info)
216
232
  elsif node.flagged?(:reinit)
@@ -218,7 +234,11 @@ module Webgen::SourceHandler
218
234
  else
219
235
  return node
220
236
  end
221
- node['modified_at'] = Time.now unless node['modified_at'].kind_of?(Time)
237
+
238
+ if !node['modified_at'].kind_of?(Time)
239
+ log(:warn) { "Meta information 'modified_at' set to current time in <#{node.alcn}> since its value '#{node['modified_at']}' was of type #{node['modified_at'].class}" } unless node['modified_at'].nil?
240
+ node['modified_at'] = Time.now
241
+ end
222
242
  node.node_info[:src] = path.source_path
223
243
  node.node_info[:creation_path] = path.path
224
244
  node.node_info[:processor] = self.class.name
@@ -226,7 +246,10 @@ module Webgen::SourceHandler
226
246
  node
227
247
  end
228
248
 
229
- # Return the content of the given +node+. This default +content+ method just returns +nil+.
249
+ # Return the content of the given +node+. If the return value is not +nil+ then the node gets
250
+ # written, otherwise it is ignored.
251
+ #
252
+ # This default +content+ method just returns +nil+.
230
253
  def content(node)
231
254
  nil
232
255
  end
@@ -237,12 +260,23 @@ module Webgen::SourceHandler
237
260
  begin
238
261
  page = Webgen::Page.from_data(path.io.data, path.meta_info)
239
262
  rescue Webgen::Page::FormatError => e
240
- raise "Error reading source path <#{path}>: #{e.message}"
263
+ raise Webgen::NodeCreationError.new("Error reading source path: #{e.message}",
264
+ self.class.name, path)
241
265
  end
242
266
  path.meta_info = page.meta_info
243
267
  page
244
268
  end
245
269
 
270
+ # Return the parent node for the given +path+.
271
+ def parent_node(path)
272
+ parent_dir = (path.parent_path == '' ? '' : Webgen::Path.new(path.parent_path).alcn)
273
+ if !(parent = Webgen::WebsiteAccess.website.tree[parent_dir])
274
+ raise Webgen::NodeCreationError.new("The needed parent path <#{parent_dir}> does not exist",
275
+ self.class.name, path)
276
+ end
277
+ parent
278
+ end
279
+
246
280
  end
247
281
 
248
282
  end