# -*- encoding: utf-8 -*- require 'yaml' require 'webgen/loggable' require 'webgen/websiteaccess' # This module should be mixed into any class that wants to serve as a webgen tag class. Have a look # a the example below to see how a basic tag class looks like. # # == Tag classes # # A tag class is a webgen extension that handles specific webgen tags. webgen tags are used to add # dynamic content to page and template files and are made for ease of use. # # A tag class can handle multiple different tags. Just add a (tag name)-(class name) pair to the # contentprocessor.tags.map configuration entry for each tag name you want to associate # with the tag class. The special name :default is used for the default tag class which is # called if a tag with an unknown tag name is encountered. # # The only method needed to be written is +call+ which is called by the tags content processor to # the actual processing. And the +initialize+ method must not take any parameters! # # Tag classes *can* also choose to not use this module. If they don't use it they have to provide # the following methods: +set_params+, +create_tag_params+, +call+. # # == Tag parameters # # webgen tags allow the specification of parameters in the tag definition. The method # +tag_params_list+ returns all configuration entries that can be set this way. And the method # +tag_config_base+ is used to resolve partially stated configuration entries. The default method # uses the full class name, strips a Webgen:: part at the beginning away, substitutes # . for :: and makes everything lowercase. # # An additional configuration entry option is also used: :mandatory. If this key is set to # +true+ for a configuration entry, the entry counts as mandatory and needs to be set in the tag # definition. If this key is set to +default+, this means that this entry should be the default # mandatory parameter (used when only a string is provided in the tag definition). There *should* be # only one default mandatory parameter. # # == Sample Tag Class # # Following is a simple tag class example which just reverses the body text and adds some # information about the context to the result. Note that the class does not reside in the # Webgen::Tag namespace and that the configuration entry is therefore also not under the # tag. namespace. # # class Reverser # # include Webgen::Tag::Base # # def call(tag, body, context) # result = param('do_reverse') ? body.reverse : body # result += "Node: " + context.content_node.absolute_lcn + " (" + context.content_node['title'] + ")" # result += "Reference node: " + context.ref_node.absolute_lcn # result # end # # end # # WebsiteAccess.website.config.reverser.do_reverse nil, :mandatory => default # WebsiteAccess.website.config['contentprocessor.tags.map']['reverse'] = 'Reverser' # module Webgen::Tag::Base include Webgen::Loggable include Webgen::WebsiteAccess # Return a hash with parameter values extracted from the string +tag_config+. def create_tag_params(tag_config, ref_node) begin config = YAML::load("--- #{tag_config}") rescue ArgumentError => e log(:error) { "Could not parse the tag params '#{tag_config}' in <#{ref_node.absolute_lcn}>: #{e.message}" } config = {} end create_params_hash(config, ref_node) end # Set the current parameter configuration to +params+. def set_params(params) @params = params end # Retrieve the parameter value for +name+. The value is taken from the current parameter # configuration if the parameter is specified there or from the website configuration otherwise. def param(name) (@params && @params.has_key?(name) ? @params[name] : website.config[name]) end # Default implementation for processing a tag. The parameter +tag+ specifies the name of the tag # which should be processed (useful for tag classes which process different tags). # # The parameter +body+ holds the optional body value for the tag. # # The +context+ parameter holds all relevant information for processing. Have a look at the # Webgen::Context class to see what is available. # # The method has to return the result of the tag processing and, optionally, a boolean value # specifying if the result should further be processed (ie. webgen tags replaced). # # Needs to be redefined by classes that mixin this module! def call(tag, body, context) raise NotImplementedError end ####### private ####### # The base part of the configuration name. This isthe class name without the Webgen module # downcased and all "::" substituted with "." (e.g. Webgen::Tag::Menu -> tag.menu). By overriding # this method one can provide a different way of specifying the base part of the configuration # name. def tag_config_base self.class.name.gsub('::', '.').gsub(/^Webgen\./, '').downcase end # Return the list of all parameters for the tag class. All configuration options starting with # +tag_config_base+ are used. def tag_params_list regexp = /^#{tag_config_base}/ website.config.data.keys.select {|key| key =~ regexp} end # Create the parameter hash from +config+ which needs to be a Hash, a String or +nil+. def create_params_hash(config, node) params = tag_params_list result = case config when Hash then create_from_hash(config, params, node) when String then create_from_string(config, params, node) when NilClass then {} else log(:error) { "Invalid parameter type (#{config.class}) for tag '#{self.class.name}' in <#{node.absolute_lcn}>" } {} end unless params.all? {|k| !website.config.meta_info[k][:mandatory] || result.has_key?(k)} log(:error) { "Not all mandatory parameters for tag '#{self.class.name}' in <#{node.absolute_lcn}> set" } end result end # Return a valid parameter hash taking values from +config+ which has to be a Hash. def create_from_hash(config, params, node) result = {} config.each do |key, value| if params.include?(key) result[key] = value elsif params.include?(tag_config_base + '.' + key) result[tag_config_base + '.' + key] = value else log(:warn) { "Invalid parameter '#{key}' for tag '#{self.class.name}' in <#{node.absolute_lcn}>" } end end result end # Return a valid parameter hash by setting +value+ to the default mandatory parameter. def create_from_string(value, params, node) param_name = params.find {|k| website.config.meta_info[k][:mandatory] == 'default'} if param_name.nil? log(:error) { "No default mandatory parameter specified for tag '#{self.class.name}' but set in <#{node.absolute_lcn}>"} {} else {param_name => value} end end end