webgen 1.0.0.beta3 → 1.0.0

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 (148) hide show
  1. checksums.yaml +7 -0
  2. data/COPYING +23 -1
  3. data/Rakefile +16 -77
  4. data/VERSION +1 -1
  5. data/bin/webgen +0 -0
  6. data/data/webgen/basic_website_template/ext/init.rb +3 -1
  7. data/data/webgen/basic_website_template/src/.gitignore +0 -0
  8. data/data/webgen/basic_website_template/webgen.config +1 -1
  9. data/data/webgen/bundle_template_files/info.yaml.erb +14 -2
  10. data/data/webgen/passive_sources/default.metainfo +10 -0
  11. data/data/webgen/passive_sources/templates/api.template +16 -2
  12. data/data/webgen/passive_sources/templates/feed.template +5 -5
  13. data/data/webgen/passive_sources/templates/sitemap.template +1 -1
  14. data/data/webgen/passive_sources/templates/tag.template +8 -4
  15. data/lib/webgen/blackboard.rb +21 -5
  16. data/lib/webgen/bundle/built-in-show-changes/info.yaml +17 -0
  17. data/lib/webgen/bundle/built-in-show-changes/init.rb +4 -5
  18. data/lib/webgen/bundle/built-in/info.yaml +1064 -0
  19. data/lib/webgen/bundle/built-in/init.rb +103 -136
  20. data/lib/webgen/bundle_loader.rb +82 -9
  21. data/lib/webgen/cli.rb +12 -8
  22. data/lib/webgen/cli/commands/create.rb +27 -0
  23. data/lib/webgen/cli/{create_bundle_command.rb → commands/create_bundle.rb} +2 -2
  24. data/lib/webgen/cli/{create_command.rb → commands/create_website.rb} +2 -2
  25. data/lib/webgen/cli/commands/generate.rb +66 -0
  26. data/lib/webgen/cli/{install_bundle_command.rb → commands/install.rb} +1 -1
  27. data/lib/webgen/cli/{show_command.rb → commands/show.rb} +6 -4
  28. data/lib/webgen/cli/{list_bundle_command.rb → commands/show_bundles.rb} +12 -15
  29. data/lib/webgen/cli/{show_config_command.rb → commands/show_config.rb} +34 -6
  30. data/lib/webgen/cli/{show_dependencies_command.rb → commands/show_dependencies.rb} +0 -0
  31. data/lib/webgen/cli/{show_extensions_command.rb → commands/show_extensions.rb} +1 -13
  32. data/lib/webgen/cli/{show_tree_command.rb → commands/show_tree.rb} +3 -0
  33. data/lib/webgen/cli/utils.rb +1 -1
  34. data/lib/webgen/configuration.rb +9 -11
  35. data/lib/webgen/content_processor/html_head.rb +3 -4
  36. data/lib/webgen/content_processor/rainpress.rb +21 -0
  37. data/lib/webgen/content_processor/sass.rb +8 -8
  38. data/lib/webgen/content_processor/tikz.rb +59 -16
  39. data/lib/webgen/item_tracker.rb +33 -12
  40. data/lib/webgen/item_tracker/missing_node.rb +5 -5
  41. data/lib/webgen/item_tracker/template_chain.rb +52 -0
  42. data/lib/webgen/misc/dummy_index.rb +78 -0
  43. data/lib/webgen/node.rb +1 -1
  44. data/lib/webgen/node_finder.rb +86 -141
  45. data/lib/webgen/page.rb +5 -5
  46. data/lib/webgen/path.rb +4 -1
  47. data/lib/webgen/path_handler.rb +25 -21
  48. data/lib/webgen/path_handler/api.rb +36 -3
  49. data/lib/webgen/path_handler/base.rb +20 -4
  50. data/lib/webgen/path_handler/feed.rb +6 -2
  51. data/lib/webgen/path_handler/meta_info.rb +4 -2
  52. data/lib/webgen/path_handler/page.rb +5 -7
  53. data/lib/webgen/path_handler/sitemap.rb +6 -1
  54. data/lib/webgen/path_handler/virtual.rb +6 -8
  55. data/lib/webgen/source/file_system.rb +2 -2
  56. data/lib/webgen/tag.rb +22 -18
  57. data/lib/webgen/tag/menu.rb +5 -5
  58. data/lib/webgen/test_helper.rb +18 -18
  59. data/lib/webgen/utils/external_command.rb +1 -1
  60. data/lib/webgen/utils/tag_parser.rb +1 -1
  61. data/lib/webgen/vendor/rainpress.rb +168 -0
  62. data/lib/webgen/version.rb +1 -1
  63. data/lib/webgen/website.rb +10 -10
  64. data/man/man1/webgen.1 +54 -23
  65. data/test/test_documentation.rb +27 -4
  66. data/test/webgen/cli/test_logger.rb +1 -1
  67. data/test/webgen/content_processor/test_blocks.rb +1 -1
  68. data/test/webgen/content_processor/test_builder.rb +3 -2
  69. data/test/webgen/content_processor/test_erb.rb +1 -1
  70. data/test/webgen/content_processor/test_erubis.rb +2 -2
  71. data/test/webgen/content_processor/test_fragments.rb +1 -1
  72. data/test/webgen/content_processor/test_haml.rb +2 -2
  73. data/test/webgen/content_processor/test_html_head.rb +5 -1
  74. data/test/webgen/content_processor/test_kramdown.rb +2 -2
  75. data/test/webgen/content_processor/test_maruku.rb +2 -2
  76. data/test/webgen/content_processor/test_r_discount.rb +2 -2
  77. data/test/webgen/content_processor/test_r_doc.rb +1 -1
  78. data/test/webgen/content_processor/test_rainpress.rb +19 -0
  79. data/test/webgen/content_processor/test_red_cloth.rb +2 -2
  80. data/test/webgen/content_processor/test_ruby.rb +1 -1
  81. data/test/webgen/content_processor/test_sass.rb +8 -6
  82. data/test/webgen/content_processor/test_scss.rb +3 -3
  83. data/test/webgen/content_processor/test_tags.rb +1 -1
  84. data/test/webgen/content_processor/test_tidy.rb +9 -1
  85. data/test/webgen/content_processor/test_tikz.rb +6 -3
  86. data/test/webgen/content_processor/test_xmllint.rb +9 -2
  87. data/test/webgen/destination/test_file_system.rb +4 -4
  88. data/test/webgen/item_tracker/test_file.rb +1 -1
  89. data/test/webgen/item_tracker/test_missing_node.rb +1 -1
  90. data/test/webgen/item_tracker/test_node_content.rb +1 -1
  91. data/test/webgen/item_tracker/test_node_meta_info.rb +1 -1
  92. data/test/webgen/item_tracker/test_nodes.rb +2 -4
  93. data/test/webgen/item_tracker/test_template_chain.rb +36 -0
  94. data/test/webgen/misc/test_dummy_index.rb +83 -0
  95. data/test/webgen/path_handler/test_api.rb +6 -46
  96. data/test/webgen/path_handler/test_base.rb +3 -2
  97. data/test/webgen/path_handler/test_copy.rb +1 -1
  98. data/test/webgen/path_handler/test_feed.rb +3 -4
  99. data/test/webgen/path_handler/test_meta_info.rb +1 -1
  100. data/test/webgen/path_handler/test_page.rb +1 -1
  101. data/test/webgen/path_handler/test_page_utils.rb +1 -1
  102. data/test/webgen/path_handler/test_sitemap.rb +3 -5
  103. data/test/webgen/path_handler/test_template.rb +1 -1
  104. data/test/webgen/path_handler/test_virtual.rb +1 -3
  105. data/test/webgen/source/test_file_system.rb +9 -4
  106. data/test/webgen/source/test_stacked.rb +1 -1
  107. data/test/webgen/source/test_tar_archive.rb +2 -2
  108. data/test/webgen/tag/test_breadcrumb_trail.rb +1 -1
  109. data/test/webgen/tag/test_coderay.rb +3 -2
  110. data/test/webgen/tag/test_date.rb +1 -1
  111. data/test/webgen/tag/test_execute_command.rb +1 -1
  112. data/test/webgen/tag/test_include_file.rb +1 -1
  113. data/test/webgen/tag/test_langbar.rb +1 -1
  114. data/test/webgen/tag/test_link.rb +1 -1
  115. data/test/webgen/tag/test_menu.rb +12 -30
  116. data/test/webgen/tag/test_meta_info.rb +1 -1
  117. data/test/webgen/tag/test_relocatable.rb +1 -1
  118. data/test/webgen/tag/test_tikz.rb +3 -2
  119. data/test/webgen/task/test_create_website.rb +2 -2
  120. data/test/webgen/test_blackboard.rb +11 -3
  121. data/test/webgen/test_bundle_loader.rb +26 -9
  122. data/test/webgen/test_cache.rb +1 -1
  123. data/test/webgen/test_cli.rb +2 -2
  124. data/test/webgen/test_configuration.rb +8 -9
  125. data/test/webgen/test_content_processor.rb +1 -1
  126. data/test/webgen/test_context.rb +1 -1
  127. data/test/webgen/test_core_ext.rb +1 -1
  128. data/test/webgen/test_destination.rb +1 -1
  129. data/test/webgen/test_error.rb +5 -5
  130. data/test/webgen/test_extension_manager.rb +1 -1
  131. data/test/webgen/test_item_tracker.rb +26 -5
  132. data/test/webgen/test_languages.rb +1 -1
  133. data/test/webgen/test_logger.rb +1 -1
  134. data/test/webgen/test_node.rb +1 -1
  135. data/test/webgen/test_node_finder.rb +2 -2
  136. data/test/webgen/test_page.rb +5 -5
  137. data/test/webgen/test_path.rb +8 -8
  138. data/test/webgen/test_rake_task.rb +1 -1
  139. data/test/webgen/test_source.rb +1 -1
  140. data/test/webgen/test_tag.rb +16 -24
  141. data/test/webgen/test_task.rb +1 -1
  142. data/test/webgen/test_tree.rb +1 -1
  143. data/test/webgen/test_utils.rb +1 -1
  144. data/test/webgen/test_website.rb +1 -1
  145. data/test/webgen/utils/test_tag_parser.rb +1 -1
  146. metadata +85 -105
  147. data/lib/webgen/cli/bundle_command.rb +0 -30
  148. data/lib/webgen/cli/generate_command.rb +0 -25
