Class: Webgen::ItemTracker
- Inherits:
-
Object
- Object
- Webgen::ItemTracker
- Includes:
- ExtensionManager
- Defined in:
- lib/webgen/item_tracker.rb,
lib/webgen/item_tracker/file.rb,
lib/webgen/item_tracker/nodes.rb,
lib/webgen/item_tracker/missing_node.rb,
lib/webgen/item_tracker/node_content.rb,
lib/webgen/item_tracker/node_meta_info.rb,
lib/webgen/item_tracker/template_chain.rb
Overview
Namespace for all item trackers.
About
This extension manager class is used to track various “items”. Such items can be added as a dependency to a node and later be checked if they have changed. This allows webgen to conditionally render a node.
An item can basically be anything, there only has to be an item tracker extension that knows how to handle it. Each item tracker extension is uniquely identified by its name (e.g. :node_content, :node_meta_info, …).
Implementing an item tracker.
An item tracker extension class must respond to the following methods:
- initialize(website)
-
Initializes the extension and provides the website object which can be used to resolve the item ID to the referenced item or item data itself.
- item_id(*item)
-
Return the unique ID for the given item. The returned ID has to be unique for this item tracker extension
- item_data(*item)
-
Return the data for the item so that it can be correctly checked later if it has changed. If data for the given item cannot be computer anymore because the item got invalid, raise an exception.
- item_changed?(item_id, old_data)
-
Return
trueif the item identified by its unique ID has changed. The parameterold_datacontains the last known data of the item. - node_referenced?(item_id, old_data, node_alcn)
-
Return
trueif the node identified bynode_alcnis referenced in the old data identified by the unique ID. - item_description(item_id, data)
-
Return a string or an array of strings which describe the item (identified by its unique ID) and the given item data. This is used for display purposes and should therefore include a nice, human readable interpretation of the item.
The parameter item for the methods item_id and item_data contains the information needed to identify the item and is depdendent on the specific item tracker extension class. Therefore you need to look at the documentation for an item tracker extension to see what it expects as the item.
Since these methods are invoked multiple times for different items, these methods should have no side effects.
Sample item tracker
The following sample item tracker tracks changes in configuration values. It needs the configuration option name as item.
class ConfigTracker
def initialize(website)
@website = website
end
def item_id(config_key)
config_key
end
def item_data(config_key)
@website.config[config_key]
end
def item_changed?(config_key, old_val)
@website.config[config_key] != old_val
end
def node_referenced?(config_key, config_value, node_alcn)
false
end
def item_description(config_key, old_val)
"The website configuration option '#{config_key}'"
end
end
website.ext.item_tracker.register ConfigTracker, name: :config
Defined Under Namespace
Classes: File, MissingNode, NodeContent, NodeMetaInfo, Nodes, TemplateChain
Instance Method Summary collapse
-
#add(node, name, *item) ⇒ Object
Add the given item that is handled by the item tracker extension
nameas a dependency to the node. -
#cached_items(include_default = true) ⇒ Object
Return a hash with mappings from node ALCNs to their used items (items are converted to a human readable representation by using the #item_description method).
-
#initialize(website) ⇒ ItemTracker
constructor
Create a new item tracker for the given website.
-
#item_changed?(name, *item) ⇒ Boolean
Return
trueif the given item that is handled by the item tracker extensionnamehas changed. -
#node_changed?(node) ⇒ Boolean
Return
trueif the given node has changed. -
#node_referenced?(node) ⇒ Boolean
Return
trueif the given node has been referenced by any item tracker extension. -
#register(klass, options = {}, &block) ⇒ Object
Register an item tracker.
Methods included from ExtensionManager
#initialize_copy, #registered?, #registered_extensions
Constructor Details
#initialize(website) ⇒ ItemTracker
Create a new item tracker for the given website.
98 99 100 101 102 103 104 105 106 107 108 109 110 111 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 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/webgen/item_tracker.rb', line 98 def initialize(website) super() @instances = {} @node_dependencies = Hash.new {|h,k| h[k] = Set.new} @item_data = {} @cached = {:node_dependencies => {}, :item_data => {}} @written_nodes = [] @checked_nodes = Set.new @website = website @item_changed = {} @website.blackboard.add_listener(:website_initialized, 'item_tracker') do @cached = @website.cache[:item_tracker_data] || @cached end @website.blackboard.add_listener(:after_tree_populated, 'item_tracker') do |node| @item_data.keys.each do |uid| @item_data[uid] = item_tracker(uid.first).item_data(*uid.last) end end @website.blackboard.add_listener(:before_all_nodes_written, 'item_tracker') do # make all used item data from the previous pass current again if applicable and remove # invalid UIDs uids_to_update = @written_nodes.each_with_object(Set.new) do |node, set| next unless @website.tree[node.alcn] set.merge(@node_dependencies[node.alcn]) end uids_to_update.each do |uid| begin @item_data[uid] = item_tracker(uid.first).item_data(*uid.last) rescue Exception @item_data.delete(uid) @cached[:item_data].delete(uid) @written_nodes.each do |node| if @node_dependencies[node.alcn].include?(uid) @node_dependencies[node.alcn].delete(uid) @cached[:node_dependencies][node.alcn].delete(uid) node.node_info[:item_tracker_changed_once] = true end end end end @written_nodes = [] @item_changed = {} end @website.blackboard.add_listener(:after_node_written, 'item_tracker') do |node| @written_nodes << node end @website.blackboard.add_listener(:after_all_nodes_written, 'item_tracker') do # update cached data with data from the run uids_to_update = Set.new @written_nodes.each do |node| next unless @website.tree[node.alcn] node.node_info.delete(:item_tracker_changed_once) @cached[:node_dependencies][node.alcn] = @node_dependencies[node.alcn] uids_to_update.merge(@node_dependencies[node.alcn]) end uids_to_update.each {|uid| @cached[:item_data][uid] = @item_data[uid]} end @website.blackboard.add_listener(:website_generated, 'item_tracker') do @cached[:node_dependencies].reject! {|alcn, data| !@website.tree[alcn]} used_uids = @cached[:node_dependencies].each_with_object(Set.new) {|(_, uids), obj| obj.merge(uids)} @cached[:item_data].merge!(@item_data) @cached[:item_data].reject! {|uid, _| !used_uids.include?(uid)} @website.cache[:item_tracker_data] = @cached end end |
Instance Method Details
#add(node, name, *item) ⇒ Object
Add the given item that is handled by the item tracker extension name as a dependency to the node.
202 203 204 205 206 |
# File 'lib/webgen/item_tracker.rb', line 202 def add(node, name, *item) uid = unique_id(name, item) @node_dependencies[node.alcn] << uid @item_data[uid] ||= item_tracker(name).item_data(*uid.last) end |
#cached_items(include_default = true) ⇒ Object
Return a hash with mappings from node ALCNs to their used items (items are converted to a human readable representation by using the #item_description method).
If the parameter include_default is set to false, the default items added to each node so that it is possible to correctly detect changes, are not included.
The cached data, not the current data, is used. So this information is only useful after a website has been generated.
247 248 249 250 251 252 253 254 255 |
# File 'lib/webgen/item_tracker.rb', line 247 def cached_items(include_default=true) @cached[:node_dependencies].each_with_object({}) do |(alcn, uids), h| h[alcn] = uids.sort {|a,b| a.first <=> b.first }.map do |uid| next if !include_default && ((uid.first == :node_meta_info && uid.last.first == alcn )|| (uid.first == :node_content && uid.last == alcn)) item_tracker(uid.first).item_description(uid.last, @cached[:item_data][uid]) end.compact end end |
#item_changed?(name, *item) ⇒ Boolean
Return true if the given item that is handled by the item tracker extension name has changed.
235 236 237 |
# File 'lib/webgen/item_tracker.rb', line 235 def item_changed?(name, *item) item_changed_by_uid?(unique_id(name, item)) end |
#node_changed?(node) ⇒ Boolean
Return true if the given node has changed.
209 210 211 212 213 214 215 216 217 |
# File 'lib/webgen/item_tracker.rb', line 209 def node_changed?(node) return false if @checked_nodes.include?(node) @checked_nodes << node node.node_info[:item_tracker_changed_once] || !@cached[:node_dependencies].has_key?(node.alcn) || @cached[:node_dependencies][node.alcn].any? {|uid| item_changed_by_uid?(uid)} ensure @checked_nodes.delete(node) end |
#node_referenced?(node) ⇒ Boolean
Return true if the given node has been referenced by any item tracker extension.
220 221 222 223 224 225 226 227 228 229 230 231 |
# File 'lib/webgen/item_tracker.rb', line 220 def node_referenced?(node) if (cached = @website.cache.volatile[:item_tracker_referenced_nodes]).nil? cached = @website.cache.volatile[:item_tracker_referenced_nodes] = Set.new @cached[:node_dependencies].each do |alcn, uids| uids.each do |uid| cached.merge(item_tracker(uid.first).referenced_nodes(uid.last, @cached[:item_data][uid]) - [alcn]) end end end cached.include?(node.alcn) end |
#register(klass, options = {}, &block) ⇒ Object
Register an item tracker.
The parameter klass has to contain the name of the item tracker class or the class object itself. If the class is located under this namespace, only the class name without the hierarchy part is needed, otherwise the full class name including parent module/class names is needed.
Options:
- :name
-
The name for the item tracker class. If not set, it defaults to the snake-case version (i.e. FileSystem → file_system) of the class name (without the hierarchy part). It should only contain letters.
Examples:
item_tracker.register('Node') # registers Webgen::ItemTracker::Node
item_tracker.register('::Node') # registers Node !!!
item_tracker.register('MyModule::Doit', name: 'infos')
196 197 198 |
# File 'lib/webgen/item_tracker.rb', line 196 def register(klass, ={}, &block) do_register(klass, , false, &block) end |