webgen 0.5.3 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/Rakefile +2 -0
  2. data/VERSION +1 -1
  3. data/bin/webgen +1 -0
  4. data/doc/contentprocessor.template +4 -3
  5. data/doc/contentprocessor/blocks.page +5 -6
  6. data/doc/contentprocessor/builder.page +1 -1
  7. data/doc/contentprocessor/erb.page +1 -1
  8. data/doc/contentprocessor/erubis.page +46 -0
  9. data/doc/contentprocessor/haml.page +1 -1
  10. data/doc/contentprocessor/maruku.page +1 -1
  11. data/doc/contentprocessor/rdiscount.page +37 -0
  12. data/doc/contentprocessor/rdoc.page +1 -1
  13. data/doc/contentprocessor/redcloth.page +0 -1
  14. data/doc/contentprocessor/sass.page +0 -1
  15. data/doc/contentprocessor/tags.page +1 -1
  16. data/doc/extensions.page +2 -7
  17. data/doc/faq.page +49 -12
  18. data/doc/index.page +9 -9
  19. data/doc/manual.page +110 -22
  20. data/doc/reference_configuration.page +28 -3
  21. data/doc/reference_metainfo.page +2 -1
  22. data/doc/sourcehandler/page.page +16 -0
  23. data/doc/upgrading.page +109 -37
  24. data/doc/webgen_page_format.page +3 -1
  25. data/lib/webgen/configuration.rb +14 -1
  26. data/lib/webgen/contentprocessor.rb +2 -0
  27. data/lib/webgen/contentprocessor/erubis.rb +40 -0
  28. data/lib/webgen/contentprocessor/rdiscount.rb +15 -0
  29. data/lib/webgen/default_config.rb +8 -0
  30. data/lib/webgen/node.rb +22 -7
  31. data/lib/webgen/page.rb +9 -5
  32. data/lib/webgen/sourcehandler/base.rb +51 -31
  33. data/lib/webgen/tag/menu.rb +1 -3
  34. data/lib/webgen/version.rb +1 -1
  35. data/test/test_configuration.rb +7 -0
  36. data/test/test_contentprocessor_erubis.rb +47 -0
  37. data/test/test_contentprocessor_rdiscount.rb +15 -0
  38. data/test/test_node.rb +8 -0
  39. data/test/test_page.rb +6 -1
  40. data/test/test_sourcehandler_base.rb +35 -21
  41. metadata +28 -2
@@ -151,7 +151,7 @@ This reference describes all available configurations that can be set via the co
151
151
  Specifies whether path names should be considered case-sensitive (set to `false`) or
152
152
  case-insensitive (set to `true`).
153
153
 
154
- * Syntax: `BOOLEAN` where `BOOLEAN` is either `true` or `false`
154
+ * Syntax: `BOOLEAN` where `BOOLEAN` is either `true` or `false`.
155
155
 
156
156
  * Examples:
157
157
 
@@ -162,7 +162,7 @@ This reference describes all available configurations that can be set via the co
162
162
 
163
163
  Specifies whether hidden files (those starting with a dot) should be used.
164
164
 
165
- * Syntax: `BOOLEAN` where `BOOLEAN` is either `true` or `false`
165
+ * Syntax: `BOOLEAN` where `BOOLEAN` is either `true` or `false`.
166
166
 
167
167
  * Examples:
168
168
 
@@ -186,7 +186,7 @@ This reference describes all available configurations that can be set via the co
186
186
  Specifies whether output paths in the default language should have the language code in their
187
187
  name.
188
188
 
189
- * Syntax: `BOOLEAN` where `BOOLEAN` is either `true` or `false`
189
+ * Syntax: `BOOLEAN` where `BOOLEAN` is either `true` or `false`.
190
190
 
191
191
  * Examples:
192
192
 
@@ -240,6 +240,31 @@ This reference describes all available configurations that can be set via the co
240
240
  config['contentprocessor.map']['newcp'] = 'Extension::MyNewContentProcessor'
241
241
 
242
242
 