@@ -69,10 +69,9 @@ module Webgen
69
69
  process_data_array(context, :css_inline) do |content|
70
70
  result += "\n<style type=\"text/css\">/*<![CDATA[/*/\n#{content}\n/*]]>*/</style>"
71
71
  end
72
-
73
- (context.persistent[:cp_html_head][:meta] || {}).merge(context.content_node['meta'] || {}).each do |name, content|
74
- result += "\n<meta name=\"#{ERB::Util.h(name)}\" content=\"#{ERB::Util.h(content)}\" />"
75
- end
72
+ end
73
+ ((context.persistent[:cp_html_head] || {})[:meta] || {}).merge(context.content_node['meta'] || {}).each do |name, content|
74
+ result += "\n<meta name=\"#{ERB::Util.h(name)}\" content=\"#{ERB::Util.h(content)}\" />"
76
75
  end
77
76
  result
78
77
  end
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'webgen/content_processor'
4
+ require 'webgen/vendor/rainpress'
5
+
6
+ module Webgen
7
+ class ContentProcessor
8
+
9
+ # Minifies CSS files.
10
+ module Rainpress
11
+
12
+ # Process the content of +context+ with Rainpress (a CSS minifier).
13
+ def self.call(context)
14
+ context.content = ::Rainpress.compress(context.content, context.website.config['content_processor.rainpress.options'])
15
+ context
16
+ end
17
+
18
+ end
19
+
20
+ end
21
+ end
@@ -30,9 +30,8 @@ module Webgen
30
30
  class NodeTreeImporter < ::Sass::Importers::Base
