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
@@ -216,7 +216,7 @@ module Webgen
216
216
  # Return all versions of this node.
217
217
  def versions
218
218
  tree.node_access[:alcn].select {|alcn, n| n.node_info[:path] == node_info[:path]}.
219
- each_with_object({}) {|(k, v), h| h[v['version']] = v}
219
+ each_with_object({}) {|(_, v), h| h[v['version']] = v}
220
220
  end
221
221
 
222
222
  end
@@ -21,116 +21,22 @@ module Webgen
21
21
  #
22
22
  # == Finder options
23
23
  #
24
- # Following is the list of all finder options. Note that there may also be other 3rd party node
25
- # filters available if you are using extension bundles!
26
- #
27
- # === Non-filter options
28
- #
29
- # These options are not used for filtering out nodes but provide additional functionality.
30
- #
31
- # [:limit]
32
- # Value: an integer. Specifies the maximum number of nodes that should be returned. Implies
33
- # 'flatten = true'.
34
- #
35
- # Note that fewer nodes may be returned if fewer nodes match the filter criterias.
36
- #
37
- # [:offset]
38
- # Value: an integer. Specifies how many nodes from the front of the list should *not* be
39
- # returned. Implies 'flatten = true'.
40
- #
41
- # [:levels]
42
- # Value: one integer (is used as start and end level) or an array with two integers (the start
43
- # and end levels). All nodes whose hierarchy level in the returned node hierarchy is greater
44
- # than or equal to the start level and lower than or equal to the end level are used.
45
- #
46
- # Only used when the node hierarchy is not flattened.
47
- #
48
- # [:flatten]
49
- # Value: anything except +nil+ or +false+. A flat list of nodes is returned if this option is
50
- # set, otherwise the nodes are returned in their correct hierarchical order using nested lists.
51
- #
52
- # Note that any missing nodes in the hierarchy are automatically added so that traversing the
53
- # hierarchy is always possible. For example, if we have the tree '/a/b/c' and only nodes +a+ and
54
- # +c+ are found, node +b+ is automatically added.
55
- #
56
- # [:sort]
57
- # Value: +nil+/+false+, +true+ or a meta information key. If +nil+ or +false+ is specified, no
58
- # sorting is performed. If +true+ is specified, the meta information +sort_info+ (or if absent,
59
- # the meta information +title+) is used for sorting. If the compared values are both integers, a
60
- # numeric comparison is done, else a string comparison. If a meta information key is specified,
61
- # the value of this meta information is used for comparison of nodes (again, if both compared
62
- # values are integers, a numeric comparison is done, else a string comparison).
63
- #
64
- # [:reverse]
65
- # Value: +true+ of +false+/+nil+. If this option is set to +true+, the sort order is reversed.
66
- #
67
- # === Filter options
68
- #
69
- # These options are used for filtering the nodes. All nodes are used by default if no filter
70
- # options are specified.
71
- #
72
- # [:alcn]
73
- # Value: an alcn pattern or an array of alcn patterns. Nodes that match any of the patterns are
74
- # used.
75
- #
76
- # [:lang]
77
- # Value: a language code/+nil+/the special value +node+ or an array of these values. Nodes that
78
- # have one of the specified language codes, are language independent (in case of the value
79
- # +nil+) or have the same language as the reference node (in case of the value +node+) are
80
- # used.
81
- #
82
- # [:mi]
83
- # Value: a hash with meta information key to value mappings. Only nodes that have the same
84
- # values for all meta information keys are used.
85
- #
86
- # [:or]
87
- # Value: a finder option set or an array of finder options sets (specifying option set names is
88
- # also possible). Nodes that appear in any specified option set are additionally used.
89
- #
90
- # [:and]
91
- # Value: a finder option set or an array of finder options sets (specifying option set names is
92
- # also possible). Only nodes that appear in all specified option sets are used.
93
- #
94
- # [:not]
95
- # Value: a finder option set or an array of finder options sets (specifying option set names is
96
- # also possible). Only nodes that do not appear in any specified option set are used.
97
- #
98
- # [:absolute_levels]
99
- # Value: one integer (is used as start and end level) or an array with two integers (the start
100
- # and end levels). All nodes whose hierarchy level in the node tree are greater than or equal to
101
- # the start level and lower than or equal to the end level are used.
102
- #
103
- # Negative numbers, where -1 stands for the reference node, -2 for its parent node and so on,
104
- # can also be used.
105
- #
106
- # [:ancestors]
107
- # Value: +true+ or +false+/+nil+. If this filter option is set to +true+, only nodes that are
108
- # ancestors of the reference node are used. The reference node itself is used as well.
109
- #
110
- # [:descendants]
111
- # Value: +true+ or +false+/+nil+. If this filter option is set to +true+, only nodes that are
112
- # descendants of the reference node are used. The reference node itself is used as well.
113
- #
114
- # [:siblings]
115
- # Value: +true+, +false+/+nil+, or an array with two integers. If this filter option is set to
116
- # +true+, only nodes that are sibling node of the reference node are used. The reference node
117
- # itself is used as well. If set to +false+ or +nil+, this filter is ignored.
118
- #
119
- # If an array with two numbers is specified, all sibling nodes of the reference node or its
120
- # parent nodes the hierarchy level of which lies between these numbers are used. The parent
121
- # nodes and the reference node are used as well if their level lies between the numbers.
122
- # Counting starts at zero (the root node).
123
- #
124
- # Negative numbers, where -1 stands for the reference node, -2 for its parent node and so on,
125
- # can also be used.
24
+ # A complete list of the supported finder options can be found in the user documentation! Note
25
+ # that there may also be other 3rd party node filters available if you are using extension
26
+ # bundles!
126
27
  #
