webgen 0.5.5 → 0.5.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. data/ChangeLog +3662 -0
  2. data/Rakefile +8 -1
  3. data/VERSION +1 -1
  4. data/data/webgen/website_styles/1024px/src/default.template +1 -1
  5. data/data/webgen/website_styles/andreas00/src/default.template +1 -1
  6. data/data/webgen/website_styles/andreas01/src/default.template +2 -1
  7. data/data/webgen/website_styles/andreas03/src/default.template +1 -1
  8. data/data/webgen/website_styles/andreas04/src/default.template +1 -1
  9. data/data/webgen/website_styles/andreas05/src/default.template +1 -1
  10. data/data/webgen/website_styles/andreas06/src/default.template +1 -1
  11. data/data/webgen/website_styles/andreas07/src/default.template +1 -1
  12. data/data/webgen/website_styles/andreas08/src/default.template +1 -1
  13. data/data/webgen/website_styles/andreas09/src/default.template +1 -1
  14. data/data/webgen/website_styles/simple/src/default.template +1 -1
  15. data/doc/contentprocessor/fragments.page +25 -0
  16. data/doc/extensions.page +1 -1
  17. data/doc/manual.page +33 -15
  18. data/doc/reference_configuration.page +339 -62
  19. data/doc/reference_metainfo.page +11 -2
  20. data/doc/sourcehandler/feed.page +19 -4
  21. data/doc/sourcehandler/page.page +0 -16
  22. data/doc/tag/langbar.page +8 -1
  23. data/doc/tag/link.page +44 -0
  24. data/doc/tag/tikz.page +158 -0
  25. data/lib/webgen/cli.rb +4 -4
  26. data/lib/webgen/common/sitemap.rb +2 -3
  27. data/lib/webgen/configuration.rb +3 -1
  28. data/lib/webgen/contentprocessor.rb +1 -0
  29. data/lib/webgen/contentprocessor/blocks.rb +0 -2
  30. data/lib/webgen/contentprocessor/context.rb +0 -3
  31. data/lib/webgen/contentprocessor/erubis.rb +0 -2
  32. data/lib/webgen/contentprocessor/fragments.rb +23 -0
  33. data/lib/webgen/default_config.rb +15 -2
  34. data/lib/webgen/languages.rb +9 -0
  35. data/lib/webgen/logger.rb +18 -1
  36. data/lib/webgen/node.rb +50 -25
  37. data/lib/webgen/page.rb +26 -16
  38. data/lib/webgen/path.rb +20 -10
  39. data/lib/webgen/sourcehandler.rb +85 -69
  40. data/lib/webgen/sourcehandler/base.rb +38 -15
  41. data/lib/webgen/sourcehandler/copy.rb +2 -2
  42. data/lib/webgen/sourcehandler/directory.rb +16 -13
  43. data/lib/webgen/sourcehandler/feed.rb +6 -12
  44. data/lib/webgen/sourcehandler/fragment.rb +6 -11
  45. data/lib/webgen/sourcehandler/memory.rb +41 -0
  46. data/lib/webgen/sourcehandler/metainfo.rb +21 -21
  47. data/lib/webgen/sourcehandler/page.rb +7 -27
  48. data/lib/webgen/sourcehandler/sitemap.rb +0 -2
  49. data/lib/webgen/sourcehandler/template.rb +0 -4
  50. data/lib/webgen/sourcehandler/virtual.rb +18 -18
  51. data/lib/webgen/tag.rb +2 -0
  52. data/lib/webgen/tag/breadcrumbtrail.rb +1 -4
  53. data/lib/webgen/tag/coderay.rb +0 -3
  54. data/lib/webgen/tag/date.rb +0 -2
  55. data/lib/webgen/tag/executecommand.rb +1 -2
  56. data/lib/webgen/tag/includefile.rb +1 -3
  57. data/lib/webgen/tag/langbar.rb +2 -5
  58. data/lib/webgen/tag/link.rb +23 -0
  59. data/lib/webgen/tag/menu.rb +1 -4
  60. data/lib/webgen/tag/metainfo.rb +0 -2
  61. data/lib/webgen/tag/relocatable.rb +2 -3
  62. data/lib/webgen/tag/sitemap.rb +0 -3
  63. data/lib/webgen/tag/tikz.rb +117 -0
  64. data/lib/webgen/tree.rb +11 -6
  65. data/lib/webgen/version.rb +1 -1
  66. data/lib/webgen/website.rb +2 -1
  67. data/test/test_cli.rb +14 -0
  68. data/test/test_common_sitemap.rb +4 -4
  69. data/test/test_contentprocessor_context.rb +1 -1
  70. data/test/test_contentprocessor_fragments.rb +40 -0
  71. data/test/test_contentprocessor_redcloth.rb +1 -0
  72. data/test/test_contentprocessor_tags.rb +1 -1
  73. data/test/test_languages.rb +12 -0
  74. data/test/test_logger.rb +19 -0
  75. data/test/test_node.rb +35 -15
  76. data/test/test_output_filesystem.rb +1 -1
  77. data/test/test_page.rb +15 -6
  78. data/test/test_path.rb +37 -5
  79. data/test/test_source_filesystem.rb +1 -1
  80. data/test/test_source_stacked.rb +1 -1
  81. data/test/test_sourcehandler_base.rb +30 -1
  82. data/test/test_sourcehandler_copy.rb +1 -1
  83. data/test/test_sourcehandler_directory.rb +16 -1
  84. data/test/test_sourcehandler_feed.rb +9 -8
  85. data/test/test_sourcehandler_fragment.rb +1 -1
  86. data/test/test_sourcehandler_memory.rb +42 -0
  87. data/test/test_sourcehandler_metainfo.rb +23 -21
  88. data/test/test_sourcehandler_page.rb +5 -12
  89. data/test/test_sourcehandler_template.rb +1 -1
  90. data/test/test_sourcehandler_virtual.rb +2 -2
  91. data/test/test_tag_base.rb +0 -1
  92. data/test/test_tag_breadcrumbtrail.rb +4 -4
  93. data/test/test_tag_includefile.rb +3 -3
  94. data/test/test_tag_langbar.rb +12 -7
  95. data/test/test_tag_link.rb +61 -0
  96. data/test/test_tag_menu.rb +7 -7
  97. data/test/test_tag_metainfo.rb +1 -1
  98. data/test/test_tag_relocatable.rb +1 -1
  99. data/test/test_tag_tikz.rb +66 -0
  100. data/test/test_tree.rb +8 -9
  101. metadata +15 -2
