webgen 1.0.0.beta3 → 1.0.0

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