webgen 1.0.0.beta1 → 1.0.0.beta2

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 (62) hide show
  1. data/API.rdoc +64 -11
  2. data/ChangeLog +391 -0
  3. data/README.md +2 -2
  4. data/VERSION +1 -1
  5. data/data/webgen/passive_sources/templates/tag.template +1 -1
  6. data/data/webgen/passive_sources/templates/tikz.template +14 -0
  7. data/lib/webgen/bundle/built-in/init.rb +9 -7
  8. data/lib/webgen/bundle_loader.rb +1 -1
  9. data/lib/webgen/cli/show_command.rb +2 -0
  10. data/lib/webgen/cli/show_config_command.rb +0 -1
  11. data/lib/webgen/cli/show_dependencies_command.rb +6 -63
  12. data/lib/webgen/cli/show_tree_command.rb +85 -0
  13. data/lib/webgen/content_processor/erb.rb +1 -1
  14. data/lib/webgen/content_processor/{rdoc.rb → r_doc.rb} +0 -0
  15. data/lib/webgen/content_processor/{redcloth.rb → red_cloth.rb} +0 -0
  16. data/lib/webgen/content_processor/ruby.rb +3 -0
  17. data/lib/webgen/content_processor/sass.rb +10 -6
  18. data/lib/webgen/content_processor/tikz.rb +10 -20
  19. data/lib/webgen/context/html_head.rb +7 -0
  20. data/lib/webgen/item_tracker.rb +42 -15
  21. data/lib/webgen/item_tracker/file.rb +7 -3
  22. data/lib/webgen/item_tracker/missing_node.rb +17 -5
  23. data/lib/webgen/item_tracker/node_content.rb +7 -3
  24. data/lib/webgen/item_tracker/node_meta_info.rb +8 -3
  25. data/lib/webgen/item_tracker/nodes.rb +10 -3
  26. data/lib/webgen/node.rb +4 -29
  27. data/lib/webgen/node_finder.rb +25 -10
  28. data/lib/webgen/path.rb +2 -2
  29. data/lib/webgen/path_handler/base.rb +20 -9
  30. data/lib/webgen/path_handler/feed.rb +29 -22
  31. data/lib/webgen/path_handler/page.rb +1 -3
  32. data/lib/webgen/path_handler/page_utils.rb +114 -33
  33. data/lib/webgen/path_handler/sitemap.rb +13 -6
  34. data/lib/webgen/path_handler/template.rb +0 -70
  35. data/lib/webgen/path_handler/virtual.rb +10 -2
  36. data/lib/webgen/tag.rb +13 -13
  37. data/lib/webgen/tag/tikz.rb +2 -1
  38. data/lib/webgen/test_helper.rb +2 -11
  39. data/lib/webgen/version.rb +1 -1
  40. data/test/webgen/content_processor/test_erb.rb +4 -0
  41. data/test/webgen/content_processor/{test_rdoc.rb → test_r_doc.rb} +1 -1
  42. data/test/webgen/content_processor/{test_redcloth.rb → test_red_cloth.rb} +1 -1
  43. data/test/webgen/content_processor/test_sass.rb +2 -2
  44. data/test/webgen/content_processor/test_tikz.rb +10 -3
  45. data/test/webgen/item_tracker/test_file.rb +5 -5
  46. data/test/webgen/item_tracker/test_missing_node.rb +8 -9
  47. data/test/webgen/item_tracker/test_node_content.rb +5 -6
  48. data/test/webgen/item_tracker/test_node_meta_info.rb +6 -7
  49. data/test/webgen/item_tracker/test_nodes.rb +7 -9
  50. data/test/webgen/path_handler/test_base.rb +4 -5
  51. data/test/webgen/path_handler/test_page.rb +1 -1
  52. data/test/webgen/path_handler/test_page_utils.rb +39 -11
  53. data/test/webgen/path_handler/test_template.rb +5 -45
  54. data/test/webgen/path_handler/test_virtual.rb +21 -0
  55. data/test/webgen/tag/test_link.rb +1 -1
  56. data/test/webgen/tag/test_tikz.rb +2 -2
  57. data/test/webgen/test_context.rb +10 -0
  58. data/test/webgen/test_item_tracker.rb +7 -4
  59. data/test/webgen/test_node.rb +3 -16
  60. data/test/webgen/test_node_finder.rb +10 -6
  61. data/test/webgen/test_tag.rb +4 -4
  62. metadata +218 -216