31
31
 
32
32
  # Creates a new importer that imports files from the node tree relative to the given node alcn.
33
- def initialize(website, alcn)
34
- @website = website
35
- @alcn = alcn
33
+ def initialize(context)
34
+ @context = context
36
35
  end
37
36
 
38
37
  # @see Base#find_relative
@@ -42,11 +41,11 @@ module Webgen
42
41
 
43
42
  # @see Base#find
44
43
  def find(name, options)
45
- _find(@alcn, name, options)
44
+ _find(@context.ref_node.alcn, name, options)
46
45
  end
47
46
 
48
47
  def mtime(name, options) #:nodoc:
49
- node = resolve_node(@alcn, name)
48
+ node = resolve_node(@context.ref_node.alcn, name)
50
49
  node['modified_at'] if node
51
50
  end
52
51
 
@@ -55,7 +54,7 @@ module Webgen
55
54
  end
56
55
 
57
56
  def to_s #:nodoc:
58
- "webgen: #{@alcn}"
57
+ "webgen: #{@context.ref_node.alcn}"
59
58
  end
60
59
 
61
60
  #######
@@ -69,6 +68,7 @@ module Webgen
69
68
  node, syntax = resolve_node(base, name)
70
69
  return unless node
71
70
 
71
+ @context.website.ext.item_tracker.add(@context.dest_node, :node_content, node)
72
72
  options[:syntax] = syntax