243
+ * ### contentprocessor.erubis.use\_pi
244
+
245
+ Specifies whether Erubis should look for XML processing instructions or the standard ERB tags
246
+ when processing content.
247
+
248
+ * Syntax: `BOOLEAN` where `BOOLEAN` is either `true` or `false`.
249
+
250
+ * Examples:
251
+
252
+ contentprocessor.erubis.use_pi: true
253
+
254
+
255
+ * ### contentprocessor.erubis.options
256
+
257
+ This configuration option can be used to specify additional options that the Erubis processor
258
+ should use.
259
+
260
+ * Syntax: `\{KEY: VALUE, ...}` where `KEY` and `VALUE` are key-value pairs of options where
261
+ `KEY` needs to be a Symbol and not a String.
262
+
263
+ * Examples:
264
+
265
+ contentprocessor.erubis.options: {:trim: true}
266
+
267
+
243
268
  * ### contentprocessor.tags.prefix
244
269
 
245
270
  Specifies the optional prefix that is used for tag names to avoid name clashes when another
@@ -157,7 +157,8 @@ Controls whether the index path should appear in a breadcrumb trail despite the
157
157
  Sets a custom output path style for the specified path. The basename is substituted for the value
158
158
  `:cnbase` and the language for the value `:lang`. Strings are used verbatim. If `:lang` is specified
159
159
  in a sub-array, the whole sub-array is omitted, if the configuration option
