class Webgen::Tag

Namespace for all webgen tags.

About ↑

A tag object is a webgen extension that handles specific webgen tags. webgen tags are used to add dynamic content to page and template files (or any other file for that matter) and are made for ease of use.

Implementing a tag ↑

A tag object only needs to respond to the method call which needs to accept three parameters:

tag

The name of the tag which should be processed (useful for tag objects which can process different tags).

body

Holds the body value for the tag if any.

context

Holds all relevant information for processing – have a look at the Webgen::Context class to see what is available. The special key :config is set to an Webgen::Configuration object that should be used to retrieve configuration option values because the values might be changed due to options set directly via the tag syntax.

The method has to return the result of the processing and, optionally, a boolean value specifying if the result should further be processed (ie. webgen tags replaced).

This allows one to implement a tag object as a class with a class method called call. Or as a Proc object.

The tag object has to be registered so that webgen knows about it, see register for more information.

Tag options ↑

webgen tags allow the specification of options in the tag definition. When registering a tag, one can specify which options are mandatory, i.e. which options always have to be set directly for the tag. The value of the option :config_prefix for the register method is used to resolve partially stated configuration entries.

Sample Tag ↑

Following is a simple tag class example which just reverses the body text and adds some information about the context to the result.

Put the following into the ext/init.rb file of your webgen website:

class Reverser

  def self.call(tag, body, context)
    result = context[:config]['tag.reverser.do_reverse'] ? body.reverse : body
    result << "\nNode: " << context.content_node.alcn << " (" << context.content_node['title'] << ")"
    result << "\nReference node: " << context.ref_node.alcn
    result
  end

end

website.config.define_option('tag.reverser.do_reverse', nil)
website.ext.tag.register(Reverser, :names => 'reverse',
                         :config_prefix => 'tag.reverser',
                         :mandatory => ['do_reverse'])

Then you can use the reverser tag as follows in a page file:

{reverse:: {do_reverse: true}}This text is reversed{reverse}

Public Class Methods

render_tag_template(context, tag) click to toggle source

Render the tag template for the given tag and return the result.

The value of the configuration option 'tag.<TAG>.template' (where '<TAG>' is replaced with tag) is used as template path.

If the template node cannot be found, an empty string is returned.

# File lib/webgen/tag.rb, line 84
def self.render_tag_template(context, tag)
  path = context[:config]["tag.#{tag}.template"]
  if path && template_node = context.ref_node.resolve(path, context.dest_node.lang, true)
    context.website.ext.item_tracker.add(context.dest_node, :template_chain, template_node)
    context.render_block(:name => "tag.#{tag}", :node => 'first',
                         :chain => [*template_node.template_chain, template_node, context.content_node])
  else
    context.website.logger.warn { "Template node '#{path}' for tag '#{tag}' not found" }
    ''
  end
end

Public Instance Methods

call(tag, params, body, context) click to toggle source

Process the tag and return the result.

The parameter params (can be a Hash, a String or nil) needs to contain the parameters for the tag and body is the optional body for the tag. context needs to be a valid Webgen::Context object.

# File lib/webgen/tag.rb, line 163
def call(tag, params, body, context)
  result = ''
  tdata = tag_data(tag, context)
  if !tdata.nil?
    context = context.clone(:config => create_config(tag, params, tdata, context))
    result, process_output = tdata.object.call(tag, body, context)
    if process_output
      context.content = result
      result = context.website.ext.content_processor.call('tags', context).content
    end
  else
    raise Webgen::RenderError.new("No tag processor for '#{tag}' found", 'tag',
                                  context.dest_node, context.ref_node)
  end
  result
rescue Webgen::Error => e
  e.path = context.dest_node if e.path.to_s.empty?
  e.location = "tag.#{tag}" unless e.location
  raise
rescue Exception => e
  raise Webgen::RenderError.new(e, "tag.#{tag}", context.dest_node, context.ref_node)
end
register(klass, options = {}, &block) click to toggle source

Register a tag.

The parameter klass can either be a String containing the name of a class/module (which has to respond to :call) or an object that responds to :call. If the class is located under this namespace, only the class name without the hierarchy part is needed, otherwise the full class/module name including parent module/class names is needed.

Instead of registering an object that responds to :call, you can also provide a block that processes a tag.

Options: ↑

:names

The tag name or an array of tag names. If not set, it defaults to the lowercase version of the class name (without the hierarchy part).

The name :default is used for specifying the default tag which is called if an unknown tag name is encountered.

:config_prefix