127
28
  # == Implementing a filter module
128
29
  #
129
30
  # Implementing a filter module is very easy. Just create a module that contains your filter
130
31
  # methods and tell the NodeFinder object about it using the #add_filter_module method. A filter
131
- # method needs to take three arguments: an array of nodes, the reference node and the filter
32
+ # method needs to take three arguments: the Result stucture, the reference node and the filter
132
33
  # value.
133
34
  #
35
+ # The +result.nodes+ accessor contains the array of nodes that should be manipulated in-place.
36
+ #
37
+ # If a filter uses the reference node in any way, it has to set +result.ref_node_used+ to +true+
38
+ # to allow proper caching!
39
+ #
134
40
  # Here is a sample filter module which provides the ability to filter nodes based on the meta
135
41
  # information key +category+. The +category+ key contains an array with one or more categories.
136
42
  # The value for this category filter is one or more strings and the filter returns those nodes
@@ -138,9 +44,9 @@ module Webgen
138
44
  #
139
45
  # module CategoryFilter
140
46
  #
141
- # def filter_on_category(nodes, ref_node, categories)
47
+ # def filter_on_category(result, ref_node, categories)
142
48
  # categories = [categories].flatten # needed in case categories is a string
143
- # nodes.select {|n| categories.any? {|c| n['category'].include?(c)}}
49
+ # result.nodes.select! {|n| categories.any? {|c| n['category'].include?(c)}}
144
50
  # end
145
51
  #
146
52
  # end
@@ -149,6 +55,11 @@ module Webgen
149
55
  #
150
56
  class NodeFinder
151
57
 
58
+ # Result class used when filtering the nodes.
59
+ #
60
+ # The attribute +ref_node_used+ must not be set to +false+ once it is +true+!
61
+ Result = Struct.new(:nodes, :ref_node_used)
62
+
152
63
  # Create a new NodeFinder object for the given website.
153
64
  def initialize(website)
154
65
  @website = website
@@ -196,7 +107,7 @@ module Webgen
196
107
  flatten = true if limit || offset
197
108
  levels = [levels || [1, 1_000_000]].flatten.map {|i| i.to_i}
198
109
 
199
- nodes = filter_nodes(opts, ref_node)
110
+ nodes, ref_node_used = filter_nodes(opts, ref_node)
200
111
 
201
112
  if flatten
202
113
  sort_nodes(nodes, sort, reverse)
@@ -226,7 +137,7 @@ module Webgen
226
137
  sort_nodes(nodes, sort, reverse, false)
227
138
  end
228
139
 
229
- cache_result(opts_or_name, ref_node, nodes)
140
+ cache_result(opts_or_name, ref_node, nodes, ref_node_used)
230
141
  end
231
142
 
232
143
  #######
@@ -234,11 +145,19 @@ module Webgen
234
145
  #######
235
146
 
236
147
  def cached_result(opts, ref_node)
237
- (@website.cache.volatile[:node_finder] ||= {})[[opts, ref_node.alcn]]
148
+ result_cache[opts] || result_cache[[opts, ref_node.alcn]]
238
149
  end