@@ -23,6 +23,34 @@ module Webgen
23
23
  include Base
24
24
  include PageUtils
25
25
 
26
+
27
+ # Provides custom methods needed for feed nodes.
28
+ class Node < PageUtils::Node
29
+
30
+ # Return the entries for this feed node.
31
+ def feed_entries
32
+ tree.website.ext.node_finder.find(self['entries'], self)
33
+ end
34
+
35
+ # Return the feed link URL for this feed node.
36
+ def feed_link
37
+ Webgen::Path.url(File.join(self['site_url'], tree[self['link']].dest_path), false)
38
+ end
39
+
40
+ # Return the content of an +entry+ (a Node object) of this feed node.
41
+ def entry_content(entry)
42
+ block_name = self['content_block_name'] || 'content'
43
+ if entry.respond_to?(:render_block) && entry.blocks[block_name]
44
+ entry.render_block(block_name, Webgen::Context.new(tree.website, :chain => [entry])).content
45
+ else
46
+ tree.website.logger.warn { "Feed entry <#{entry}> not used, is not a renderable node" }
47
+ ''
48
+ end
49
+ end
50
+
51
+ end
52
+
53
+
26
54
  # The mandatory keys that need to be set in a feed file.
27
55
  MANDATORY_INFOS = %W[site_url author entries]
28
56
 
@@ -40,7 +68,7 @@ module Webgen
40
68
  path.ext = path['version']
41
69
  path['dest_path'] = '<parent><basename>(.<lang>)<ext>'
42
70
  path['cn'] = '<basename><ext>'
43
- create_node(path) do |node|
71
+ create_node(path, Node) do |node|
44
72
  set_blocks(node, blocks)
45
73
  node.meta_info['link'] ||= node.parent.alcn
46
74
  @website.ext.item_tracker.add(node, :nodes, :node_finder_option_set,
@@ -55,27 +83,6 @@ module Webgen
55
83
  :chain => [node, node.resolve("/templates/feed.template", node.lang, true), node].compact)
56
84
  end
57
85
 
58
- # Return the entries for the feed node.
59
- def feed_entries(node)
60
- @website.ext.node_finder.find(node['entries'], node)
61
- end
62
-
63
- # Return the feed link URL for the feed node.
64
- def feed_link(node)
65
- Webgen::Path.url(File.join(node['site_url'], node.tree[node['link']].dest_path), false)
66
- end
67
-
68
- # Return the content of an +entry+ of the feed +node+.
69
- def entry_content(node, entry)
70
- block_name = node['content_block_name'] || 'content'
71
- if entry.respond_to?(:render_block) && entry.blocks[block_name]
72
- entry.render_block(block_name, Webgen::Context.new(@website, :chain => [entry])).content
73
- else
74
- @website.logger.warn { "Feed entry <#{entry}> not used, is not a renderable node" }
75
- ''
76
- end
77
- end
78
-
79
86
  end
80
87
 
81
88
  end
@@ -25,10 +25,8 @@ module Webgen
25
25
  #
26
26
  # If the parameter +chain+ (an array of template nodes) is not set, the default template chain
27
27
  # for the given +node+ is used.
28
- def content(node, block_name = 'content', chain = nil)
29
- chain ||= @website.ext.path_handler.instance(:template).template_chain(node)
28
+ def content(node, block_name = 'content', chain = node.template_chain)
30
29
  chain << node
31
-
32
30
  chain.first.render_block(block_name, Webgen::Context.new(@website, :chain => chain)).content
33
31
  end
34
32
 
@@ -1,6 +1,6 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
- require 'webgen/path_handler'
3
+ require 'webgen/path_handler/base'
4
4
  require 'webgen/page'