The configuration prefix, i.e. the part of a configuration option name that does not need to be specified. Defaults to the full class name without the Webgen module downcased and all “::” substituted with “.” (e.g. Webgen::Tag::Menu → tag.menu). Needs to be specified when a block is used!

:mandatory

A list of configuration option names whose values always need to be provided. The first configuration option name is used as the default mandatory option (used when only a string is provided in the tag definition).

Examples: ↑

tag.register('Date')    # registers Webgen::Tag::Date

tag.register('::Date')  # registers Date !!!

tag.register('MyModule::Date', names: ['mydate', 'date'])

tag.register('date', config_prefix: 'tag.date') do |tag, body, context|
  Time.now.strftime(param('tag.date.format'))
end
# File lib/webgen/tag.rb, line 143
def register(klass, options = {}, &block)
  if block_given? && !options[:config_prefix]
    raise ArgumentError, "The option :config_prefix needs to be specified when registering a tag using a block"
  end

  names = [options.delete(:names)].flatten.compact
  options[:name] = names.shift
  name = do_register(klass, options, true, &block)
  ext_data(name).mandatory = options[:mandatory] || []
  ext_data(name).config_prefix = options[:config_prefix] ||
    Webgen::Utils.snake_case(ext_data(name).object.to_s.gsub(/::/, '.').gsub(/^Webgen\./, ''))
  ext_data(name).initialized = false
  names.each {|n| @extensions[n.to_sym] = @extensions[name]}
end
replace_tags(str) { |tag_name, params, body| ... } click to toggle source

See Webgen::Utils::TagParser#replace_tags.

# File lib/webgen/tag.rb, line 187
def replace_tags(str, &block)  #:yields: tag_name, params, body
  @parser.replace_tags(str, &block)
end

Private Instance Methods

create_config(tag, params, tdata, context) click to toggle source

Create the Webgen::Configuration object from the parameters and the given configuration prefix.

# File lib/webgen/tag.rb, line 197
def create_config(tag, params, tdata, context)
  values = case params
           when Hash
             values_from_hash(tag, params, tdata, context)
           when String, Array, TrueClass, FalseClass, Numeric
             values_for_default_mandatory(tag, params, tdata, context)
           when NilClass then {}
           else
             raise Webgen::RenderError.new("Invalid parameter type (#{params.class})",
                                           "tag", context.dest_node, context.ref_node)
           end

  if !tdata.mandatory.all? {|k| values.has_key?(k)}
    raise Webgen::RenderError.new("Not all mandatory parameters set", "tag", context.dest_node, context.ref_node)
  end
  config = context.website.config.dup
  config.set_values(values)
  config.freeze
  config
end
tag_data(tag, context) click to toggle source

Return the tag data for tag or nil if tag is unknown.

# File lib/webgen/tag.rb, line 251
def tag_data(tag, context)
  tdata = @extensions[tag.to_sym] || @extensions[:default]
  if tdata && !tdata.initialized
    tdata.object = resolve_class(tdata.object)
    tdata.mandatory.each_with_index do |o, index|
      next if context.website.config.option?(o)
      o = tdata.config_prefix + '.' + o
      if context.website.config.option?(o)
        tdata.mandatory[index] = o
      else
        raise ArgumentError, "Invalid configuration option name '#{o}' specified as mandatory option for tag '#{tag}'"
      end
    end
    tdata.initialized = true
  end
  tdata
end
values_for_default_mandatory(tag, value, tdata, context) click to toggle source

Return a hash containing valid configuration options by setting the default mandatory parameter for tag to value.

# File lib/webgen/tag.rb, line 238
def values_for_default_mandatory(tag, value, tdata, context)
  if tdata.mandatory.first.nil?
    context.website.logger.error do
      ["No default mandatory option specified for tag '#{tag}' but set in <#{context.ref_node}>",
       "Use the {key: value} syntax for assigning the value '#{value}' to the intended tag option!"]
    end
    {}
  else
    {tdata.mandatory.first => value}
  end
end
values_from_hash(tag, params, tdata, context) click to toggle source

Return a hash containing valid configuration options by taking key-value pairs from params.

# File lib/webgen/tag.rb, line 219
def values_from_hash(tag, params, tdata, context)
  result = {}
  params.each do |key, value|
    if context.website.config.option?(key)
      result[key] = value
    elsif context.website.config.option?(tdata.config_prefix + '.' + key)
      result[tdata.config_prefix + '.' + key] = value
    else
      context.website.logger.warn do
        ["Invalid configuration option '#{key}' for tag '#{tag}' found in <#{context.ref_node}>",
         "Remove the invalid key '#{key}' to fix the warning."]
      end
    end
  end
  result
end