239
150
 
240
- def cache_result(opts, ref_node, result)
241
- (@website.cache.volatile[:node_finder] ||= {})[[opts, ref_node.alcn]] = result
151
+ def cache_result(opts, ref_node, result, ref_node_used)
152
+ if ref_node_used
153
+ result_cache[[opts, ref_node.alcn]] = result
154
+ else
155
+ result_cache[opts] = result
156
+ end
157
+ end
158
+
159
+ def result_cache
160
+ @website.cache.volatile[:node_finder] ||= {}
242
161
  end
243
162
 
244
163
  def prepare_options_hash(opts_or_name)
@@ -260,15 +179,17 @@ module Webgen
260
179
  nodes = @website.tree.node_access[:alcn].values
261
180
  nodes.delete(@website.tree.dummy_root)
262
181
 
182
+ result = Result.new(nodes, false)
183
+
263
184
  opts.each do |filter, value|
264
185
  if @mapping.has_key?(filter)
265
- nodes = send(@mapping[filter], nodes, ref_node, value)
186
+ send(@mapping[filter], result, ref_node, value)
266
187
  else
267
188
  @website.logger.warn { "Ignoring unknown node finder filter '#{filter}'" }
268
189
  end
269
190
  end
270
191
 
271
- nodes
192
+ [result.nodes, result.ref_node_used]
272
193
  end
273
194
 
274
195
  def sort_nodes(nodes, sort, reverse, flat_mode = true)
@@ -292,78 +213,102 @@ module Webgen
292
213
 
293
214
  # :section: Filter methods
294
215
 
295
- def filter_and(nodes, ref_node, opts)
216
+ def filter_and(result, ref_node, opts)
296
217
  [opts].flatten.each do |cur_opts|
297
218
  cur_opts = prepare_options_hash(cur_opts)
298
219
  remove_non_filter_options(cur_opts)
299
- nodes &= filter_nodes(cur_opts, ref_node)
220
+ nodes, ref_node_used = filter_nodes(cur_opts, ref_node)
221
+ result.nodes &= nodes
222
+ result.ref_node_used |= ref_node_used
300
223
  end
301
- nodes
302
224
  end
303
225
 
304
- def filter_or(nodes, ref_node, opts)
226
+ def filter_or(result, ref_node, opts)
305
227
  [opts].flatten.each do |cur_opts|
306
228
  cur_opts = prepare_options_hash(cur_opts)
307
229
  remove_non_filter_options(cur_opts)
308
- nodes |= filter_nodes(cur_opts, ref_node)
230
+ nodes, ref_node_used = filter_nodes(cur_opts, ref_node)
231
+ result.nodes |= nodes
232
+ result.ref_node_used |= ref_node_used
309
233
  end
310
- nodes
311
234
  end
312
235
 
313
- def filter_not(nodes, ref_node, opts)
236
+ def filter_not(result, ref_node, opts)
314
237
  [opts].flatten.each do |cur_opts|
315
238
  cur_opts = prepare_options_hash(cur_opts)
316
239
  remove_non_filter_options(cur_opts)
317
- nodes -= filter_nodes(cur_opts, ref_node)
240
+ nodes, ref_node_used = filter_nodes(cur_opts, ref_node)
241
+ result.nodes -= nodes
242
+ result.ref_node_used |= ref_node_used
318
243
  end
319
- nodes
320
244
  end
321
245
 
322
- def filter_meta_info(nodes, ref_node, mi)
323
- nodes.keep_if {|n| mi.all? {|key, val| n[key] == val}}
246
+ def filter_meta_info(result, ref_node, mi)
247
+ result.nodes.keep_if {|n| mi.all? {|key, val| n[key] == val}}
324
248
  end
325
249
 
326
- def filter_alcn(nodes, ref_node, alcn)
250
+ def filter_alcn(result, ref_node, alcn)
251
+ result.ref_node_used = true
327
252
  alcn = [alcn].flatten.map {|a| Webgen::Path.append(ref_node.alcn, a.to_s)}
328
- nodes.keep_if {|n| alcn.any? {|a| n =~ a}}
253
+ result.nodes.keep_if {|n| alcn.any? {|a| n =~ a}}
329
254
  end
330
255
 
