Class: Webgen::NodeFinder
- Inherits:
-
Object
- Object
- Webgen::NodeFinder
- Defined in:
- lib/webgen/node_finder.rb
Overview
Used for finding nodes that match certain criterias.
About
This extension class is used for finding nodes that match certain criterias (all nodes are used if no filter options are specified) when calling the #find method. There are some built-in filters but one can also provide custom filters via #add_filter_module.
The found nodes are either returned in a flat list or hierarchical in nested lists (if a node has no child nodes, only the node itself is used; otherwise a two element array containing the node and child nodes is used). Sorting, limiting the number of returned nodes and using an offset are also possible.
Note that results are cached in the volatile cache of the Cache instance!
Finder options
A complete list of the supported finder options can be found in the user documentation! Note that there may also be other 3rd party node filters available if you are using extension bundles!
Implementing a filter module
Implementing a filter module is very easy. Just create a module that contains your filter methods and tell the NodeFinder object about it using the #add_filter_module method. A filter method needs to take three arguments: the Result stucture, the reference node and the filter value.
The result.nodes
accessor contains the array of nodes that should be manipulated in-place.
If a filter uses the reference node, it has to tell the node finder about it to allow proper caching:
-
If the node’s language property is used,
result.lang_used
has to be set totrue
. -
If the node’s hierarchy level is used,
result.level_used
has to be set totrue
. -
If the node’s parent is used,
result.parent_node_used
has to be set totrue
. -
In all other cases,
result.ref_node_used
has to be set totrue
Here is a sample filter module which provides the ability to filter nodes based on the meta information key category
. The category
key contains an array with one or more categories. The value for this category filter is one or more strings and the filter returns those nodes that contain at least one specified category.
module CategoryFilter
def filter_on_category(result, ref_node, categories)
categories = [categories].flatten # needed in case categories is a string
result.nodes.select! {|n| categories.any? {|c| n['category'].include?(c)}}
end
end
website.ext.node_finder.add_filter_module(CategoryFilter, category: 'filter_on_category')
Defined Under Namespace
Classes: Result
Instance Method Summary collapse
-
#add_filter_module(mod, mapping) ⇒ Object
Add a module with filter methods.
-
#find(opts_or_name, ref_node) ⇒ Object
Return all nodes that match certain criterias.
-
#initialize(website) ⇒ NodeFinder
constructor
Create a new NodeFinder object for the given website.
Constructor Details
#initialize(website) ⇒ NodeFinder
Create a new NodeFinder object for the given website.
76 77 78 79 80 81 82 83 84 85 |
# File 'lib/webgen/node_finder.rb', line 76 def initialize(website) @website = website @mapping = { :alcn => :filter_alcn, :absolute_levels => :filter_absolute_levels, :lang => :filter_lang, :and => :filter_and, :or => :filter_or, :not => :filter_not, :ancestors => :filter_ancestors, :descendants => :filter_descendants, :siblings => :filter_siblings, :mi => :filter_meta_info } end |
Instance Method Details
#add_filter_module(mod, mapping) ⇒ Object
Add a module with filter methods.
The parameter mapping
needs to be a hash associating unique names with the methods of the given module that can be used as finder methods.
Examples:
node_finder.add_filter_module(MyModule, blog: 'filter_on_blog')
96 97 98 99 100 101 102 103 104 105 |
# File 'lib/webgen/node_finder.rb', line 96 def add_filter_module(mod, mapping) public_methods = mod.public_instance_methods.map {|c| c.to_s} mapping.each do |name, method| if !public_methods.include?(method.to_s) raise ArgumentError, "Finder method '#{method}' not found in module #{mod}" end @mapping[name.intern] = method end extend(mod) end |
#find(opts_or_name, ref_node) ⇒ Object
Return all nodes that match certain criterias.
The parameter opts_or_name
can either be a hash with finder options or the name of a finder option set defined using the configuration option ‘node_finder.options_sets’. The node ref_node
is used as reference node.
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/webgen/node_finder.rb', line 112 def find(opts_or_name, ref_node) if result = cached_result(opts_or_name, ref_node) return result.nodes end opts = (opts_or_name) limit, offset, flatten, sort, levels, reverse = (opts) flatten = true if limit || offset levels = [levels || [1, 1_000_000]].flatten.map {|i| i.to_i} result = filter_nodes(opts, ref_node) nodes = result.nodes if flatten sort_nodes(nodes, sort, reverse) nodes = nodes[(offset.to_s.to_i)..(limit ? offset.to_s.to_i + limit.to_s.to_i - 1 : -1)] if limit || offset else temp = {} min_level = 1_000_000 nodes.each {|n| min_level = n.level if n.level < min_level} nodes.each do |n| hierarchy_nodes = [] (hierarchy_nodes.unshift(n); n = n.parent) while n.level >= min_level hierarchy_nodes.inject(temp) {|memo, hn| memo[hn] ||= {}} end reducer = lambda do |h, level| if level < levels.first temp = h.map {|k,v| v.empty? ? nil : reducer.call(v, level + 1)}.compact temp.length == 1 && temp.first.kind_of?(Array) ? temp.first : temp elsif level < levels.last h.map {|k,v| v.empty? ? k : [k, reducer.call(v, level + 1)]} else h.map {|k,v| k} end end nodes = reducer.call(temp, 1) sort_nodes(nodes, sort, reverse, false) end result.nodes = nodes cache_result(opts_or_name, ref_node, result) result.nodes end |