webgen 1.3.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +2 -2
  3. data/VERSION +1 -1
  4. data/data/webgen/passive_sources/stylesheets/api.css +2 -4
  5. data/data/webgen/passive_sources/templates/api.template +1 -1
  6. data/data/webgen/passive_sources/templates/feed.template +1 -1
  7. data/lib/webgen/cli.rb +67 -63
  8. data/lib/webgen/cli/commands/create.rb +4 -6
  9. data/lib/webgen/cli/commands/create_bundle.rb +8 -18
  10. data/lib/webgen/cli/commands/create_website.rb +15 -21
  11. data/lib/webgen/cli/commands/generate.rb +10 -16
  12. data/lib/webgen/cli/commands/install.rb +4 -6
  13. data/lib/webgen/cli/commands/show.rb +3 -3
  14. data/lib/webgen/cli/commands/show_bundles.rb +9 -13
  15. data/lib/webgen/cli/commands/show_config.rb +12 -18
  16. data/lib/webgen/cli/commands/show_dependencies.rb +7 -8
  17. data/lib/webgen/cli/commands/show_extensions.rb +10 -13
  18. data/lib/webgen/cli/commands/show_tree.rb +11 -16
  19. data/lib/webgen/cli/utils.rb +0 -18
  20. data/lib/webgen/content_processor.rb +2 -2
  21. data/lib/webgen/content_processor/sass.rb +6 -0
  22. data/lib/webgen/item_tracker/nodes.rb +8 -1
  23. data/lib/webgen/node_finder.rb +53 -31
  24. data/lib/webgen/path.rb +15 -3
  25. data/lib/webgen/path_handler/api.rb +23 -2
  26. data/lib/webgen/path_handler/page_utils.rb +5 -3
  27. data/lib/webgen/path_handler/sitemap.rb +1 -1
  28. data/lib/webgen/version.rb +1 -1
  29. data/test/webgen/path_handler/test_api.rb +24 -6
  30. data/test/webgen/path_handler/test_page_utils.rb +5 -0
  31. data/test/webgen/test_cli.rb +3 -3
  32. data/test/webgen/test_content_processor.rb +1 -0
  33. data/test/webgen/test_node_finder.rb +5 -4
  34. data/test/webgen/test_path.rb +14 -4
  35. metadata +52 -52
@@ -10,17 +10,15 @@ module Webgen
10
10
  class InstallCommand < CmdParse::Command
11
11
 
12
12
  def initialize # :nodoc:
13
- super('install', false, false, true)
14
- self.short_desc = 'Install an extension bundle'
15
- self.description = Utils.format_command_desc(<<DESC)
13
+ super('install', takes_commands: false)
14
+ short_desc('Install an extension bundle')
15
+ long_desc(<<DESC)
16
16
  Installs an extension bundle via Rubygems. You can either provide the name
17
17
  of a webgen extension bundle, the name of a Rubygem or a local file name.
18
18
  DESC
19
19
  end
20
20
 
21
- def execute(args) # :nodoc:
22
- raise CmdParse::InvalidArgumentError.new("Bundle name needed but none given") if args.length == 0
23
- name = args.first
21
+ def execute(name) # :nodoc:
24
22
  name = "webgen-#{name}-bundle" unless name =~ /\.gem$/ || name =~ /webgen-.*-bundle/
25
23
 
26
24
  inst = Gem::DependencyInstaller.new(:domain => :both, :force => false)
@@ -14,9 +14,9 @@ module Webgen
14
14
  class ShowCommand < CmdParse::Command
15
15
 
16
16
  def initialize # :nodoc:
17
- super('show', true, true, false)
18
- self.short_desc = 'Show various information about webgen or a website'
19
- self.description = Webgen::CLI::Utils.format_command_desc(<<DESC)
17
+ super('show', takes_commands: true)
18
+ short_desc('Show various information about webgen or a website')
19
+ long_desc(<<DESC)
20
20
  Groups various commands together that are used for showing information about webgen,
21
21
  like available extensions or configuration options. If a sub-command is invoked in the