331
- def filter_absolute_levels(nodes, ref_node, range)
332
- range = [range].flatten.map {|i| (i = i.to_i) < 0 ? ref_node.level + 1 + i : i}
333
- nodes.keep_if {|n| n.level >= range.first && n.level <= range.last}
256
+ def filter_absolute_levels(result, ref_node, range)
257
+ range = [range].flatten.map do |i|
258
+ if (i = i.to_i) < 0
259
+ result.ref_node_used = true
260
+ ref_node.level + 1 + i
261
+ else
262
+ i
263
+ end
264
+ end
265
+ result.nodes.keep_if {|n| n.level >= range.first && n.level <= range.last}
334
266
  end
335
267
 
336
- def filter_lang(nodes, ref_node, langs)
337
- langs = [langs].flatten.map {|l| l == 'node' ? ref_node.lang : l}.uniq
338
- nodes.keep_if {|n| langs.any? {|l| n.lang == l}}
268
+ def filter_lang(result, ref_node, langs)
269
+ langs = [langs].flatten.map do |l|
270
+ if l == 'node'
271
+ result.ref_node_used = true
272
+ ref_node.lang
273
+ else
274
+ l
275
+ end
276
+ end.uniq
277
+ result.nodes.keep_if {|n| langs.any? {|l| n.lang == l}}
339
278
  end
340
279
 
341
- def filter_ancestors(nodes, ref_node, enabled)
342
- return nodes unless enabled
280
+ def filter_ancestors(result, ref_node, enabled)
281
+ return unless enabled
282
+ result.ref_node_used = true
283
+
343
284
  nodes = []
344
285
  node = ref_node
345
286
  until node == node.tree.dummy_root
346
287
  nodes.unshift(node)
347
288
  node = node.parent
348
289
  end
349
- nodes
290
+ result.nodes = nodes
350
291
  end
351
292
 
352
- def filter_descendants(nodes, ref_node, enabled)
353
- return nodes unless enabled
354
- nodes.keep_if do |n|
293
+ def filter_descendants(result, ref_node, enabled)
294
+ return unless enabled
295
+ result.ref_node_used = true
296
+
297
+ result.nodes.keep_if do |n|
355
298
  n = n.parent while n != n.tree.dummy_root && n != ref_node
356
299
  n == ref_node
357
300
  end
358
301
  end
359
302
 
360
- def filter_siblings(nodes, ref_node, value)
361
- return nodes unless value
303
+ def filter_siblings(result, ref_node, value)
304
+ return unless value
305
+ result.ref_node_used = true
306
+
362
307
  if value == true
363
- nodes.keep_if {|n| n.parent == ref_node.parent}
308
+ result.nodes.keep_if {|n| n.parent == ref_node.parent}
364
309
  else
365
310
  value = [value].flatten.map {|i| (i = i.to_i) < 0 ? ref_node.level + 1 + i : i}
366
- nodes.keep_if do |n|
311
+ result.nodes.keep_if do |n|
367
312
  n.level >= value.first && n.level <= value.last && (n.parent.is_ancestor_of?(ref_node) || n.is_root?)
368
313
  end
369
314
  end
@@ -18,11 +18,11 @@ module Webgen
18
18
 
19
19
  # :stopdoc:
20
20
  RE_NEWLINE = /\r?\n/
21
- RE_META_INFO_START = /\A---\s*#{RE_NEWLINE}/
21
+ RE_META_INFO_START = /\A---[ \t]*#{RE_NEWLINE}/
22
22
  RE_META_INFO = /#{RE_META_INFO_START}.*?#{RE_NEWLINE}(?=---.*?#{RE_NEWLINE}|\Z)/m
