lib/webgen/sourcehandler.rb in gettalong-webgen-0.5.5.20081001 vs lib/webgen/sourcehandler.rb in gettalong-webgen-0.5.5.20081010

- old
+ new

@@ -17,10 +17,11 @@ autoload :Page, 'webgen/sourcehandler/page' autoload :Fragment, 'webgen/sourcehandler/fragment' autoload :Virtual, 'webgen/sourcehandler/virtual' autoload :Feed, 'webgen/sourcehandler/feed' autoload :Sitemap, 'webgen/sourcehandler/sitemap' + autoload :Memory, 'webgen/sourcehandler/memory' # This class is used by Website to do the actual rendering of the website. It # # * collects all source paths using the source classes # * creates nodes using the source handler classes @@ -37,66 +38,101 @@ end # Render the nodes provided in the +tree+. Before the actual rendering is done, the sources # are checked (nodes for deleted sources are deleted, nodes for new and changed sources). def render(tree) - # Add new and changed nodes, remove nodes of deleted paths - puts "Generating tree..." - time = Benchmark.measure do - used_paths = Set.new - paths = Set.new([nil]) - while paths.length > 0 - used_paths += (paths = Set.new(find_all_source_paths.keys) - used_paths - clean(tree)) - create_nodes_from_paths(tree, paths) - website.cache.reset_volatile_cache + begin + puts "Updating tree..." + time = Benchmark.measure do + update_tree(tree) end - end - puts "...done in " + ('%2.4f' % time.real) + ' seconds' + puts "...done in " + ('%2.4f' % time.real) + ' seconds' - output = website.blackboard.invoke(:output_instance) - - puts "Writing changed nodes..." - time = Benchmark.measure do - tree.node_access[:alcn].sort.each do |name, node| - node.dirty_meta_info = node.created = false - next if node == tree.dummy_root || !node.dirty - node.dirty = false - - begin - if !node['no_output'] && (content = node.content) - puts " "*4 + name, :verbose - type = if node.is_directory? - :directory - elsif node.is_fragment? - :fragment - else - :file - end - output.write(node.path, content, type) - end - rescue - raise RuntimeError, "Error while processing <#{node.absolute_lcn}>: #{$!.message}", $!.backtrace - end + puts "Writing changed nodes..." + time = Benchmark.measure do + write_tree(tree) end - end - puts "...done in " + ('%2.4f' % time.real) + ' seconds' + puts "...done in " + ('%2.4f' % time.real) + ' seconds' + end while tree.node_access[:alcn].any? {|name,node| node.flagged(:created) || node.flagged(:reinit)} end ####### private ####### + # Update the +tree+ by creating/reinitializing all needed nodes. + def update_tree(tree) + unused_paths = Set.new + begin + used_paths = Set.new(find_all_source_paths.keys) - unused_paths + paths_to_use = Set.new + nodes_to_delete = Set.new + + tree.node_access[:alcn].each do |alcn, node| + next if node == tree.dummy_root + used_paths.delete(node.node_info[:src]) + + deleted = !find_all_source_paths.include?(node.node_info[:src]) + if deleted + nodes_to_delete << node + #TODO: delete output path + elsif (!node.flagged(:created) && find_all_source_paths[node.node_info[:src]].changed?) || node.meta_info_changed? + node.flag(:reinit) + paths_to_use << node.node_info[:src] + elsif node.changed? + # nothing to be done here + end + end + + nodes_to_delete.each {|node| tree.delete_node(node)} + used_paths.merge(paths_to_use) + paths = create_nodes_from_paths(tree, used_paths.to_a.sort) + unused_paths.merge(used_paths - paths) + tree.node_access[:alcn].each {|name, node| tree.delete_node(node) if node.flagged(:reinit)} + website.cache.reset_volatile_cache + end until used_paths.empty? + end + + # Write out all changed nodes of the +tree+. + def write_tree(tree) + output = website.blackboard.invoke(:output_instance) + + tree.node_access[:alcn].select do |name, node| + use_node = (node != tree.dummy_root && node.flagged(:dirty)) + node.unflag(:dirty_meta_info) + node.unflag(:created) + node.unflag(:dirty) + use_node + end.sort.each do |name, node| + next if node['no_output'] || !(content = node.content) + + begin + puts " "*4 + name, :verbose + type = if node.is_directory? + :directory + elsif node.is_fragment? + :fragment + else + :file + end + output.write(node.path, content, type) + rescue + raise RuntimeError, "Error while processing <#{node.absolute_lcn}>: #{$!.message}", $!.backtrace + end + end + end + # Return a hash with all source paths. def find_all_source_paths if !defined?(@paths) source = Webgen::Source::Stacked.new(website.config['sources'].collect do |mp, name, *args| [mp, constant(name).new(*args)] end) @paths = {} source.paths.each do |path| if !(website.config['sourcehandler.ignore'].any? {|pat| File.fnmatch(pat, path, File::FNM_CASEFOLD|File::FNM_DOTMATCH)}) - @paths[path.path] = path + @paths[path.source_path] = path end end end @paths end @@ -113,20 +149,24 @@ end end # Use the source handlers to create nodes for the +paths+ in the +tree+. def create_nodes_from_paths(tree, paths) + used_paths = Set.new website.config['sourcehandler.invoke'].sort.each do |priority, shns| shns.each do |shn| sh = website.cache.instance(shn) - paths_for_handler(shn, paths).sort.each do |path| + handler_paths = paths_for_handler(shn, paths) + used_paths.merge(handler_paths) + handler_paths.sort {|a,b| a.path.length <=> b.path.length}.each do |path| parent_dir = path.directory.split('/').collect {|p| Path.new(p).cn}.join('/') parent_dir += '/' if path != '/' && parent_dir == '' create_nodes(tree, parent_dir, path, sh) end end end + used_paths end # Prepare everything to create nodes under the absolute lcn path +parent_path_name+ in the # +tree from the +path+ using the +source_handler+. If a block is given, the actual creation # of the nodes is deferred to it. After the nodes are created, it is also checked if they have @@ -140,11 +180,11 @@ (website.cache[:sourcehandler_path_mi] ||= {})[[path.path, source_handler.class.name]] = path.meta_info.dup website.blackboard.dispatch_msg(:before_node_created, parent, path) *nodes = if block_given? yield(parent, path) else - source_handler.create_node(parent, path.dup) + source_handler.create_node(parent, path) end nodes.flatten.compact.each do |node| website.blackboard.dispatch_msg(:after_node_created, node) end nodes @@ -158,42 +198,15 @@ # Check if the default meta information for +node+ has changed since the last run. But don't # take the node's path's +modified_at+ meta information into account since that changes on # every path change. def meta_info_changed?(node) - path = node.node_info[:src] + path = node.node_info[:creation_path] old_mi = website.cache[:sourcehandler_path_mi][[path, node.node_info[:processor]]] old_mi.delete('modified_at') - new_mi = default_meta_info(@paths[path], node.node_info[:processor]) + new_mi = default_meta_info(@paths[path] || Webgen::Path.new(path), node.node_info[:processor]) new_mi.delete('modified_at') - node.dirty_meta_info = true if !old_mi || old_mi != new_mi - end - - # Clean the +tree+ by deleting nodes which have changed or which don't have an associated - # source anymore. Return all paths for which nodes need to be created. - def clean(tree) - paths_to_delete = Set.new - paths_not_to_delete = Set.new - nodes_to_be_deleted = Set.new - tree.node_access[:alcn].each do |alcn, node| - next if node == tree.dummy_root || tree[alcn].nil? - - deleted = !find_all_source_paths.include?(node.node_info[:src]) - if !node.created && (deleted || - find_all_source_paths[node.node_info[:src]].changed? || - node.changed?) - paths_not_to_delete << node.node_info[:src] - nodes_to_be_deleted << [node, deleted] - else - paths_to_delete << node.node_info[:src] - end - end - - nodes_to_be_deleted.each {|node, deleted| tree.delete_node(node, deleted)} - #TODO: delete output path - - # source paths that should be used - paths_to_delete - paths_not_to_delete + node.flag(:dirty_meta_info) if !old_mi || old_mi != new_mi end end end