22
22
  context of a webgen website, information about the website is also included.
@@ -9,9 +9,9 @@ module Webgen
9
9
  class ShowBundlesCommand < CmdParse::Command
10
10
 
11
11
  def initialize # :nodoc:
12
- super('bundles', false, false, true)
13
- self.short_desc = 'Show extension bundles'
14
- self.description = Utils.format_command_desc(<<DESC)
12
+ super('bundles', takes_commands: false)
13
+ short_desc('Show extension bundles')
14
+ long_desc(<<DESC)
15
15
  Shows all loaded, available and installable bundles.
16
16
 
17
17
  Loaded bundles are already used by the website, available ones are installed but
@@ -19,18 +19,14 @@ not used and installable bundles can be installed if needed.
19
19
 
20
20
  Hint: The global verbosity option enables additional output.
21
21
  DESC
22
- self.options = CmdParse::OptionParserWrapper.new do |opts|
23
- opts.separator "Options:"
24
- opts.on("-r", "--[no-]remote",
25
- *Utils.format_option_desc("Use remote server for listing installable bundles")) do |remote|
26
- @remote = remote
27
- end
22
+ options.on("-r", "--[no-]remote", "Use remote server for listing installable bundles") do |remote|
23
+ @remote = remote
28
24
  end
29
25
  @remote = false
30
26
  end
31
27
 
32
- def execute(args) # :nodoc:
33
- bundles = commandparser.website.ext.bundle_infos.bundles.dup
28
+ def execute # :nodoc:
29
+ bundles = command_parser.website.ext.bundle_infos.bundles.dup
34
30
  bundles.each {|n,d| d[:state] = :loaded}
35
31
 
36
32
  populate_hash = lambda do |file|
@@ -63,7 +59,7 @@ DESC
63
59
  next unless name_tuple.name =~ /webgen-(.*)-bundle/
64
60
  bundle_name = $1
65
61
  if !bundles.has_key?(bundle_name)
66
- bundles[bundle_name] = {:state => :installable, :gem => spec.first}
62
+ bundles[bundle_name] = {:state => :installable, :gem => name_tuple.name}
67
63
  end
68
64
  end
69
65
  end
@@ -89,7 +85,7 @@ DESC
89
85
  puts(Utils.light(Utils.blue(name)))
90
86
  puts(" State: #{data[:state]}")
91
87
  puts(" Rubygem: #{data[:gem]}") if data[:gem]
92
- if commandparser.verbose && data['author']
88
+ if command_parser.verbose && data['author']
93
89
  puts(" Author: #{data['author']}")
94
90
  print(" Summary: ")
95
91
  puts(Utils.format(data['summary'], 78, 12, false))
@@ -9,9 +9,9 @@ module Webgen
9
9
  class ShowConfigCommand < CmdParse::Command
10
10
 
11
11
  def initialize # :nodoc:
12
- super('config', false, false, true)
13
- self.short_desc = 'Show available configuration options'
14
- self.description = Utils.format_command_desc(<<DESC)
12
+ super('config', takes_commands: false)
13
+ short_desc('Show available configuration options')
14
+ long_desc(<<DESC)
15
15
  Shows all available configuration options. The option name and default value as
16
16
  well as the currently used value are displayed.
17
17
 
@@ -24,22 +24,17 @@ Hint: A debug message will appear at the top of the output if this command is ru
24
24
  in the context of a website, there are unknown configuration options in the
25
25
  configuration file and the log level is set to debug.
26
26
  DESC
27
- self.options = CmdParse::OptionParserWrapper.new do |opts|
28
- opts.separator "Options:"
29
- opts.on("-m", "--modified",
30
- *Utils.format_option_desc("Show modified configuration options only")) do |v|
27
+ options do |opts|
28
+ opts.on("-m", "--modified", "Show modified configuration options only") do |v|
31
29
  @modified = true
32
30
  end