5
5
  require 'webgen/error'
6
6
 
@@ -14,6 +14,119 @@ module Webgen
14
14
  # to override this method if you need custom behaviour!
15
15
  module PageUtils
16
16
 
17
+ # Custom Node class that provides easy access to the blocks of the parsed page file and
18
+ # methods for rendering a block.
19
+ class Node < Webgen::PathHandler::Base::Node
20
+
21
+ # Return the blocks (see PageUtils#parse_as_page!) for this node.
22
+ def blocks
23
+ node_info[:blocks]
24
+ end
25
+
26
+ # Render the block +name+ of this node using the provided Context object.
27
+ #
28
+ # Uses the content processors specified for the block via the +blocks+ meta information key if
29
+ # the +pipeline+ parameter is not set.
30
+ #
31
+ # Returns the given context with the rendered content.
32
+ def render_block(name, context, pipeline = nil)
33
+ unless blocks.has_key?(name)
34
+ raise Webgen::RenderError.new("No block named '#{name}' found", nil, context.dest_node.alcn, alcn)
35
+ end
36
+
37
+ content_processor = context.website.ext.content_processor
38
+ context.website.ext.item_tracker.add(context.dest_node, :node_content, alcn)
39
+
40
+ context.content = blocks[name].dup
41
+ context[:block_name] = name
42
+ pipeline ||= ((self['blocks'] || {})[name] || {})['pipeline'] ||
43
+ ((self['blocks'] || {})['defaults'] || {})['pipeline'] ||
44
+ []
45
+ content_processor.normalize_pipeline(pipeline).each do |processor|
46
+ content_processor.call(processor, context)
47
+ end
48
+ context[:block_name] = nil
49
+ context
50
+ end
51
+
52
+ # Return the template chain for this node.
53
+ #
54
+ # When invoked directly, the +lang+ parameter should not be used. This parameter is necessary
55
+ # for the recursive invocation of the method so that the correct templates are used. Consider
56
+ # the following path hierarchy:
57
+ #
58
+ # /default.en.template
59
+ # /default.de.template
60
+ # /custom.template
61
+ # /index.de.page template: custom.template
62
+ # /index.en.page template: custom.template
63
+ #
64
+ # The template chains for index.en.page and index.de.page are therefore
65
+ #
66
+ # /default.en.template → /custom.template
67
+ # /default.de.template → /custom.template
68
+ #
69
+ # This means that the /custom.template needs to reference different templates depending on the
70
+ # language.
71
+ def template_chain(lang = @lang)
72
+ cached_template = (tree.website.cache.volatile[[alcn, :templates]] ||= {})
73
+ if cached_template.has_key?(lang)
74
+ template_node = cached_template[lang]
75
+ elsif self['template'].kind_of?(String)
76
+ template_node = resolve(self['template'], lang, true)
77
+ if template_node.nil?
78
+ tree.website.logger.warn do
79
+ ["Template '#{self['template']}' for <#{self}> not found, using default template!",
80
+ 'Fix the value of the meta information \'template\' for <#{self}>']
81
+ end
82
+ template_node = default_template(parent, lang)
83
+ end
84
+ cached_template[lang] = template_node
85
+ elsif meta_info.has_key?('template') && self['template'].nil?
86
+ template_node = cached_template[lang] = nil
87
+ else
88
+ tree.website.logger.debug { "Using default template in language '#{lang}' for <#{self}>" }
89
+ template_node = default_template(parent, lang)
90
+ if template_node == self && !parent.is_root?
91
+ template_node = default_template(parent.parent, lang)
92
+ end
93
+ cached_template[lang] = template_node
94
+ end
95
+
96
+ if template_node.nil?
97
+ []
98
+ else
99
+ (template_node == self ? [] : template_node.template_chain(lang) + [template_node])
100
+ end
101
+ end
102
+
103
+ # Return the default template for the directory node +dir+ and language +lang+.
104
+ #
105
+ # If the template node is not found, the parent directories are searched.
106
+ def default_template(dir, lang)
107
+ default_template_name = tree.website.config['path_handler.default_template']
108
+ template = dir.resolve(default_template_name, lang)
109
+ if template.nil?
110
+ if dir.is_root?
111
+ tree.website.logger.warn do
112
+ ["Default template '#{default_template_name}' not found in root directory!",
113
+ 'Provide a </#{default_template_name}> to fix this warning.']
114
+ end
115
+ else
116
+ template = default_template(dir.parent, lang)
117
+ end
118
+ end
119
+ template
120
+ end
121
+ protected :default_template
122
+
123
+ end
124
+
125
+
126
+ def create_node(path, node_klass = Node) #:nodoc:
127
+ super
128
+ end
129
+
17
130
  # Calls #parse_as_page! to update the meta information hash of +path+. Returns the found