@@ -50,6 +50,15 @@ module Webgen
50
50
  self.to_s <=> other.to_s
51
51
  end
52
52
 
53
+ def eql?(other) #:nodoc:
54
+ (other.is_a?(self.class) && other.to_s == self.to_s) ||
55
+ (other.is_a?(String) && self.to_s == other)
56
+ end
57
+
58
+ def hash #:nodoc:
59
+ self.to_s.hash
60
+ end
61
+
53
62
  end
54
63
 
55
64
 
@@ -27,11 +27,28 @@ module Webgen
27
27
  end
28
28
  self.level = ::Logger::WARN
29
29
  self.verbosity = :normal
30
+ @marks = []
30
31
  end
31
32
 
32
33
  # Returns the output of the logger when #sync is +false+. Otherwise an empty string is returned.
33
34
  def log_output
34
- @sync ? '' : @logio.string
35
+ if @sync
36
+ ''
37
+ else
38
+ out = @logio.string.dup
39
+ @marks.reverse.each_with_index do |mark, index|
40
+ out.insert(mark, " INFO -- Log messages for run #{@marks.length - index} are following\n")
41
+ end if out.length > 0
42
+ out
43
+ end
44
+ end
45
+
46
+ # Only used when #sync is +false: Mark the location in the log stream where a new update/write
47
+ # run begins.
48
+ def mark_new_cycle
49
+ if !@sync
50
+ @marks << @logio.string.length
51
+ end
35
52
  end
36
53
 
37
54
  # The severity threshold level.
@@ -46,17 +46,6 @@ module Webgen
46
46
  # Meta information associated with the node.
47
47
  attr_reader :meta_info
48
48
 
49
- # Set by other objects to +true+ if they think the object has changed since the last run. Must
50
- # not be set to +false+ once it is +true+!
51
- attr_accessor :dirty
52
-
53
- # Set by other objects to +true+ if the meta information of the node has changed since the last
54
- # run. Must not be set to +false+ once it is +true+!
55
- attr_accessor :dirty_meta_info
56
-
57
- # Has the node been created or has it been read from the cache?
58
- attr_accessor :created
59
-
60
49
  # Create a new Node instance.
