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
@@ -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)