18
131
  # blocks which will be passed as second parameter to the #create_nodes method.
19
132
  def parse_meta_info!(path)
@@ -41,38 +154,6 @@ module Webgen
41
154
  end
42
155
  private :set_blocks
43
156
 
44
- # Return the blocks (see #parse_as_page!) for the node.
45
- def blocks(node)
46
- node.node_info[:blocks]
47
- end
48
-
49
- # Render the block +name+ of +node+ using the provided Context object.
50
- #
51
- # Uses the content processors specified for the block via the +blocks+ meta information key if
52
- # the +pipeline+ parameter is not set.
53
- #
54
- # Returns the given context with the rendered content.
55
- def render_block(node, name, context, pipeline = nil)
56
- unless node.blocks.has_key?(name)
57
- raise Webgen::RenderError.new("No block named '#{name}' found", self.class.name,
58
- context.dest_node.alcn, node.alcn)
59
- end
60
-
61
- content_processor = context.website.ext.content_processor
62
- context.website.ext.item_tracker.add(context.dest_node, :node_content, node.alcn)
63
-
64
- context.content = node.blocks[name].dup
65
- context[:block_name] = name
66
- pipeline ||= ((node.meta_info['blocks'] || {})[name] || {})['pipeline'] ||
67
- ((node.meta_info['blocks'] || {})['defaults'] || {})['pipeline'] ||
68
- []
69
- content_processor.normalize_pipeline(pipeline).each do |processor|
70
- content_processor.call(processor, context)
71
- end
72
- context[:block_name] = nil
73
- context
74
- end
75
-
76
157
  end
77
158
 
78
159
  end
@@ -15,6 +15,18 @@ module Webgen
15
15
  include Base
16
16
  include PageUtils
17
17
 
18
+
19
+ # Provides custom methods for sitemap nodes.
20
+ class Node < PageUtils::Node
21
+
22
+ # Return the entries for the sitemap +node+.
23
+ def sitemap_entries
24
+ tree.website.ext.node_finder.find(node_info[:entries], self)
25
+ end
26
+
27
+ end
28
+
29
+
18
30
  # The mandatory keys that need to be set in a sitemap file.
19
31
  MANDATORY_INFOS = %W[site_url entries]
20
32
 
@@ -26,7 +38,7 @@ module Webgen
26
38
  end
27
39
 
28
40
  path.ext = 'xml'
29
- create_node(path) do |node|
41
+ create_node(path, Node) do |node|
30
42
  set_blocks(node, blocks)
31
43
  node.node_info[:entries] = {:flatten => true, :and => node['entries']}
32
44
  @website.ext.item_tracker.add(node, :nodes, :node_finder_option_set,
@@ -41,11 +53,6 @@ module Webgen
41
53
  :chain => [node, node.resolve("/templates/sitemap.template", node.lang, true), node].compact)
42
54
  end
43
55
 
44
- # Return the entries for the sitemap +node+.
45
- def sitemap_entries(node)
46
- @website.ext.node_finder.find(node.node_info[:entries], node)
47
- end
48
-
49
56
  end
50
57
 
51
58
  end
@@ -20,76 +20,6 @@ module Webgen
20
20
  end
21
21
  end
22
22
 