23
- RE_BLOCKS_START_SIMPLE = /^--- (\w+)(?:\s*| -+\s*)$|^$/
24
- RE_BLOCKS_START_COMPLEX = /^--- *?(?: *((?:\w+:[^\s]* *)*))?-*\s*$|^$/
25
- RE_BLOCKS_START = /^---(?: .*?|)(?=#{RE_NEWLINE})/
23
+ RE_BLOCKS_START_SIMPLE = /^---[ \t]*$|^---[ \t]+(\w+)[ \t]*(?:[ \t]+-+[ \t]*)?$|^$/
24
+ RE_BLOCKS_START_COMPLEX = /^---[ \t]+?(?:[ \t]*((?:\w+:\S*[ \t]*)*))?(?:[ \t]+-+[ \t]*)?$/
25
+ RE_BLOCKS_START = /^---(?:[ \t]+.*?|)(?=#{RE_NEWLINE})/
26
26
  RE_BLOCKS = /(?:(#{RE_BLOCKS_START})|\A)#{RE_NEWLINE}?(.*?)(?:(?=#{RE_BLOCKS_START})|\z)/m
27
27
  RE_PAGE = /(#{RE_META_INFO})?(.*)/m
28
28
  # :startdoc:
@@ -53,7 +53,7 @@ module Webgen
53
53
  unless meta_info.kind_of?(Hash)
54
54
  raise FormatError, "Invalid structure of meta information block: expected YAML hash but found #{meta_info.class}"
55
55
  end
56
- rescue ArgumentError, SyntaxError => e
56
+ rescue ArgumentError, SyntaxError, YAML::SyntaxError => e
57
57
  raise FormatError, "Invalid YAML syntax in meta information block: #{e.message}"
58
58
  end
59
59
  meta_info
@@ -64,11 +64,14 @@ module Webgen
64
64
 
65
65
  # Return +true+ if the given path string matches the given path pattern.
66
66
  #
67
+ # If a fragment path (i.e. one which has a hash character somewhere) should be matched, the
68
+ # pattern needs to have a hash character as well.
69
+ #
67
70
  # For information on which patterns are supported, have a look at the API documentation of
68
71
  # File.fnmatch.
69
72
  def self.matches_pattern?(path, pattern, options = File::FNM_DOTMATCH|File::FNM_CASEFOLD|File::FNM_PATHNAME)
70
73
  pattern += '/' if path =~ /\/$/ && pattern !~ /\/$|^$/
71
- File.fnmatch(pattern, path, options)
74
+ (path.to_s.include?('#') ? pattern.include?('#') : true) && File.fnmatch(pattern, path, options)
72
75
  end
73
76
 
74
77
  # Construct a localized canonical name from a given canonical name and a language.
@@ -17,14 +17,10 @@ module Webgen
17
17
  # source to the destination, or a complex things, like generating a whole set of nodes from one
18
18
  # input path (e.g. generating a whole image gallery)!
19
19
  #
20
- # The paths that are handled by a path handler are specified via path patterns (see below). The
21
- # #create_nodes method of a path handler is called for each source path that matches a specified
22
- # path pattern. And when it is time to write out a node, the #content method on the associated
23
- # path handler is called to retrieve the rendered content of the node.
24
- #
25
- # Also note that any method invoked on a Node object that is not defined in the Node class itself
26
- # is forwarded to the associated path handler, adding the node as first parameter to the parameter
27
- # list.
20
+ # The paths that are handled by a path handler are generally specified via path patterns. The
21
+ # #create_nodes method of a path handler is called for each source path that should be handled.
22
+ # And when it is time to write out a node, the #content method on the path handler associated with
23
+ # the node is called to retrieve the rendered content of the node.
28
24
  #
29
25
  # === Tree creation
30
26
  #
@@ -40,10 +36,11 @@ module Webgen
40
36
  # created before their file nodes.
41
37
  #
42
38
  # 2. When a path handler is used for creating nodes, all source paths (retrieved by using
43
- # Webgen::Source#paths method) that match one of the associated patterns are used.
39
+ # Webgen::Source#paths method) that match one of the associated patterns and/or all path with
40
+ # the 'handler' meta information set to the path handler are used.
44
41
  #
45
- # 3. The meta information of a used source path is then updated with the meta information from the
46
- # 'path_handler.default_meta_info' configuration option key.
42
+ # 3. The meta information of a used source path is then updated with the meta information applied
43
+ # by methods registered for the :apply_meta_info_to_path blackboard message.
47
44
  #
48
45
  # After that the source path is given to the #parse_meta_info! method of the path handler so
49
46
  # that meta information of the path can be updated with meta information stored in the content
@@ -53,7 +50,7 @@ module Webgen
53
50
  # should be used for creating nodes and each path version is then given to the #create_nodes
54
51
  # method of the path handler so that it can create one or more nodes.
55
52
  #
56
- # 4. Nodes returned by the #creates_nodes of a path handler are assumed to have the Node#node_info
53
+ # 4. Nodes returned by #creates_nodes of a path handler are assumed to have the Node#node_info
57
54
  # keys :path and :path_handler and the meta info key 'modified_at' correctly set (this is
58
55
  # automatically done if the Webgen::PathHandler::Base#create_node method is used).
59
56
  #
@@ -61,7 +58,8 @@ module Webgen
61
58
  #
62
59
  # Path patterns define which paths are handled by a specific path handler. These patterns are
63
60
  # specified when a path handler is registered using #register method. The patterns need to have a
64
- # format that Dir.glob can handle.
61
+ # format that Dir.glob can handle. Note that a user can always associate any path with a path
62
+ # handler through a meta information path and the 'handler' meta information key.
65
63
  #
66
64
  # In addition to specifying the patterns a path handler uses, one can also specify the place in
67
65
  # the invocation list which the path handler should use. The invocation list is used from the
@@ -141,23 +139,23 @@ module Webgen
141
139
  @instances = {}
142
140
  @secondary_nodes = {}
143
141
 
144
- @website.blackboard.add_listener(:website_generated, self) do
142
+ @website.blackboard.add_listener(:website_generated, 'path_handler') do
145
143
  @website.cache[:path_handler_secondary_nodes] = @secondary_nodes
146
144
  end
147
145
 
148
146
  used_secondary_paths = {}
149
147
  written_nodes = Set.new
150
- @website.blackboard.add_listener(:before_secondary_nodes_created, self) do |path, source_alcn|
148
+ @website.blackboard.add_listener(:before_secondary_nodes_created, 'path_handler') do |path, source_alcn|
151
149
  (used_secondary_paths[source_alcn] ||= Set.new) << path if source_alcn
152
150
  end
153
- @website.blackboard.add_listener(:before_all_nodes_written, self) do |node|
151
+ @website.blackboard.add_listener(:before_all_nodes_written, 'path_handler') do |node|
154
152
  used_secondary_paths = {}
155
153
  written_nodes = Set.new
156
154
  end
157
- @website.blackboard.add_listener(:after_node_written, self) do |node|
155
+ @website.blackboard.add_listener(:after_node_written, 'path_handler') do |node|
158
156
  written_nodes << node.alcn
159
157
  end
160
- @website.blackboard.add_listener(:after_all_nodes_written, self) do |node|
158
+ @website.blackboard.add_listener(:after_all_nodes_written, 'path_handler') do
161
159
  @secondary_nodes.delete_if do |path, data|
162
160
  if written_nodes.include?(data[1]) && (!used_secondary_paths[data[1]] ||
163
161
  !used_secondary_paths[data[1]].include?(path))
@@ -225,10 +223,16 @@ module Webgen
225
223
 
226
224
  time = Benchmark.measure do
227
225
  meta_info, rest = @website.ext.source.paths.partition {|path| path.path =~ /[\/.]metainfo$/}
226
+
227
+ used_paths = []
228
+
229
+ @website.blackboard.add_listener(:before_node_created, 'path_handler (temp_populate_tree)') do |path|
230
+ used_paths << path
231
+ end
228
232
  create_nodes(meta_info, [:meta_info])
229
233
  create_nodes(rest)
234
+ @website.blackboard.remove_listener(:before_node_created, 'path_handler (temp_populate_tree)')
230
235
 
231
- used_paths = @website.tree.node_access[:alcn].values.map {|n| n.node_info[:path]}
232
236
  unused_paths = rest - used_paths
233
237
  @website.logger.vinfo do
234
238
  "The following source paths have not been used: #{unused_paths.join(', ')}"
@@ -258,11 +262,11 @@ module Webgen
258
262
  at_least_one_node_written = false
259
263
  @website.cache.reset_volatile_cache
260
264
  @website.blackboard.dispatch_msg(:before_all_nodes_written)
261
- @website.tree.node_access[:alcn].sort.each do |name, node|
265
+ @website.tree.node_access[:alcn].sort_by {|a, n| [n['write_order'].to_s, a]}.each do |name, node|
262
266
  begin
263
267
  next if node == @website.tree.dummy_root ||
264
268
  (node['passive'] && !node['no_output'] && !@website.ext.item_tracker.node_referenced?(node)) ||
265
- ((@website.config['website.dry_run'] || @website.ext.destination.exists?(node.dest_path)) &&
269
+ ((@website.config['website.dry_run'] || node['no_output'] || @website.ext.destination.exists?(node.dest_path)) &&
266
270
  !@website.ext.item_tracker.node_changed?(node))
267
271
 
268
272
  @website.blackboard.dispatch_msg(:before_node_written, node)