33
- opts.on("-d", "--[no-]descriptions",
34
- *Utils.format_option_desc("Show descriptions")) do |d|
31
+ opts.on("-d", "--[no-]descriptions", "Show descriptions") do |d|
35
32
  @show_description = d
36
33
  end
37
- opts.on("-s", "--[no-]syntax",
38
- *Utils.format_option_desc("Show the syntax")) do |s|
34
+ opts.on("-s", "--[no-]syntax", "Show the syntax") do |s|
39
35
  @show_syntax = s
40
36
  end
41
- opts.on("-e", "--[no-]example",
42
- *Utils.format_option_desc("Show usage examples")) do |e|
37
+ opts.on("-e", "--[no-]example", "Show usage examples") do |e|
43
38
  @show_examples = e
44
39
  end
45
40
  end
@@ -49,12 +44,11 @@ DESC
49
44
  @show_examples = false
50
45
  end
51
46
 
52
- def execute(args) # :nodoc:
53
- @show_description = @show_syntax = @show_examples = true if commandparser.verbose
47
+ def execute(selector = '') # :nodoc:
48
+ @show_description = @show_syntax = @show_examples = true if command_parser.verbose
54
49
 
55
- config = commandparser.website.config
56
- descriptions = commandparser.website.ext.bundle_infos.options
57
- selector = args.first.to_s
50
+ config = command_parser.website.config
51
+ descriptions = command_parser.website.ext.bundle_infos.options
58
52
  config.options.select do |n, d|
59
53
  n.include?(selector) && (!@modified || config[n] != d.default)
60
54
  end.sort.each do |name, data|
@@ -9,9 +9,9 @@ module Webgen
9
9
  class ShowDependenciesCommand < CmdParse::Command
10
10
 
11
11
  def initialize # :nodoc:
12
- super('deps', false, false, true)
13
- self.short_desc = 'Show dependencies for all paths'
14
- self.description = Utils.format_command_desc(<<DESC)
12
+ super('deps', takes_commands: false)
13
+ short_desc('Show dependencies for all paths')
14
+ long_desc(<<DESC)
15
15
  Shows the dependencies (i.e. tracked items) for each path. This is only useful
16
16
  after webgen has generated the website at least once so that this information
17
17
  is available.
@@ -23,17 +23,16 @@ Hint: The global verbosity option enables additional output.
23
23
  DESC
24
24
  end
25
25
 
26
- def execute(args) # :nodoc:
27
- data = commandparser.website.ext.item_tracker.cached_items(commandparser.verbose)
26
+ def execute(selector = nil) # :nodoc:
27
+ data = command_parser.website.ext.item_tracker.cached_items(command_parser.verbose)
28
28
  if data.empty?
29
29
  puts "No data available, you need to generate the website first!"
30
30
  return
31
31
  end
32
- arg = args.shift
33
32
 
34
- data.select! {|alcn, _| alcn.include?(arg) } if arg
33
+ data.select! {|alcn, _| alcn.include?(selector) } if selector
35
34
  data.sort.each do |alcn, items|
36
- if items.length > 0 || commandparser.verbose
35
+ if items.length > 0 || command_parser.verbose
37
36
  puts("#{Utils.light(Utils.blue(alcn))}: ")
38
37
  items.each {|d| puts(" #{[d].flatten.join("\n ")}")}
39
38
  end
@@ -9,9 +9,9 @@ module Webgen
9
9
  class ShowExtensionsCommand < CmdParse::Command
10
10
 
11
11
  def initialize # :nodoc:
12
- super('extensions', false, false, true)
13
- self.short_desc = 'Show available extensions'
14
- self.description = Utils.format_command_desc(<<DESC)
12
+ super('extensions', takes_commands: false)
13
+ short_desc('Show available extensions')
14
+ long_desc(<<DESC)
15
15
  Shows all available extensions and additional information about them, e.g. a
16
16
  short summary of the functionality or the extension bundle it is defined in.
17
17
 
@@ -20,19 +20,16 @@ name are displayed.
20
20
 
21
21
  Hint: The global verbosity option enables additional output.
22
22
  DESC