73
73
  options[:filename] = node.alcn
74
74
  options[:importer] = self
@@ -80,7 +80,7 @@ module Webgen
80
80
  # Returns [node, syntax] if a node was found or nil otherwise
81
81
  def resolve_node(base, path)
82
82
  possible_filenames(path).each do |filename, syntax|
83
- node = @website.tree.resolve_node(Webgen::Path.append(base, filename), nil)
83
+ node = @context.website.tree.resolve_node(Webgen::Path.append(base, filename), nil)
84
84
  return [node, syntax] if node
85
85
  end
86
86
  nil
@@ -132,7 +132,7 @@ module Webgen
132
132
 
133
133
  def self.default_options(context) # :nodoc:
134
134
  opts = context.website.config['content_processor.sass.options']
135
- load_paths = context.website.ext.sass_load_paths + [NodeTreeImporter.new(context.website, '/')]
135
+ load_paths = context.website.ext.sass_load_paths + [NodeTreeImporter.new(context)]
136
136
  opts.merge({
137
137
  :filename => context.ref_node.alcn,
138
138
  :syntax => :sass,
@@ -23,7 +23,7 @@ module Webgen
23
23
  prepare_options(context)
24
24
  context.content = context.render_block(:name => 'content',
25
25
  :chain => [context.website.tree[context['content_processor.tikz.template']]])
26
- context.content = File.binread(compile(context))
26
+ context.content = File.binread(use_cache_or_compile(context))
27
27
  context
28
28
  end
29
29
 
@@ -38,48 +38,91 @@ module Webgen
38
38
  end
39
39
  private_class_method :prepare_options
40
40
 
41
+ # Checks whether a cached version exists and if it is usable. If not, the LaTeX document is
42
+ # compiled.
43
+ def self.use_cache_or_compile(context)
44
+ cwd = context.website.tmpdir('content_processor.tikz')
45
+ FileUtils.mkdir_p(cwd)
46
+
47
+ tex_file = File.join(cwd, context.dest_node.dest_path.tr('/', '_').sub(/\..*?$/, '.tex'))
48
+ basename = File.basename(tex_file, '.tex')
49
+ ext = File.extname(context.dest_node.dest_path)
50
+ image_file = tex_file.sub(/\.tex$/, ext)
51
+
52
+ if !cache_usable?(context, tex_file, image_file)
53
+ compile(context, cwd, tex_file, basename, ext)
54
+ save_cache(context, tex_file)
55
+ end
56
+
57
+ image_file
58
+ end
59
+ private_class_method :use_cache_or_compile
60
+
41
61
  # Compile the LaTeX document stored in the Context and convert the resulting PDF to the
42
62
  # correct output image format specified by context[:ext] (the extension needs to include the
43
63
  # dot).
44
64
  #
45
65
  # Returns the path to the created image.
46
- def self.compile(context)
47
- cwd = context.website.tmpdir('content_processor.tikz')
48
- tex_file = File.join(cwd, context.dest_node.dest_path.tr('/', '_').sub(/\..*?$/, '.tex'))
49
- FileUtils.mkdir_p(cwd)
50
- File.write(tex_file, context.content)
51
-
52
- file = File.basename(tex_file, '.tex')
53
- ext = File.extname(context.dest_node.dest_path)
66
+ def self.compile(context, cwd, tex_file, basename, ext)
54
67
  render_res, output_res = context['content_processor.tikz.resolution'].split(' ')
55
68
 
56
- execute("pdflatex -shell-escape -interaction=nonstopmode -halt-on-error #{file}.tex", cwd, context) do |status, stdout, stderr|
69
+ File.write(tex_file, context.content)
70
+ execute("pdflatex -shell-escape -interaction=nonstopmode -halt-on-error #{basename}.tex", cwd, context) do |status, stdout, stderr|
57
71
  errors = (stdout+stderr).scan(/^!(.*\n.*)/).join("\n")
58
72
  raise Webgen::RenderError.new("Error while parsing TikZ picture commands with PDFLaTeX: #{errors}",
59
73
  'content_processor.tikz', context.dest_node, context.ref_node)
60
74
  end
61
75
 
62
- execute("pdfcrop #{file}.pdf #{file}.pdf", cwd, context)
76
+ execute("pdfcrop #{basename}.pdf #{basename}.pdf", cwd, context)
63
77
 
64
78
  if context['content_processor.tikz.transparent'] && ext =~ /\.png/i
65
79
  cmd = "gs -dSAFER -dBATCH -dNOPAUSE -r#{render_res} -sDEVICE=pngalpha -dGraphicsAlphaBits=4 " +
66
- "-dTextAlphaBits=4 -sOutputFile=#{file}#{ext} #{file}.pdf"
80
+ "-dTextAlphaBits=4 -sOutputFile=#{basename}#{ext} #{basename}.pdf"
67
81
  else
68
- cmd = "convert -density #{render_res} #{file}.pdf #{file}#{ext}"
82
+ cmd = "convert -density #{render_res} #{basename}.pdf #{basename}#{ext}"
69
83
  end
70
84
  execute(cmd, cwd, context)
71
85
 
72
86
  if render_res != output_res
73
- status, stdout, stderr = execute("identify #{file}#{ext}", cwd, context)
87
+ status, stdout, stderr = execute("identify #{basename}#{ext}", cwd, context)
74
88
  width, height = stdout.scan(/\s\d+x\d+\s/).first.strip.split('x').collect do |s|
75
89
  s.to_f * output_res.to_f / render_res.to_f
76
90
  end
77
- execute("convert -resize #{width}x#{height} #{file}#{ext} #{file}#{ext}", cwd, context)
91
+ execute("convert -resize #{width}x#{height} #{basename}#{ext} #{basename}#{ext}", cwd, context)
78
92
  end
79
- File.join(cwd, file + ext)
80
93
  end
81
94
  private_class_method :compile
82
95
 
96
+ # Save cache data so that it is possible to use it the next time.
97
+ def self.save_cache(context, tex_file)
98
+ File.write(cache_file(tex_file), cache_data(context))
99
+ end
100
+ private_class_method :save_cache
101
+
102
+ # Check if the content of the LaTeX document or the used options have changed.
103
+ def self.cache_usable?(context, tex_file, image_file)
104
+ cfile = cache_file(tex_file)
105
+ File.exist?(image_file) && File.exist?(cfile) && File.binread(cfile) == cache_data(context)
106
+ end
107
+ private_class_method :cache_usable?
108
+
109
+ # The data that should be written to the cache file.
110
+ def self.cache_data(context)
111
+ ("" << context.content <<
112
+ "\n" << context['content_processor.tikz.resolution'].to_s <<
113
+ "\n" << context['content_processor.tikz.transparent'].to_s <<
114
+ "\n" << context['content_processor.tikz.libraries'].to_s <<
115
+ "\n" << context['content_processor.tikz.opts'].to_s <<
116
+ "\n" << context['content_processor.tikz.template'].to_s).force_encoding('BINARY')
117
+ end
118
+ private_class_method :cache_data
119
+
120
+ # Return the name of the cache file for the give LaTeX file.
121
+ def self.cache_file(tex_file)
122
+ tex_file.sub(/\.tex$/, ".cache")
123
+ end
124
+ private_class_method :cache_file
125
+
83
126
  # Execute the command +cmd+ in the working directory +cwd+.
84
127
  #
85
128
  # If the exit status is not zero, yields to the given block if one is given, or raises an error
@@ -30,7 +30,9 @@ module Webgen
30
30
  # tracker extension
31
31
  #
32
32
  # [item_data(*item)]
33
- # Return the data for the item so that it can be correctly checked later if it has changed.
33
+ # Return the data for the item so that it can be correctly checked later if it has changed. If
34
+ # data for the given item cannot be computer anymore because the item got invalid, raise an
35
+ # exception.
34
36
  #
35
37
  # [item_changed?(item_id, old_data)]
36
38
  # Return +true+ if the item identified by its unique ID has changed. The parameter +old_data+
@@ -106,42 +108,60 @@ module Webgen
106
108
 
107
109
  @item_changed = {}
108
110
 
109
- @website.blackboard.add_listener(:website_initialized, self) do
111
+ @website.blackboard.add_listener(:website_initialized, 'item_tracker') do
110
112
  @cached = @website.cache[:item_tracker_data] || @cached
111
113
  end
112
114
 
113
- @website.blackboard.add_listener(:after_tree_populated, self) do |node|
115
+ @website.blackboard.add_listener(:after_tree_populated, 'item_tracker') do |node|
114
116
  @item_data.keys.each do |uid|
115
117
  @item_data[uid] = item_tracker(uid.first).item_data(*uid.last)
116
118
  end
117
119
  end
118
120
 
119
- @website.blackboard.add_listener(:before_all_nodes_written, self) do
120
- # make all used item data from the previous pass current again if applicable
121
- @written_nodes.each do |node|
121
+ @website.blackboard.add_listener(:before_all_nodes_written, 'item_tracker') do
122
+ # make all used item data from the previous pass current again if applicable and remove
123
+ # invalid UIDs
124
+ uids_to_update = @written_nodes.each_with_object(Set.new) do |node, set|
122
125
  next unless @website.tree[node.alcn]
123
- @node_dependencies[node.alcn].each do |uid|
126
+ set.merge(@node_dependencies[node.alcn])
127
+ end
128
+ uids_to_update.each do |uid|
129
+ begin
124
130
  @item_data[uid] = item_tracker(uid.first).item_data(*uid.last)
131
+ rescue Exception
132
+ @item_data.delete(uid)
133
+ @cached[:item_data].delete(uid)
134
+ @written_nodes.each do |node|
135
+ if @node_dependencies[node.alcn].include?(uid)
136
+ @node_dependencies[node.alcn].delete(uid)
137
+ @cached[:node_dependencies][node.alcn].delete(uid)
138
+ node.node_info[:item_tracker_changed_once] = true
139
+ end
140
+ end
125
141
  end
126
142
  end
143
+
127
144
  @written_nodes = []
128
145
  @item_changed = {}
129
146
  end
130
147
 
131
- @website.blackboard.add_listener(:after_node_written, self) do |node|
148
+ @website.blackboard.add_listener(:after_node_written, 'item_tracker') do |node|
132
149
  @written_nodes << node
133
150
  end
134
151
 
135
- @website.blackboard.add_listener(:after_all_nodes_written, self) do
152
+ @website.blackboard.add_listener(:after_all_nodes_written, 'item_tracker') do
136
153
  # update cached data with data from the run
154
+ uids_to_update = Set.new
137
155
  @written_nodes.each do |node|
138
156
  next unless @website.tree[node.alcn]
157
+ node.node_info.delete(:item_tracker_changed_once)
139
158
  @cached[:node_dependencies][node.alcn] = @node_dependencies[node.alcn]
140
- @node_dependencies[node.alcn].each {|uid| @cached[:item_data][uid] = @item_data[uid]}
159
+ uids_to_update.merge(@node_dependencies[node.alcn])
141
160
  end
161
+ uids_to_update.each {|uid| @cached[:item_data][uid] = @item_data[uid]}
142
162
  end
143
163
 
144
- @website.blackboard.add_listener(:website_generated, self) do
164
+ @website.blackboard.add_listener(:website_generated, 'item_tracker') do
145
165
  @cached[:node_dependencies].reject! {|alcn, data| !@website.tree[alcn]}
146
166
 
147
167
  used_uids = @cached[:node_dependencies].each_with_object(Set.new) {|(_, uids), obj| obj.merge(uids)}
@@ -189,7 +209,8 @@ module Webgen
189
209
  def node_changed?(node)
190
210
  return false if @checked_nodes.include?(node)
191
211
  @checked_nodes << node
192
- !@cached[:node_dependencies].has_key?(node.alcn) ||
212
+ node.node_info[:item_tracker_changed_once] ||
213
+ !@cached[:node_dependencies].has_key?(node.alcn) ||
193
214
  @cached[:node_dependencies][node.alcn].any? {|uid| item_changed_by_uid?(uid)}
194
215
  ensure
195
216
  @checked_nodes.delete(node)
@@ -23,13 +23,13 @@ module Webgen
23
23
  @stop_reporting = false
24
24
  @nodes_to_ignore = Set.new
25
25
 
26
- @website.blackboard.add_listener(:reused_existing_node, self) do |node, path|
26
+ @website.blackboard.add_listener(:reused_existing_node, 'item_tracker.missing_node') do |node, path|
27
27
  @nodes_to_ignore << node
28
28
  end
29
- @website.blackboard.add_listener(:after_node_created, self) do |node|
29
+ @website.blackboard.add_listener(:after_node_created, 'item_tracker.missing_node') do |node|
30
30
  @at_least_one_node_created = true unless @nodes_to_ignore.include?(node)
31
31
  end
32
- @website.blackboard.add_listener(:after_all_nodes_written, self) do
32
+ @website.blackboard.add_listener(:after_all_nodes_written, 'item_tracker.missing_node') do
33
33
  if @at_least_one_node_created
34
34
  @at_least_one_node_created = false
35
35
  else
@@ -37,7 +37,7 @@ module Webgen
37
37
  end
38
38
  @nodes_to_ignore = Set.new
39
39
  end
40
- @website.blackboard.add_listener(:website_generated, self) do
40
+ @website.blackboard.add_listener(:website_generated, 'item_tracker.missing_node') do
41
41
  @at_least_one_node_created = true
42
42
  @stop_reporting = false
43
43
  @nodes_to_ignore = Set.new
@@ -63,7 +63,7 @@ module Webgen
63
63
  end
64
64
 
65
65
  def item_description(iid, data) #:nodoc:
66
- path, lang = *iid.last
66
+ path, lang = iid
67
67
  "Missing acn, alcn or dest path <#{path}>" << (lang.nil? ? '' : " in language '#{lang}'")
68
68
  end
69
69
 
@@ -0,0 +1,52 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'webgen/item_tracker'
4
+
5
+ module Webgen
6
+ class ItemTracker
7
+
8
+ # This class is used to track the template chain for a node.
9
+ #
10
+ # Note that only nodes that support the #template_chain method can be used (so all page,
11
+ # template and custom webgen nodes are okay).
12
+ #
13
+ # The item for this tracker is the node whose template chain should be tracked, i.e. add an item
14
+ # like this:
15
+ #
16
+ # website.ext.item_tracker.add(some_node, :template_chain, other_node)
17
+ #
18
+ class TemplateChain
19
+
20
+ def initialize(website) #:nodoc:
21
+ @website = website
22
+ end
23
+
24
+ def item_id(node) #:nodoc:
25
+ node.alcn
26
+ end
27
+
28
+ def item_data(alcn) #:nodoc:
29
+ nodes_to_alcn(@website.tree[alcn].template_chain)
30
+ end
31
+
32
+ def item_changed?(alcn, old_chain) #:nodoc:
33
+ @website.tree[alcn].nil? || item_data(alcn) != old_chain
34
+ end
35
+
36
+ def referenced_nodes(alcn, data) #:nodoc:
37
+ [alcn]
38
+ end
39
+
40
+ def item_description(alcn, data) #:nodoc:
41
+ "Template chain for node '#{alcn}'"
42
+ end
43
+
44
+ def nodes_to_alcn(nodes) #:nodoc:
45
+ nodes.map {|node| node.alcn}
46
+ end
47
+ private :nodes_to_alcn
48
+
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,78 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'yaml'
4
+ require 'webgen/error'
5
+
6
+ module Webgen
7
+
8
+ module Misc
9
+
10
+ # This extension creates dummy directory index paths for directories where the proxy_path meta
11
+ # information does not point to a node whose lcn matches a directory index path name.
12
+ class DummyIndex
13
+
14
+ def initialize(website) #:nodoc:
15
+ @website = website
16
+
17
+ @website.blackboard.add_listener(:website_initialized, 'misc.dummy_index') do
18
+ if @website.config['misc.dummy_index.enabled'] && @website.config['misc.dummy_index.directory_indexes'].length > 0
19
+ @website.blackboard.add_listener(:website_generated, 'misc.dummy_index', &method(:create_dummy_indexes))
20
+ end
21
+ end
22
+ end
23
+
24
+ # Create the dummy index paths at the destination.
25
+ def create_dummy_indexes
26
+ indexes = @website.config['misc.dummy_index.directory_indexes']
27
+ @website.tree.node_access[:alcn].each do |_, node|
28
+ next if !node.is_directory? || !directory_exists?(node) || directory_index_exists?(node, indexes)
29
+
30
+ route = node.route_to(node)
31
+ route = node['proxy_path'].to_s if route == File.basename(node.dest_path)
32
+ index_path = node.dest_path + indexes.first
33
+
34
+ next if route == '' || indexes.any? {|index| index == route} ||
35
+ (cache[node.alcn] == [indexes.first, route] && @website.ext.destination.exists?(index_path))
36
+
37
+ @website.logger.info do
38
+ "[#{@website.ext.destination.exists?(index_path) ? 'update' : 'create'}] <#{index_path}> (dummy directory path pointing to #{route})"
39
+ end
40
+ cache[node.alcn] = [indexes.first, route]
41
+ @website.ext.destination.write(index_path, dummy_index_content(route))
42
+ end
43
+ end
44
+ protected :create_dummy_indexes
45
+
46
+ # Does the node directory exist at the destination?
47
+ def directory_exists?(node)
48
+ @website.ext.destination.exists?(node.dest_path)
49
+ end
50
+ protected :directory_exists?
51
+
52
+ # Is there any node with a destination path matching any of the directory index paths?
53
+ def directory_index_exists?(node, indexes) #:nodoc:
54
+ indexes.any? {|index| @website.tree.node(node.dest_path + index, :dest_path)}
55
+ end
56
+ protected :directory_index_exists?
57
+
58
+ # Return the dummy index path content for redirecting to +url+.
59
+ def dummy_index_content(url)
60
+ <<EOF
61
+ <!DOCTYPE html><html><head><title>Redirect</title><meta charset="UTF-8" />
62
+ <meta http-equiv="Refresh" content="0; url=#{url}" />
63
+ </head><body></body></html>
64
+ EOF
65
+ end
66
+ protected :dummy_index_content
67
+
68
+ # Return the cache used by this extension.
69
+ def cache
70
+ @website.cache['misc.dummy_index.data'] ||= {}
71
+ end
72
+ protected :cache
73
+
74
+ end
75
+
76
+ end
77
+
78
+ end