61
50
  #
62
51
  # +parent+ (immutable)::
@@ -78,17 +67,29 @@ module Webgen
78
67
  # found, the node is language neutral.
79
68
  def initialize(parent, path, cn, meta_info = {})
80
69
  @parent = parent
81
- @path = path.freeze
82
70
  @cn = cn.chomp('/').freeze
83
- @lang = meta_info.delete('lang').freeze
84
- @lang = nil unless is_file?
85
- @meta_info = meta_info
86
71
  @children = []
87
- @dirty = true
88
- @created = true
72
+ reinit(path, meta_info)
89
73
  init_rest
90
74
  end
91
75
 
76
+ # Re-initializes an already initialized node and resets it to its pristine state.
77
+ def reinit(path, meta_info = {})
78
+ old_path = @path
79
+ @path = path.freeze
80
+ @lang = Webgen::LanguageManager.language_for_code(meta_info.delete('lang'))
81
+ @lang = nil unless is_file?
82
+ @meta_info = meta_info
83
+ @flags = Set.new([:dirty, :created])
84
+ if @tree
85
+ @tree.node_access[:path].delete(old_path) if old_path
86
+ @tree.register_path(self)
87
+ self.node_info.clear
88
+ self.node_info[:used_nodes] = Set.new
89
+ self.node_info[:used_meta_info_nodes] = Set.new
90
+ end
91
+ end
92
+
92
93
  # Return the meta information item for +key+.
93
94
  def [](key)
94
95
  @meta_info[key]
@@ -116,26 +117,50 @@ module Webgen
116
117
  # Check if the node is the root node.
117
118
  def is_root?; self == tree.root; end
118
119
 
120
+ # Check if the node is flagged with one of the following:
121
+ #
122
+ # :created:: Has the node been created or has it been read from the cache?
123
+ # :reinit:: Does the node need to be reinitialized?
124
+ # :dirty:: Set by other objects to +true+ if they think the object has changed since the last
125
+ # run. Must not be set to +false+ once it is +true+!
126
+ # :dirty_meta_info:: Set by other objects to +true+ if the meta information of the node has
127
+ # changed since the last run. Must not be set to +false+ once it is +true+!
128
+ def flagged(key)
129
+ @flags.include?(key)
130
+ end
131
+
132
+ # Flag the node with the +keys+. See #flagged for valid keys.
133
+ def flag(*keys)
134
+ @flags += keys
135
+ website.blackboard.dispatch_msg(:node_flagged, self, keys)
136
+ end
137
+
138
+ # Remove the flags +keys+ from the node.
139
+ def unflag(*keys)
140
+ @flags.subtract(keys)
141
+ website.blackboard.dispatch_msg(:node_unflagged, self, keys)
142
+ end
143
+
119
144
  # Return +true+ if the node has changed since the last webgen run. If it has changed, +dirty+ is
120
145
  # set to +true+.
121
146
  def changed?
122
147
  if_not_checked(:node) do
123
- @dirty = @dirty || meta_info_changed?
124
- @dirty = node_info[:used_nodes].any? {|n| n != @absolute_lcn && (!tree[n] || tree[n].changed?)} unless @dirty
125
- website.blackboard.dispatch_msg(:node_changed?, self) unless @dirty
148
+ flag(:dirty) if meta_info_changed? ||
149
+ node_info[:used_nodes].any? {|n| n != @absolute_lcn && (!tree[n] || tree[n].changed?)}
150
+ website.blackboard.dispatch_msg(:node_changed?, self) unless flagged(:dirty)
126
151
  end
127
- @dirty
152
+ flagged(:dirty)
128
153
  end
129
154
 
130
155
  # Return +true+ if the meta information of the node has changed.
131
156
  def meta_info_changed?
132
157
  if_not_checked(:meta_info) do
133
- @dirty_meta_info = node_info[:used_meta_info_nodes].any? do |n|
158
+ flag(:dirty_meta_info) if node_info[:used_meta_info_nodes].any? do |n|
134
159
  n != @absolute_lcn && (!tree[n] || tree[n].meta_info_changed?)