23
- self.options = CmdParse::OptionParserWrapper.new do |opts|
24
- opts.separator "Options:"
25
- opts.on("-b NAME", "--bundle NAME", String,
26
- *Utils.format_option_desc("Only show extensions of this bundle")) do |bundle|
27
- @bundle = bundle
28
- end
23
+ options.on("-b NAME", "--bundle NAME", String, "Only show extensions of this bundle") do |bundle|
24
+ @bundle = bundle
29
25
  end
30
26
  @bundle = nil
31
27
  end
32
28
 
33
- def execute(args) # :nodoc:
34
- selector = args.first.to_s
35
- commandparser.website.ext.bundle_infos.extensions.select {|n, d| n.include?(selector)}.sort.each do |name, data|
29
+ def execute(selector = '') # :nodoc:
30
+ command_parser.website.ext.bundle_infos.extensions.select do |n, d|
31
+ n.include?(selector) && (@bundle.nil? || d['bundle'] == @bundle)
32
+ end.sort.each do |name, data|
36
33
  format_extension_info(name, data, !selector.empty?)
37
34
  end
38
35
  end
@@ -42,7 +39,7 @@ DESC
42
39
 
43
40
  indentation = (has_selector ? 0 : name.count('.')*2)
44
41
  puts(" "*indentation + Utils.light(Utils.blue(name)))
45
- if commandparser.verbose
42
+ if command_parser.verbose
46
43
  print(" "*(indentation + 2) + "Bundle: ")
47
44
  puts(Utils.format(data['bundle'], 78, indentation + 11, false))
48
45
  print(" "*(indentation + 2) + "Author: ")
@@ -9,9 +9,9 @@ module Webgen
9
9
  class ShowTreeCommand < CmdParse::Command
10
10
 
11
11
  def initialize # :nodoc:
12
- super('tree', false, false, true)
13
- self.short_desc = 'Show the node tree'
14
- self.description = Utils.format_command_desc(<<DESC)
12
+ super('tree', takes_commands: false)
13
+ short_desc('Show the node tree')
14
+ long_desc(<<DESC)
15
15
  Shows the internal representation of all destination paths that have been created
16
16
  from the source paths. Additionally, the meta information associated with each
17
17
  node can be shown as well.
@@ -28,18 +28,14 @@ are displayed.
28
28
 
29
29
  Hint: The global verbosity option enables additional output.
30
30
  DESC
31
- self.options = CmdParse::OptionParserWrapper.new do |opts|
32
- opts.separator "Options:"
33
- opts.on("-a", "--alcn",
34
- *Utils.format_option_desc("Use ALCN insted of LCN for paths")) do |v|
31
+ options do |opts|
32
+ opts.on("-a", "--alcn", "Use ALCN insted of LCN for paths") do |v|
35
33
  @use_alcn = true
36
34
  end
37
- opts.on("-f", "--[no-]fragments",
38
- *Utils.format_option_desc("Show fragment nodes (default: no)")) do |v|
35
+ opts.on("-f", "--[no-]fragments", "Show fragment nodes (default: no)") do |v|
39
36
  @show_fragments = v
40
37
  end
41
- opts.on("-m", "--[no-]meta-info",
42
- *Utils.format_option_desc("Show meta information (default: no)")) do |v|
38
+ opts.on("-m", "--[no-]meta-info", "Show meta information (default: no)") do |v|
43
39
  @meta_info = v
44
40
  end
45
41
  end
@@ -48,10 +44,9 @@ DESC
48
44
  @show_fragments = false
49
45
  end
50
46
 
51
- def execute(args) # :nodoc:
52
- selector = args.shift
53
- commandparser.website.ext.path_handler.populate_tree
54
- data = collect_data(commandparser.website.tree.dummy_root.children, selector)
47
+ def execute(selector = nil) # :nodoc:
48
+ command_parser.website.ext.path_handler.populate_tree
49
+ data = collect_data(command_parser.website.tree.dummy_root.children, selector)
55
50
  print_tree(data, selector)
56
51
  end
57
52
 