160
- `sourcehandler.default_lang_in_output_path` is false.
160
+ `sourcehandler.default_lang_in_output_path` is false. For more and detailed information, have look
161
+ at the [output path creation section]({relocatable: manual.html#source-output}) of the manual!
161
162
 
162
163
  > This meta information has to be set BEFORE a node gets created. Setting this value is therefore
163
164
  > only useful, for example, in the `paths` block of a meta information backing file.
@@ -12,3 +12,19 @@ meta information.
12
12
  set, the default language specified using the configuration option `website.lang` is used. This
13
13
  means that for the default language set to English and a source path named `index.page`, the
14
14
  language meta information is automatically set to English.
15
+
16
+ ## Fragment nodes
17
+
18
+ The page handler automatically generates fragment nodes for all found header tags in the block named
19
+ `content` (i.e. `h1`, `h2`, ...) that have an `id` attribute set. The default markup language Maruku
20
+ automatically generates an `id` attribute for all headers. If you use another markup language or
21
+ plain old HTML, you might need to set the `id` attributes by hand.
22
+
23
+ > The reason why only header tags with an `id` attribute are used is that only those can be
24
+ > referenced and linked to later.
25
+ {.information}
26
+
27
+ The generated fragment nodes can be used like any other node. So you can link to them and use them
28
+ in a menu. Concerning the menu, there is a setting for the `tag.menu.used_nodes` option called
29
+ `fragments` which only uses the fragment node of the current page to generate a menu. This allows to
30
+ generate a nice overview of the page.
data/doc/upgrading.page CHANGED
@@ -3,31 +3,44 @@ title: Upgrading from 0.4.x
3
3
  ---
4
4
  # Upgrading
5
5
 
6
- Here are some helpers to make upgrading a webgen website from 0.4.x to 0.5.x more easy:
6
+ Here are step-by-step instructions on how to update your webgen website from 0.4.x to 0.5.x:
7
7
 
8
- * **Files in [Webgen Page Format]({relocatable: webgen_page_format.html})**: Since the format of
9
- these files changed a little bit you may need to adapt all your files that use it, that is
10
- primarily page and template files. The main change in the format was a different use of the block
11
- separator line. Whereas before you would write
8
+ * **Update the configuration file `config.yaml`**
12
9
 
13
- --- content, textile
10
+ The configuration file syntax as well as the names of the configuration options and some defaults
11
+ changed. For example, the default processing pipeline now uses Maruku (a Markdown converter) as
12
+ markup language processor instead of Textile. You can find an overview over all available
13
+ configuration options in the [configuration option reference]({relocatable:
14
+ reference_configuration.html}). Also have a look at the [configuration file
15
+ documentation]({relocatable: manual.html#website-configfile}) for more information on the syntax
16
+ of this file and the available helpers.
14
17
 
15
- for specifying the name of the block and its processor, you now can specify any number of
16
- options. Two options are currently used by webgen: `name` and `pipeline`. So you could change the
17
- name and the processing pipeline of a block by using a block start line like:
18
+ * Name changes: All configuration options now use underscores to separate word parts instead of
19
+ camelCase.
20
+ * Syntax changes: The configuration options are not specific to a certain extension anymore. You
21
+ now need the full configuration option name to specify it. So instead of
18
22
 
19
- --- name:other pipeline:tags,maruku,blocks
23
+ Tag/Menu:
24
+ maxLevels: 4
25
+
26
+ you now use
27
+
28
+ tag.menu.max_levels: 4
29
+
30
+ * **Convert your `metainfo.yaml`**
20
31
 
21
- * **Block inclusion in template/page files**: The way how named blocks are included has
22
- changed. This feature is now provided by the content processor [blocks]({relocatable:
23
- contentprocessor/blocks.html}) instead of the tag `block`. This allows you to specify the point in
24
- the processing pipeline when a block should be included. So you definitely need to update your
25
- `default.template` file as well as any other page/template file where you used the `block` tag.
32
+ This file is not supported anymore since webgen 0.5.x uses a more flexible way for specifying meta
33
+ information and virtual paths. You need to migrate its data to `metainfo` and `virtual` files in
34
+ the source directory. Have a look at the documentation of the [metainfo source
35
+ handler]({relocatable: sourcehandler/metainfo.html}) and the [virtual source
36
+ handler]({relocatable: sourcehandler/virtual.html}).
37
+
38
+ * **Update meta information names and values**
26
39
 
27
- * **Meta information names**: The names of some meta information keys have been changed. Meta
28
- information names are not specified in camelCase anymore but with under\_scores. You can find a
29
- complete list of supported meta information names in the [meta information
30
- reference]({relocatable: reference_metainfo.html}). The most notable changes are:
40
+ The names of some meta information keys have been changed. Meta information names are not
41
+ specified in camelCase anymore but with under\_scores. You can find a complete list of supported
42
+ meta information names in the [meta information reference]({relocatable:
43
+ reference_metainfo.html}). The most notable changes are:
31
44
 
32
45
  * directoryName → routed\_title
33
46
  * inMenu → in\_menu
@@ -36,32 +49,91 @@ Here are some helpers to make upgrading a webgen website from 0.4.x to 0.5.x mor
36
49
  * outputNameStyle → output\_path\_style
37
50
  * orderInfo → sort\_info
38
51
 
39
- * **website\_dir/metainfo.yaml**: This file is not supported anymore since webgen 0.5.x uses a more
40
- flexible way for specifying meta information and virtual paths. You need to migrate its data to
41
- `metainfo` and `virtual` files in the source directory. Have a look at the documentation of the
42
- [metainfo source handler]({relocatable: sourcehandler/metainfo.html}) and the [virtual source
43
- handler]({relocatable: sourcehandler/virtual.html}).
52
+ Also be aware that the syntax of some meta information keys has changed. For example, all meta
53
+ information keys that took a source path name, e.g. `index_path`, now take an localized canonical
54
+ name.
44
55
 
45
- * **Configuration file syntax**: The configuration file syntax as well as the names of the
46
- configuration options and some defaults changed. For example, the default processing pipeline now
47
- uses Maruku (a Markdown converter) as markup language processor. You can find an overview over all
48
- available configuration options in the [configuration option reference]({relocatable:
49
- reference_configuration.html}). Also have a look at the [configuration file
50
- documentation]({relocatable: manual.html#website-configfile}) for more information on the syntax
51
- of this file and the available helpers.
56
+ You need to change the names/value in all places where meta information can be specified:
57
+
58
+ * `metainfo` files
59
+ * `virtual` files
60
+ * page and template files
61
+
62
+ * **Files in [Webgen Page Format]({relocatable: webgen_page_format.html})**
63
+
64
+ Since the format of these files changed a little bit you may need to adapt all your files that use
65
+ it, that are primarily page and template files. The main change in the format was a different use
66
+ of the block start line. Whereas before you would write
67
+
68
+ --- content, textile
69
+
70
+ for specifying the name of the block and its processor, you now can specify any number of
71
+ options. Two options are currently used by webgen: `name` and `pipeline`. So you could change the
72
+ name and the processing pipeline of a block by using a block start line like:
73
+
74
+ --- name:other pipeline:tags,maruku,blocks
75
+
76
+ * **Block inclusion in template/page files**
77
+
78
+ The way how named blocks are included has changed. This feature is now provided by the content
79
+ processor [blocks]({relocatable: contentprocessor/blocks.html}) instead of the tag `block`. This
80
+ allows you to specify the point in the processing pipeline when a block should be included. So you
81
+ definitely need to update your `default.template` file as well as any other page/template file
82
+ where you used the `block` tag.
83
+
84
+ So you need to look for `\{block: content}` tags (where `content` is just a place holder for the
85
+ name of the block that should be included) and replace them with `<webgen:block name='content'
86
+ />`.
87
+
88
+ * **Update tag names and parameters**
89
+
90
+ Since the names of the configuration options changed (from using camelCase to using under\_scores)
91
+ and some tags have different options, you need to change all tag parameters. You may also need to
92
+ convert old tag names to new ones (same reason: camelCase to under\_score), for example,
93
+ `includeFile` is now `include_file`.
94
+
95
+ * **Update your ERB code**
96
+
97
+ If you have any ERB code in your template or page files you will most certainly have to adapt them
98
+ to the new [API]({relocatable: api.html}). One thing that has been used often is the check if a
99
+ page file has a certain block:
100
+
101
+ <%% if node.node_info[:page_data].blocks.has_key?('NAME') %>
102
+ ...
103
+ <%% end %>
104
+
105
+ This needs to be changed into the following:
106
+
107
+ <%% if context.content_node.node_info[:page].blocks.has_key?('NAME') %>
108
+ ...
109
+ <%% end %>
110
+
111
+ * **Extensions development**
112
+
113
+ Since the complete core of webgen has changed you need to rewrite all your plugins for the 0.5.x
114
+ series. Howver, webgen has complete [API documentation]({relocatable: api.html}) now which
115
+ provides you with all needed information as well as examples on how to implement source handlers,
116
+ tags, content processors, ... If you still have any questions, don't hesitate to contact me or
117
+ write a mail to the mailing list!
118
+
119
+ * **Running webgen on the converted website**
120
+
121
+ You now can run webgen 0.5.x on the converted website. This helps in ironing out the remaining
122
+ errors, for example:
123
+
124
+ * If you have forgotten to change a block start line, you will get an application error and the
125
+ name of the file where the error occured.
52
126
 
53
- * **Extensions development**: Since the complete core of webgen has changed you need to rewrite all
54
- your plugins for the 0.5.x series. Howver, webgen has complete [API documentation]({relocatable:
55
- api.html}) now which provides you with all needed information as well as examples on how to
56
- implement source handlers, tags, content processors, ... If you still have any questions, don't
57
- hesitate to contact me or write a mail to the mailing list!
127
+ * If you have overlooked changing a tag parameter, you will find `ERROR` and `WARN` lines in the
128
+ log output showing you what still needs to be changed.
58
129
 
59
130
  * **Not Implement Yet**: There are several features of the 0.4.x series which are currently not
60
131
  implemented in the 0.5.x series:
61
132
 
62
133
  * source handlers: gallery, sipttra
63
134
  * tags: customvar (won't be ported), download, htmlmetainfo, news (won't be ported, superceded
64
- by blogging support), wikilink
135
+ by blogging support), resource, wikilink
65
136
  * misc: smiley replacer, html validators
137
+ * CLI commands: check, show, use
66
138
 
67
139
  If you need any of those you have to wait till they are implemented or port them on your on.
@@ -96,7 +96,9 @@ The first block has no identifieres set (there is no line with three dashes and
96
96
  Therefore the default value for the name is used: `content`. The second block is named `sidebar` and
97
97
  uses the processing pipeline `maruku,tags`. As you can see, the name of a block as well as
98
98
  additional options are specified by stating the key (e.g. `name` or `pipeline`) followed by a colon
99
- and the value. Multiple options are separated via one or more spaces.
99
+ and the value. Multiple options are separated via one or more spaces. The value of a block option is
100
+ parsed with YAML. For example, when specifying `use_something:true` the value `true` is
101
+ automatically converted from the string `true` to the boolean `true`.
100
102
 
101
103
  > Only the first block gets the default name of `content`. The second and following blocks have
102
104
  > numbered names like `block2`, `block3` and so on.
@@ -35,7 +35,7 @@ module Webgen
35
35
  end
36
36
 
37
37
  # This module provides methods for setting more complex configuration options. It is mixed into
38
- # Webgen::Configuration so that it methods can be used. Detailed information on the use of the
38
+ # Webgen::Configuration so that its methods can be used. Detailed information on the use of the
39
39
  # methods can be found in the "User Manual" in the "Configuration File" section.
40
40
  #
41
41
  # All public methods defined in this module are available for direct use in the
@@ -85,14 +85,27 @@ module Webgen
85
85
  end
86
86
  end
87
87
 
88
+
89
+ # Set the default processing pipeline for a source handler.
90
+ def default_processing_pipeline(args)
91
+ args.each do |sh_name, pipeline|
92
+ raise ArgumentError, 'Invalid argument for configuration helper pipeline' unless pipeline.kind_of?(String)
93
+ mi_hash = (self['sourcehandler.default_meta_info'][complete_source_handler_name(sh_name)] ||= {})
94
+ ((mi_hash['blocks'] ||= {})['default'] ||= {})['pipeline'] = pipeline
95
+ end
96
+ end
97
+
98
+
88
99
  # Complete +sh_name+ by checking if a source handler called
89
100
  # <tt>Webgen::SourceHandler::SH_NAME</tt> exists.
90
101
  def complete_source_handler_name(sh_name)
91
102
  (Webgen::SourceHandler.constants.include?(sh_name) ? 'Webgen::SourceHandler::' + sh_name : sh_name)
92
103
  end
93
104
  private :complete_source_handler_name
105
+
94
106
  end
95
107
 
108
+
96
109
  include Helpers
97
110
 
98
111
  # The hash which stores the meta info for the configuration options.
@@ -63,6 +63,8 @@ module Webgen
63
63
  autoload :Sass, 'webgen/contentprocessor/sass'
64
64
  autoload :RDoc, 'webgen/contentprocessor/rdoc'
65
65
  autoload :Builder, 'webgen/contentprocessor/builder'
66
+ autoload :Erubis, 'webgen/contentprocessor/erubis'
67
+ autoload :RDiscount, 'webgen/contentprocessor/rdiscount'
66
68
 
67
69
  # Return the list of all available content processors.
68
70
  def self.list
@@ -0,0 +1,40 @@
1
+ require 'webgen/websiteaccess'
2
+
3
+ module Webgen::ContentProcessor
4
+
5
+ # Processes embedded Ruby statements with the +erubis+ library.
6
+ class Erubis
7
+
8
+ include Webgen::WebsiteAccess
9
+
10
+ # Process the Ruby statements embedded in the content of +context+.
11
+ def call(context)
12
+ require 'erubis'
13
+ # including Erubis because of problem with resolving Erubis::XmlHelper et al
14
+ self.class.class_eval "include ::Erubis"
15
+
16
+ node = context.content_node
17
+ ref_node = context.ref_node
18
+ dest_node = context.dest_node
19
+
20
+ options = website.config['contentprocessor.erubis.options']
21
+ if context[:block]
22
+ use_pi = context[:block].options['erubis_use_pi']
23
+ context[:block].options.select {|k,v| k =~ /^erubis_/}.
24
+ each {|k,v| options[k.sub(/^erubis_/, '').to_sym] = v }
25
+ end
26
+ erubis = if (!use_pi.nil? && use_pi) || (use_pi.nil? && website.config['contentprocessor.erubis.use_pi'])
27
+ ::Erubis::PI::Eruby.new(context.content, options)
28
+ else
29
+ ::Erubis::Eruby.new(context.content, options)
30
+ end
31
+ erubis.filename = context.ref_node.absolute_lcn
32
+ context.content = erubis.result(binding)
33
+ context
34
+ rescue Exception => e
35
+ raise RuntimeError, "Erubis processing failed in <#{context.ref_node.absolute_lcn}>: #{e.message}", e.backtrace
36
+ end
37
+
38
+ end
39
+
40
+ end
@@ -0,0 +1,15 @@
1
+ module Webgen::ContentProcessor
2
+
3
+ # Processes content in Markdown markup with the fast +rdiscount+ library.
4
+ class RDiscount
5
+
6
+ # Convert the content in +context+ to HTML.
7
+ def call(context)
8
+ require 'rdiscount'
9
+ context.content = ::RDiscount.new(context.content).to_html
10
+ context
11
+ end
12
+
13
+ end
14
+
15
+ end
@@ -67,6 +67,7 @@ config.sourcehandler.default_lang_in_output_path(false, :doc => 'Specifies wheth
67
67
 
68
68
  config.sourcehandler.default_meta_info({
69
69
  :all => {
70
+ 'output_path' => 'standard',
70
71
  'output_path_style' => [:parent, :cnbase, ['.', :lang], :ext]
71
72
  },
72
73
  'Webgen::SourceHandler::Copy' => {
@@ -117,7 +118,9 @@ Webgen::WebsiteAccess.website.blackboard.add_service(:output_instance, Webgen::O
117
118
 
118
119
  # All things regarding content processors
119
120
  config.contentprocessor.map({
121
+ 'markdown' => 'Webgen::ContentProcessor::Maruku',
120
122
  'maruku' => 'Webgen::ContentProcessor::Maruku',
123
+ 'textile' => 'Webgen::ContentProcessor::RedCloth',
121
124
  'redcloth' => 'Webgen::ContentProcessor::RedCloth',
122
125
  'tags' => 'Webgen::ContentProcessor::Tags',
123
126
  'blocks' => 'Webgen::ContentProcessor::Blocks',
@@ -126,6 +129,8 @@ config.contentprocessor.map({
126
129
  'sass' => 'Webgen::ContentProcessor::Sass',
127
130
  'rdoc' => 'Webgen::ContentProcessor::RDoc',
128
131
  'builder' => 'Webgen::ContentProcessor::Builder',
132
+ 'erubis' => 'Webgen::ContentProcessor::Erubis',
133
+ 'rdiscount' => 'Webgen::ContentProcessor::RDiscount',
129
134
  }, :doc => 'Content processor name to class map')
130
135
 
131
136
  Webgen::WebsiteAccess.website.blackboard.add_service(:content_processor_names, Webgen::ContentProcessor.method(:list))
@@ -146,6 +151,9 @@ config.contentprocessor.tags.map({
146
151
  :default => 'Webgen::Tag::Metainfo'
147
152
  }, :doc => 'Tag processor name to class map')
148
153
 
154
+ config.contentprocessor.erubis.use_pi(false, :doc => 'Specify whether processing instructions should be used')
155
+ config.contentprocessor.erubis.options({}, :doc => 'A hash of additional options')
156
+
149
157
  config.tag.relocatable.path(nil, :doc => 'The path which should be made relocatable', :mandatory => 'default')
150
158
 
151
159
  config.tag.menu.start_level(1, :doc => 'The level at which the menu starts.')
data/lib/webgen/node.rb CHANGED
@@ -119,18 +119,22 @@ module Webgen
119
119
  # Return +true+ if the node has changed since the last webgen run. If it has changed, +dirty+ is
120
120
  # set to +true+.
121
121
  def changed?
122
- @dirty = @dirty || meta_info_changed?
123
- @dirty = node_info[:used_nodes].any? {|n| n != @absolute_lcn && (!tree[n] || tree[n].changed?)} unless @dirty
124
- website.blackboard.dispatch_msg(:node_changed?, self) unless @dirty
122
+ if_not_checked(:node) do
123
+ @dirty = @dirty || meta_info_changed?
124
+ @dirty = node_info[:used_nodes].any? {|n| n != @absolute_lcn && (!tree[n] || tree[n].changed?)} unless @dirty
125
+ website.blackboard.dispatch_msg(:node_changed?, self) unless @dirty
126
+ end
125
127
  @dirty
126
128
  end
127
129
 
128
130
  # Return +true+ if the meta information of the node has changed.
129
131
  def meta_info_changed?
130
- @dirty_meta_info = node_info[:used_meta_info_nodes].any? do |n|
131
- n != @absolute_lcn && (!tree[n] || tree[n].meta_info_changed?)
132
- end unless @dirty_meta_info
133
- website.blackboard.dispatch_msg(:node_meta_info_changed?, self) unless @dirty_meta_info
132
+ if_not_checked(:meta_info) do
133
+ @dirty_meta_info = node_info[:used_meta_info_nodes].any? do |n|
134
+ n != @absolute_lcn && (!tree[n] || tree[n].meta_info_changed?)
135
+ end unless @dirty_meta_info
136
+ website.blackboard.dispatch_msg(:node_meta_info_changed?, self) unless @dirty_meta_info
137
+ end
134
138
  @dirty_meta_info
135
139
  end
136
140
 
@@ -317,6 +321,17 @@ module Webgen
317
321
  self.node_info[:used_meta_info_nodes] = Set.new
318
322
  end
319
323
 
324
+ # Only run the code in the block if this node has not already been checked. Different checks are
325
+ # supported by setting a different +type+ value.
326
+ def if_not_checked(type)
327
+ array = (website.cache.volatile[:node_change_checking] ||= {})[type] ||= []
328
+ if !array.include?(self)
329
+ array << self
330
+ yield
331
+ array.delete(self)
332
+ end
333
+ end
334
+
320
335
  # Delegate missing methods to a processor. The current node is placed into the argument array as
321
336
  # the first argument before the method +name+ is invoked on the processor.
322
337
  def method_missing(name, *args, &block)