23
- # Return the template chain for +node+
24
- #
25
- # When invoked directly, the +lang+ parameter should not be used. This parameter is necessary
26
- # for the recursive invocation of the method so that the correct templates are used. Consider
27
- # the following path hierarchy:
28
- #
29
- # /default.en.template
30
- # /default.de.template
31
- # /custom.template
32
- # /index.de.page template: custom.template
33
- # /index.en.page template: custom.template
34
- #
35
- # The template chains for index.en.page and index.de.page are therefore
36
- #
37
- # /default.en.template → /custom.template
38
- # /default.de.template → /custom.template
39
- #
40
- # This means that the /custom.template needs to reference different templates depending on the
41
- # language.
42
- def template_chain(node, lang = node.lang)
43
- cached_template = (@website.cache.volatile[[node.alcn, :templates]] ||= {})
44
- if cached_template.has_key?(lang)
45
- template_node = cached_template[lang]
46
- elsif node['template'].kind_of?(String)
47
- template_node = node.resolve(node['template'], lang, true)
48
- if template_node.nil?
49
- @website.logger.warn do
50
- ["Template '#{node['template']}' for <#{node}> not found, using default template!",
51
- 'Fix the value of the meta information \'template\' for <#{node}>']
52
- end
53
- template_node = default_template(node.parent, lang)
54
- end
55
- cached_template[lang] = template_node
56
- elsif node.meta_info.has_key?('template') && node['template'].nil?
57
- template_node = cached_template[lang] = nil
58
- else
59
- @website.logger.debug { "Using default template in language '#{lang}' for <#{node}>" }
60
- template_node = default_template(node.parent, lang)
61
- if template_node == node && !node.parent.is_root?
62
- template_node = default_template(node.parent.parent, lang)
63
- end
64
- cached_template[lang] = template_node
65
- end
66
-
67
- if template_node.nil?
68
- []
69
- else
70
- (template_node == node ? [] : template_chain(template_node, lang) + [template_node])
71
- end
72
- end
73
-
74
- # Return the default template for the directory node +dir+ and language +lang+.
75
- #
76
- # If the template node is not found, the parent directories are searched.
77
- def default_template(dir, lang)
78
- default_template_name = @website.config['path_handler.template.default_template']
79
- template = dir.resolve(default_template_name, lang)
80
- if template.nil?
81
- if dir.is_root?
82
- @website.logger.warn do
83
- ["Default template '#{default_template_name}' not found in root directory!",
84
- 'Provide a </#{default_template_name}> to fix this warning.']
85
- end
86
- else
87
- template = default_template(dir.parent, lang)
88
- end
89
- end
90
- template
91
- end
92
-
93
23
  end
94
24
 
95
25
  end
@@ -60,8 +60,16 @@ module Webgen
60
60
  # each entry.
61
61
  def read_entries(blocks)
62
62
  blocks.each do |name, content|
63
- YAML::load(content).each do |key, meta_info|
64
- yield(key, meta_info || {})
63
+ begin
64
+ data = YAML::load(content)
65
+ rescue RuntimeError, ArgumentError, SyntaxError => e
66
+ raise RuntimeError, "Problem parsing block '#{name}' (it needs to contain a YAML hash): #{e.message}", e.backtrace
67
+ end
68
+ raise "Structure of block '#{name}' is invalid, it has to be a Hash" unless data.kind_of?(Hash)
69
+ data.each do |key, meta_info|
70
+ meta_info ||= {}
71
+ raise "Each path key value needs to be a Hash, found a #{meta_info.class} for '#{key}'" unless meta_info.kind_of?(Hash)
72
+ yield(key, meta_info)
65
73
  end
66
74
  end
67
75
  end
data/lib/webgen/tag.rb CHANGED
@@ -42,7 +42,7 @@ module Webgen
42
42
  #
43
43
  # webgen tags allow the specification of options in the tag definition. When registering a tag,
44
44
  # one can specify which options are mandatory, i.e. which options always have to be set directly