@@ -61,7 +56,7 @@ DESC
61
56
  if sub.length > 0 ||
62
57
  ((selector.nil? || node.alcn.include?(selector)) &&
63
58
  ((!node.is_fragment? || @show_fragments) &&
64
- (!node['passive'] || commandparser.website.ext.item_tracker.node_referenced?(node))))
59
+ (!node['passive'] || command_parser.website.ext.item_tracker.node_referenced?(node))))
65
60
  data = [@use_alcn ? node.alcn : node.lcn]
66
61
  data << node.alcn
67
62
  data << (@meta_info ? node.meta_info.map {|k,v| "#{k}: #{v.inspect}"} : [])
@@ -38,24 +38,6 @@ module Webgen
38
38
  end
39
39
  end
40
40
 
41
- # Format the command description.
42
- #
43
- # Returns an array of Strings.
44
- #
45
- # See Utils.format for more information.
46
- def self.format_command_desc(desc)
47
- format(desc, 76)
48
- end
49
-
50
- # Format the option description.
51
- #
52
- # Returns an array of Strings.
53
- #
54
- # See Utils.format for more information.
55
- def self.format_option_desc(desc)
56
- format(desc, 48)
57
- end
58
-
59
41
  # Return an array of lines which represents the text in +content+ formatted so that no line is
60
42
  # longer than +width+ characters.
61
43
  #
@@ -113,13 +113,13 @@ module Webgen
113
113
  # Normalize the content processor pipeline.
114
114
  #
115
115
  # The pipeline parameter can be a String in the format 'a,b,c' or 'a, b, c' or an array '[a, b,
116
- # c]' with content processors a, b and c.
116
+ # c]' with content processor names a, b and c.
117
117
  #
118
118
  # Raises an error if an unknown content processor is found.
119
119
  #
120
120
  # Returns an array with valid content processors.
121
121
  def normalize_pipeline(pipeline)
122
- pipeline = (pipeline.kind_of?(String) ? pipeline.split(/,\s*/) : pipeline)
122
+ pipeline = (pipeline.kind_of?(String) ? pipeline.split(/,\s*/) : pipeline.to_a)
123
123
  pipeline.each do |processor|
124
124
  raise Webgen::Error.new("Unknown content processor '#{processor}'") if !registered?(processor)
125
125
  end
@@ -34,6 +34,12 @@ module Webgen
34
34
  @context = context
35
35
  end
36
36
 
37
+ def marshal_dump #:nodoc:
38
+ end
39
+
40
+ def marshal_load(data) #:nodoc:
41
+ end
42
+
37
43
  # @see Base#find_relative
38
44
  def find_relative(name, base, options)
39
45
  _find(base, name, options)
@@ -62,7 +62,14 @@ module Webgen
62
62
  method, _, type = *iid
63
63
  str = (type == :content ? "Content" : "Meta info") << " from these nodes"
64
64
  str << " (result of #{[method].flatten.join('.')}):"
65
- [str] + data.flatten
65
+ data_mapper = lambda do |item|
66
+ if item.kind_of?(Array)
67
+ [item[0], item[1].map(&data_mapper).map {|inner| [inner].flatten.map {|l| " #{l}"}}]
68
+ else
69
+ item
70
+ end
71
+ end
72
+ [str] + data.map(&data_mapper).flatten
66
73
  end
67
74
 
68
75
  # Use Webgen::NodeFinder to generate a (nested) list of nodes. The options hash has to contain
@@ -34,8 +34,13 @@ module Webgen
34
34
  #
35
35
  # The +result.nodes+ accessor contains the array of nodes that should be manipulated in-place.
36
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!
37
+ # If a filter uses the reference node, it has to tell the node finder about it to allow proper
38
+ # caching:
39
+ #
40
+ # * If the node's language property is used, +result.lang_used+ has to be set to +true+.
41
+ # * If the node's hierarchy level is used, +result.level_used+ has to be set to +true+.
42
+ # * If the node's parent is used, +result.parent_node_used+ has to be set to +true+.
43
+ # * In all other cases, +result.ref_node_used+ has to be set to +true+
39
44
  #