135
- end unless @dirty_meta_info
136
- website.blackboard.dispatch_msg(:node_meta_info_changed?, self) unless @dirty_meta_info
160
+ end
161
+ website.blackboard.dispatch_msg(:node_meta_info_changed?, self) unless flagged(:dirty_meta_info)
137
162
  end
138
- @dirty_meta_info
163
+ flagged(:dirty_meta_info)
139
164
  end
140
165
 
141
166
  # Return an informative representation of the node.
@@ -55,14 +55,17 @@ module Webgen
55
55
  # the information. The +meta_info+ parameter can be used to provide default meta information.
56
56
  def from_data(data, meta_info = {})
57
57
  md = /(#{RE_META_INFO})?(.*)/m.match(normalize_eol(data))
58
- if md[1].nil? && data =~ RE_META_INFO_START
59
- raise WebgenPageFormatError, 'Found start line for meta information block but no valid meta information block'
60
- end
61
- meta_info = meta_info.merge(md[1].nil? ? {} : parse_meta_info(md[1]))
58
+ meta_info = meta_info.merge(parse_meta_info(md[1], data))
62
59
  blocks = parse_blocks(md[2] || '', meta_info)
63
60
  new(meta_info, blocks)
64
61
  end
65
62
 
63
+ # Parse the given string +data+ in Webgen Page Format and return the found meta information.
64
+ def meta_info_from_data(data)
65
+ md = /(#{RE_META_INFO})?/m.match(normalize_eol(data))
66
+ parse_meta_info(md[1], data)
67
+ end
68
+
66
69
  #######
67
70
  private
68
71
  #######
@@ -72,17 +75,24 @@ module Webgen
72
75
  data.gsub(/\r\n?/, "\n")
73
76
  end
74
77
 
75
- # Parse the meta info string +data+ and return the hash with the meta information.
76
- def parse_meta_info(data)
77
- begin
78
- meta_info = YAML::load(data)
79
- unless meta_info.kind_of?(Hash)
80
- raise WebgenPageFormatError, "Invalid structure of meta information block: expected YAML hash but found #{meta_info.class}"
78
+ # Parse the meta info string in +mi_data+ and return the hash with the meta information. The
79
+ # original +data+ is used for checking the validness of the meta information block.
80
+ def parse_meta_info(mi_data, data)
81
+ if mi_data.nil? && data =~ RE_META_INFO_START
82
+ raise WebgenPageFormatError, 'Found start line for meta information block but no valid meta information block'
83
+ elsif mi_data.nil?
84
+ {}
85
+ else
86
+ begin
87
+ meta_info = YAML::load(mi_data.to_s)
88
+ unless meta_info.kind_of?(Hash)
89
+ raise WebgenPageFormatError, "Invalid structure of meta information block: expected YAML hash but found #{meta_info.class}"
90
+ end
91
+ rescue ArgumentError => e
92
+ raise WebgenPageFormatError, e.message
81
93
  end
82
- rescue ArgumentError => e
83
- raise WebgenPageFormatError, e.message
94
+ meta_info
84
95
  end
85
- meta_info
86
96
  end
87
97
 
88
98
  # Parse all blocks in +data+ and return them. Meta information can be provided in +meta_info+
@@ -96,7 +106,7 @@ module Webgen
96
106
  options, content = *block_data
97
107
  md = RE_BLOCKS_OPTIONS.match(options.to_s)
98
108
  raise(WebgenPageFormatError, "Found invalid blocks starting line for block #{index+1}: #{options}") if content =~ /\A---/ || md.nil?
99
- options = Hash[*md[1].to_s.scan(/(\w+):([^\s]*)/).map {|k,v| [k, YAML::load(v)]}.flatten]
109
+ options = Hash[*md[1].to_s.scan(/(\w+):([^\s]*)/).map {|k,v| [k, (v == '' ? nil : YAML::load(v))]}.flatten]
100
110
  options = (meta_info['blocks']['default'] || {} rescue {}).
101
111
  merge((meta_info['blocks'][index+1] || {} rescue {})).
102
112
  merge(options)
@@ -118,12 +128,12 @@ module Webgen
118
128
  # The contents of the meta information block.
119
129
  attr_reader :meta_info
120
130
 
121
- # The array of blocks for the page.
131
+ # The hash of blocks for the page.
122
132
  attr_reader :blocks
123
133
 
124
134
  # Create a new Page object with the meta information provided in +meta_info+ and the given
125
135
  # +blocks+.
126
- def initialize(meta_info = {}, blocks = nil)
136
+ def initialize(meta_info = {}, blocks = {})
127
137
  @meta_info = meta_info
128
138
  @blocks = blocks
129
139
  end
@@ -35,11 +35,24 @@ module Webgen
35
35
  end
36
36
 
37
37
 
38
+ # Return +true+ if the given +path+ matches the given +pattern+ (trailing slashes of directories
39
+ # are not respected). For information on which patterns are supported, have a look at the
40
+ # documentation of File.fnmatch.
41
+ def self.match(path, pattern)
42
+ path = path.to_s.chomp('/') unless path == '/'
43
+ pattern = pattern.to_s.chomp('/') unless pattern == '/'
44
+ File.fnmatch(pattern, path, File::FNM_DOTMATCH|File::FNM_CASEFOLD|File::FNM_PATHNAME)
45
+ end
46
+
47
+
38
48
  include Comparable
39
49
 
40
- # The full source path.
50
+ # The full path.
41
51
  attr_accessor :path
42
52
 
53
+ # The source path that lead to the creation of this path.
54
+ attr_accessor :source_path
55
+
43
56
  # The basename part of the path.
44
57
  attr_accessor :basename
45
58
 
@@ -55,11 +68,13 @@ module Webgen
55
68
  # Extracted meta information for the path.
56
69
  attr_accessor :meta_info
57
70
 
58
- # Create a new Path object for +path+. The optional block needs to return an IO object for the
59
- # content of the path.
60
- def initialize(path, &ioblock)
71
+ # Create a new Path object for +path+. The optional +source_path+ parameter specifies the path
72
+ # that lead to the creation of this path. The optional block needs to return an IO object for
73
+ # the content of the path.
74
+ def initialize(path, source_path = path, &ioblock)
61
75
  @meta_info = {}
62
76
  @io = SourceIO.new(&ioblock) if block_given?
77
+ @source_path = source_path
63
78
  analyse(path)
64
79
  end
65
80
 
@@ -70,6 +85,7 @@ module Webgen
70
85
  temp.path = temp.path.sub(/^#{Regexp.escape(prefix.chomp("/"))}/, '') if prefix #"
71
86
  reanalyse = (@path == '/' || temp.path == '/')
72
87
  temp.path = File.join(mp, temp.path)
88
+ temp.source_path = temp.path if @path == @source_path
73
89
  if reanalyse
74
90
  temp.send(:analyse, temp.path)
75
91
  else
@@ -131,12 +147,6 @@ module Webgen
131
147
  end
132
148
  alias_method(:eql?, :==)
133
149
 
134
- # Return +true+ if the localized path matches the given +pattern+. For information on which
135
- # patterns are supported, have a look at the documentation of File.fnmatch.
136
- def =~(pattern)
137
- File.fnmatch(pattern, File.join(@directory, lcn), File::FNM_DOTMATCH|File::FNM_CASEFOLD|File::FNM_PATHNAME)
138
- end
139
-
140
150
  # Implemented sothat a Path looks like a String when used as key in a hash.
141
151
  def <=>(other)
142
152
  @path <=> other.to_str
@@ -19,6 +19,7 @@ module Webgen
19
19
  autoload :Virtual, 'webgen/sourcehandler/virtual'
20
20
  autoload :Feed, 'webgen/sourcehandler/feed'
21
21
  autoload :Sitemap, 'webgen/sourcehandler/sitemap'
22
+ autoload :Memory, 'webgen/sourcehandler/memory'
22
23
 
23
24
  # This class is used by Website to do the actual rendering of the website. It
24
25
  #
@@ -39,52 +40,90 @@ module Webgen
39
40
  # Render the nodes provided in the +tree+. Before the actual rendering is done, the sources
40
41
  # are checked (nodes for deleted sources are deleted, nodes for new and changed sources).
41
42
  def render(tree)
42
- # Add new and changed nodes, remove nodes of deleted paths
43
- puts "Generating tree..."
44
- time = Benchmark.measure do
45
- used_paths = Set.new
46
- paths = Set.new([nil])
47
- while paths.length > 0
48
- used_paths += (paths = Set.new(find_all_source_paths.keys) - used_paths - clean(tree))
49
- create_nodes_from_paths(tree, paths)
43
+ begin
44
+ website.logger.mark_new_cycle if website.logger
45
+
46
+ puts "Updating tree..."
47
+ time = Benchmark.measure do
50
48
  website.cache.reset_volatile_cache
49
+ update_tree(tree)
51
50
  end
52
- end
53
- puts "...done in " + ('%2.4f' % time.real) + ' seconds'
54
-
55
- output = website.blackboard.invoke(:output_instance)
51
+ puts "...done in " + ('%2.4f' % time.real) + ' seconds'
56
52
 
57
- puts "Writing changed nodes..."
58
- time = Benchmark.measure do
59
- tree.node_access[:alcn].sort.each do |name, node|
60
- node.dirty_meta_info = node.created = false
61
- next if node == tree.dummy_root || !node.dirty
62
- node.dirty = false
63
-
64
- begin
65
- if !node['no_output'] && (content = node.content)
66
- puts " "*4 + name, :verbose
67
- type = if node.is_directory?
68
- :directory
69
- elsif node.is_fragment?
70
- :fragment
71
- else
72
- :file
73
- end
74
- output.write(node.path, content, type)
75
- end
76
- rescue
77
- raise RuntimeError, "Error while processing <#{node.absolute_lcn}>: #{$!.message}", $!.backtrace
78
- end
53
+ puts "Writing changed nodes..."
54
+ time = Benchmark.measure do
55
+ write_tree(tree)
79
56
  end
80
- end
81
- puts "...done in " + ('%2.4f' % time.real) + ' seconds'
57
+ puts "...done in " + ('%2.4f' % time.real) + ' seconds'
58
+ end while tree.node_access[:alcn].any? {|name,node| node.flagged(:created) || node.flagged(:reinit)}
82
59
  end
83
60
 
84
61
  #######
85
62
  private
86
63
  #######
87
64
 
65
+ # Update the +tree+ by creating/reinitializing all needed nodes.
66
+ def update_tree(tree)
67
+ unused_paths = Set.new
68
+ begin
69
+ used_paths = Set.new(find_all_source_paths.keys) - unused_paths
70
+ paths_to_use = Set.new
71
+ nodes_to_delete = Set.new
72
+
73
+ tree.node_access[:alcn].each do |alcn, node|
74
+ next if node == tree.dummy_root
75
+ used_paths.delete(node.node_info[:src])
76
+
77
+ deleted = !find_all_source_paths.include?(node.node_info[:src])
78
+ if deleted
79
+ nodes_to_delete << node
80
+ #TODO: delete output path
81
+ elsif (!node.flagged(:created) && find_all_source_paths[node.node_info[:src]].changed?) || node.meta_info_changed?
82
+ node.flag(:reinit)
83
+ paths_to_use << node.node_info[:src]
84
+ elsif node.changed?
85
+ # nothing to be done here
86
+ end
87
+ end
88
+
89
+ nodes_to_delete.each {|node| tree.delete_node(node)}
90
+ used_paths.merge(paths_to_use)
91
+ paths = create_nodes_from_paths(tree, used_paths.to_a.sort)
92
+ unused_paths.merge(used_paths - paths)
93
+ tree.node_access[:alcn].each {|name, node| tree.delete_node(node) if node.flagged(:reinit)}
94
+ website.cache.reset_volatile_cache
95
+ end until used_paths.empty?
96
+ end
97
+
98
+ # Write out all changed nodes of the +tree+.
99
+ def write_tree(tree)
100
+ output = website.blackboard.invoke(:output_instance)
101
+
102
+ tree.node_access[:alcn].select do |name, node|
103
+ use_node = (node != tree.dummy_root && node.flagged(:dirty))
104
+ node.unflag(:dirty_meta_info)
105
+ node.unflag(:created)
106
+ node.unflag(:dirty)
107
+ use_node
108
+ end.sort.each do |name, node|
109
+ next if node['no_output'] || !(content = node.content)
110
+
111
+ begin
112
+ puts " "*4 + name, :verbose
113
+ type = if node.is_directory?
114
+ :directory
115
+ elsif node.is_fragment?
116
+ :fragment
117
+ else
118
+ :file
119
+ end
120
+ output.write(node.path, content, type)
121
+ rescue
122
+ raise RuntimeError, "Error while processing <#{node.absolute_lcn}>: #{$!.message}", $!.backtrace
123
+ end
124
+ end
125
+ end
126
+
88
127
  # Return a hash with all source paths.
89
128
  def find_all_source_paths
90
129
  if !defined?(@paths)
@@ -94,7 +133,7 @@ module Webgen
94
133
  @paths = {}
95
134
  source.paths.each do |path|
96
135
  if !(website.config['sourcehandler.ignore'].any? {|pat| File.fnmatch(pat, path, File::FNM_CASEFOLD|File::FNM_DOTMATCH)})
97
- @paths[path.path] = path
136
+ @paths[path.source_path] = path
98
137
  end
99
138
  end
100
139
  end
@@ -115,16 +154,20 @@ module Webgen
115
154
 
116
155
  # Use the source handlers to create nodes for the +paths+ in the +tree+.
117
156
  def create_nodes_from_paths(tree, paths)
157
+ used_paths = Set.new
118
158
  website.config['sourcehandler.invoke'].sort.each do |priority, shns|
119
159
  shns.each do |shn|
120
160
  sh = website.cache.instance(shn)
121
- paths_for_handler(shn, paths).sort.each do |path|
161
+ handler_paths = paths_for_handler(shn, paths)
162
+ used_paths.merge(handler_paths)
163
+ handler_paths.sort {|a,b| a.path.length <=> b.path.length}.each do |path|
122
164
  parent_dir = path.directory.split('/').collect {|p| Path.new(p).cn}.join('/')
123
165
  parent_dir += '/' if path != '/' && parent_dir == ''
124
166
  create_nodes(tree, parent_dir, path, sh)
125
167
  end
126
168
  end
127
169
  end
170
+ used_paths
128
171
  end
129
172
 
130
173
  # Prepare everything to create nodes under the absolute lcn path +parent_path_name+ in the
@@ -142,7 +185,7 @@ module Webgen
142
185
  *nodes = if block_given?
143
186
  yield(parent, path)
144
187
  else
145
- source_handler.create_node(parent, path.dup)
188
+ source_handler.create_node(parent, path)
146
189
  end
147
190
  nodes.flatten.compact.each do |node|
148
191
  website.blackboard.dispatch_msg(:after_node_created, node)
@@ -160,39 +203,12 @@ module Webgen
160
203
  # take the node's path's +modified_at+ meta information into account since that changes on
161
204
  # every path change.
162
205
  def meta_info_changed?(node)
163
- path = node.node_info[:src]
206
+ path = node.node_info[:creation_path]
164
207
  old_mi = website.cache[:sourcehandler_path_mi][[path, node.node_info[:processor]]]
165
208
  old_mi.delete('modified_at')
166
- new_mi = default_meta_info(@paths[path], node.node_info[:processor])
209
+ new_mi = default_meta_info(@paths[path] || Webgen::Path.new(path), node.node_info[:processor])
167
210
  new_mi.delete('modified_at')
168
- node.dirty_meta_info = true if !old_mi || old_mi != new_mi
169
- end
170
-
171
- # Clean the +tree+ by deleting nodes which have changed or which don't have an associated
172
- # source anymore. Return all paths for which nodes need to be created.
173
- def clean(tree)
174
- paths_to_delete = Set.new
175
- paths_not_to_delete = Set.new
176
- nodes_to_be_deleted = Set.new
177
- tree.node_access[:alcn].each do |alcn, node|
178
- next if node == tree.dummy_root || tree[alcn].nil?
179
-
180
- deleted = !find_all_source_paths.include?(node.node_info[:src])
181
- if !node.created && (deleted ||
182
- find_all_source_paths[node.node_info[:src]].changed? ||
183
- node.changed?)
184
- paths_not_to_delete << node.node_info[:src]
185
- nodes_to_be_deleted << [node, deleted]
186
- else
187
- paths_to_delete << node.node_info[:src]
188
- end
189
- end
190
-
191
- nodes_to_be_deleted.each {|node, deleted| tree.delete_node(node, deleted)}
192
- #TODO: delete output path
193
-
194
- # source paths that should be used
195
- paths_to_delete - paths_not_to_delete
211
+ node.flag(:dirty_meta_info) if !old_mi || old_mi != new_mi
196
212
  end
197
213
 
198
214
  end