45
- # for the tag. The value of the option :config_base for the #register method is used to resolve
45
+ # for the tag. The value of the option :config_prefix for the #register method is used to resolve
46
46
  # partially stated configuration entries.
47
47
  #
48
48
  # == Sample Tag
@@ -111,10 +111,10 @@ module Webgen
111
111
  # The name :default is used for specifying the default tag which is called if an
112
112
  # unknown tag name is encountered.
113
113
  #
114
- # [:config_base] The configuration base, i.e. the part of a configuration option name that does
115
- # not need to be specified. Defaults to the full class name without the Webgen
116
- # module downcased and all "::" substituted with "." (e.g. Webgen::Tag::Menu →
117
- # tag.menu). Needs to be specified when a block is used!
114
+ # [:config_prefix] The configuration prefix, i.e. the part of a configuration option name that
115
+ # does not need to be specified. Defaults to the full class name without the
116
+ # Webgen module downcased and all "::" substituted with "." (e.g.
117
+ # Webgen::Tag::Menu → tag.menu). Needs to be specified when a block is used!
118
118
  #
119
119
  # [:mandatory] A list of configuration option names whose values always need to be provided. The
120
120
  # first configuration option name is used as the default mandatory option (used
@@ -128,20 +128,20 @@ module Webgen
128
128
  #
129
129
  # tag.register('MyModule::Date', names: ['mydate', 'date'])
130
130
  #
131
- # tag.register('date', config_base: 'tag.date') do |tag, body, context|
131
+ # tag.register('date', config_prefix: 'tag.date') do |tag, body, context|
132
132
  # Time.now.strftime(param('tag.date.format'))
133
133
  # end
134
134
  #
135
135
  def register(klass, options = {}, &block)
136
- if block_given? && !options[:config_base]
137
- raise ArgumentError, "The option :config_base needs to be specified when using a block"
136
+ if block_given? && !options[:config_prefix]
137
+ raise ArgumentError, "The option :config_prefix needs to be specified when registering a tag using a block"
138
138
  end
139
139
 
140
140
  names = [options.delete(:names)].flatten.compact
141
141
  options[:name] = names.shift
142
142
  name = do_register(klass, options, true, &block)
143
143
  ext_data(name).mandatory = options[:mandatory] || []
144
- ext_data(name).config_base = options[:config_base] ||
144
+ ext_data(name).config_prefix = options[:config_prefix] ||
145
145
  Webgen::Utils.snake_case(ext_data(name).object.to_s.gsub(/::/, '.').gsub(/^Webgen\./, ''))
146
146
  ext_data(name).initialized = false
147
147
  names.each {|n| @extensions[n.to_sym] = @extensions[name]}
@@ -182,7 +182,7 @@ module Webgen
182
182
  #######
183
183
 
184
184
  # Create the Webgen::Configuration object from the parameters and the given configuration
185
- # base.
185
+ # prefix.
186
186
  def create_config(tag, params, tdata, context)
187
187
  values = case params
188
188
  when Hash then values_from_hash(tag, params, tdata, context)
@@ -208,8 +208,8 @@ module Webgen
208
208
  params.each do |key, value|
209
209
  if context.website.config.option?(key)
210
210
  result[key] = value
211
- elsif context.website.config.option?(tdata.config_base + '.' + key)
212
- result[tdata.config_base + '.' + key] = value
211
+ elsif context.website.config.option?(tdata.config_prefix + '.' + key)
212
+ result[tdata.config_prefix + '.' + key] = value
213
213
  else
214
214
  context.website.logger.warn do
215
215
  ["Invalid configuration option '#{key}' for tag '#{tag}' found in <#{context.ref_node}>",
@@ -241,7 +241,7 @@ module Webgen
241
241
  tdata.object = resolve_class(tdata.object)
242
242
  tdata.mandatory.each_with_index do |o, index|
243
243
  next if context.website.config.option?(o)
244
- o = tdata.config_base + '.' + o
244
+ o = tdata.config_prefix + '.' + o
245
245
  if context.website.config.option?(o)
246
246
  tdata.mandatory[index] = o
247
247
  else