40
45
  # Here is a sample filter module which provides the ability to filter nodes based on the meta
41
46
  # information key +category+. The +category+ key contains an array with one or more categories.
@@ -58,7 +63,14 @@ module Webgen
58
63
  # Result class used when filtering the nodes.
59
64
  #
60
65
  # The attribute +ref_node_used+ must not be set to +false+ once it is +true+!
61
- Result = Struct.new(:nodes, :ref_node_used)
66
+ Result = Struct.new(:nodes, :ref_node_used, :level_used, :lang_used, :parent_node_used) do
67
+ def merge_attrs!(result)
68
+ self.ref_node_used ||= result.ref_node_used
69
+ self.lang_used ||= result.lang_used
70
+ self.level_used ||= result.level_used
71
+ self.parent_node_used ||= result.parent_node_used
72
+ end
73
+ end
62
74
 
63
75
  # Create a new NodeFinder object for the given website.
64
76
  def initialize(website)
@@ -99,7 +111,7 @@ module Webgen
99
111
  # +ref_node+ is used as reference node.
100
112
  def find(opts_or_name, ref_node)
101
113
  if result = cached_result(opts_or_name, ref_node)
102
- return result
114
+ return result.nodes
103
115
  end
104
116
  opts = prepare_options_hash(opts_or_name)
105
117
 
@@ -107,20 +119,21 @@ module Webgen
107
119
  flatten = true if limit || offset
108
120
  levels = [levels || [1, 1_000_000]].flatten.map {|i| i.to_i}
109
121
 
110
- nodes, ref_node_used = filter_nodes(opts, ref_node)
122
+ result = filter_nodes(opts, ref_node)
123
+ nodes = result.nodes
111
124
 
112
125
  if flatten
113
126
  sort_nodes(nodes, sort, reverse)
114
127
  nodes = nodes[(offset.to_s.to_i)..(limit ? offset.to_s.to_i + limit.to_s.to_i - 1 : -1)] if limit || offset
115
128
  else
116
- result = {}
129
+ temp = {}
117
130
  min_level = 1_000_000
118
131
  nodes.each {|n| min_level = n.level if n.level < min_level}
119
132
 
120
133
  nodes.each do |n|
121
134
  hierarchy_nodes = []
122
135
  (hierarchy_nodes.unshift(n); n = n.parent) while n.level >= min_level
123
- hierarchy_nodes.inject(result) {|memo, hn| memo[hn] ||= {}}
136
+ hierarchy_nodes.inject(temp) {|memo, hn| memo[hn] ||= {}}
124
137
  end
125
138
 
126
139
  reducer = lambda do |h, level|
@@ -133,11 +146,13 @@ module Webgen
133
146
  h.map {|k,v| k}
134
147
  end
135
148
  end
136
- nodes = reducer.call(result, 1)
149
+ nodes = reducer.call(temp, 1)
137
150
  sort_nodes(nodes, sort, reverse, false)
138
151
  end
139
152
 
140
- cache_result(opts_or_name, ref_node, nodes, ref_node_used)
153
+ result.nodes = nodes
154
+ cache_result(opts_or_name, ref_node, result)
155
+ result.nodes
141
156
  end
142
157
 
143
158
  #######
@@ -145,15 +160,20 @@ module Webgen
145
160
  #######
146
161
 
147
162
  def cached_result(opts, ref_node)
148
- result_cache[opts] || result_cache[[opts, ref_node.alcn]]
163
+ (result = result_cache[opts]) && result_cache[cache_key(opts, ref_node, result)]
149
164
  end
150
165
 
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
166
+ def cache_result(opts, ref_node, result)
167
+ result_cache[opts] = result
168
+ result_cache[cache_key(opts, ref_node, result)] = result
169
+ end
170
+
171
+ def cache_key(opts, ref_node, result)
172
+ [opts,
173
+ result.ref_node_used && ref_node.alcn,
174
+ result.lang_used && ref_node.lang,
175
+ result.level_used && ref_node.level,
176
+ result.parent_node_used && ref_node.parent.alcn]
157
177
  end
