webgen 1.0.0.beta1 → 1.0.0.beta2

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