158
178
 
159
179
  def result_cache
@@ -189,7 +209,7 @@ module Webgen
189
209
  end
190
210
  end
191
211
 
192
- [result.nodes, result.ref_node_used]
212
+ result
193
213
  end
194
214
 
195
215
  def sort_nodes(nodes, sort, reverse, flat_mode = true)
@@ -217,9 +237,9 @@ module Webgen
217
237
  [opts].flatten.each do |cur_opts|
218
238
  cur_opts = prepare_options_hash(cur_opts)
219
239
  remove_non_filter_options(cur_opts)
220
- nodes, ref_node_used = filter_nodes(cur_opts, ref_node)
221
- result.nodes &= nodes
222
- result.ref_node_used |= ref_node_used
240
+ inner_result = filter_nodes(cur_opts, ref_node)
241
+ result.nodes &= inner_result.nodes
242
+ result.merge_attrs!(inner_result)
223
243
  end
224
244
  end
225
245
 
@@ -227,9 +247,9 @@ module Webgen
227
247
  [opts].flatten.each do |cur_opts|
228
248
  cur_opts = prepare_options_hash(cur_opts)
229
249
  remove_non_filter_options(cur_opts)
230
- nodes, ref_node_used = filter_nodes(cur_opts, ref_node)
231
- result.nodes |= nodes
232
- result.ref_node_used |= ref_node_used
250
+ inner_result = filter_nodes(cur_opts, ref_node)
251
+ result.nodes |= inner_result.nodes
252
+ result.merge_attrs!(inner_result)
233
253
  end
234
254
  end
235
255
 
@@ -237,9 +257,9 @@ module Webgen
237
257
  [opts].flatten.each do |cur_opts|
238
258
  cur_opts = prepare_options_hash(cur_opts)
239
259
  remove_non_filter_options(cur_opts)
240
- nodes, ref_node_used = filter_nodes(cur_opts, ref_node)
241
- result.nodes -= nodes
242
- result.ref_node_used |= ref_node_used
260
+ inner_result = filter_nodes(cur_opts, ref_node)
261
+ result.nodes -= inner_result.nodes
262
+ result.merge_attrs!(inner_result)
243
263
  end
244
264
  end
245
265
 
@@ -248,15 +268,17 @@ module Webgen
248
268
  end
249
269
 
250
270
  def filter_alcn(result, ref_node, alcn)
251
- result.ref_node_used = true
252
- alcn = [alcn].flatten.map {|a| Webgen::Path.append(ref_node.alcn, a.to_s)}
271
+ alcn = [alcn].flatten.map do |a|
272
+ result.ref_node_used = true unless a.to_s.start_with?('/')
273
+ Webgen::Path.append(ref_node.alcn, a.to_s)
274
+ end
253
275
  result.nodes.keep_if {|n| alcn.any? {|a| n =~ a}}
254
276
  end
255
277
 
256
278
  def filter_absolute_levels(result, ref_node, range)
257
279
  range = [range].flatten.map do |i|
258
280
  if (i = i.to_i) < 0
259
- result.ref_node_used = true
281
+ result.level_used = true
260
282
  ref_node.level + 1 + i
261
283
  else
262
284
  i
@@ -268,7 +290,7 @@ module Webgen
268
290
  def filter_lang(result, ref_node, langs)
269
291
  langs = [langs].flatten.map do |l|
270
292
  if l == 'node'
271
- result.ref_node_used = true
293
+ result.lang_used = true
272
294
  ref_node.lang
273
295
  else
274
296
  l
@@ -306,7 +328,7 @@ module Webgen
306
328
 
307
329
  def filter_siblings(result, ref_node, value)
308
330
  return unless value
309
- result.ref_node_used = true
331
+ result.parent_node_used = true
310
332
 
311
333
  if value == true
312
334
  result.nodes.keep_if {|n| n.parent == ref_node.parent}