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
84 def self.render_tag_template(context, tag)
85   path = context[:config]["tag.#{tag}.template"]
86   if path && template_node = context.ref_node.resolve(path, context.dest_node.lang, true)
87     context.website.ext.item_tracker.add(context.dest_node, :template_chain, template_node)
88     context.render_block(:name => "tag.#{tag}", :node => 'first',
89                          :chain => [*template_node.template_chain, template_node, context.content_node])
90   else
91     context.website.logger.warn { "Template node '#{path}' for tag '#{tag}' not found" }
92     ''
93   end
94 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
164 def call(tag, params, body, context)
165   result = ''
166   tdata = tag_data(tag, context)
167   if !tdata.nil?
168     context = context.clone(:config => create_config(tag, params, tdata, context))
169     result, process_output = tdata.object.call(tag, body, context)
170     if process_output
171       context.content = result
172       result = context.website.ext.content_processor.call('tags', context).content
173     end
174   else
175     raise Webgen::RenderError.new("No tag processor for '#{tag}' found", 'tag',
176                                   context.dest_node, context.ref_node)
177   end
178   result
179 rescue Webgen::Error => e
180   e.path = context.dest_node if e.path.to_s.empty?
181   e.location = "tag.#{tag}" unless e.location
182   raise
183 rescue Exception => e
184   raise Webgen::RenderError.new(e, "tag.#{tag}", context.dest_node, context.ref_node)
185 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
144 def register(klass, options = {}, &block)
145   if block_given? && !options[:config_prefix]
146     raise ArgumentError, "The option :config_prefix needs to be specified when registering a tag using a block"
147   end
148 
149   names = [options.delete(:names)].flatten.compact
150   options[:name] = names.shift
151   name = do_register(klass, options, true, &block)
152   ext_data(name).mandatory = options[:mandatory] || []
153   ext_data(name).config_prefix = options[:config_prefix] ||
154     Webgen::Utils.snake_case(ext_data(name).object.to_s.gsub(/::/, '.').gsub(/^Webgen\./, ''))
155   ext_data(name).initialized = false
156   names.each {|n| @extensions[n.to_sym] = @extensions[name]}
157 end
replace_tags(str) { |tag_name, params, body| ... } click to toggle source

See Webgen::Utils::TagParser#replace_tags.

    # File lib/webgen/tag.rb
188 def replace_tags(str, &block)  #:yields: tag_name, params, body
189   @parser.replace_tags(str, &block)
190 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
198 def create_config(tag, params, tdata, context)
199   values = case params
200            when Hash
201              values_from_hash(tag, params, tdata, context)
202            when String, Array, TrueClass, FalseClass, Numeric
203              values_for_default_mandatory(tag, params, tdata, context)
204            when NilClass then {}
205            else
206              raise Webgen::RenderError.new("Invalid parameter type (#{params.class})",
207                                            "tag", context.dest_node, context.ref_node)
208            end
209 
210   if !tdata.mandatory.all? {|k| values.has_key?(k)}
211     raise Webgen::RenderError.new("Not all mandatory parameters set", "tag", context.dest_node, context.ref_node)
212   end
213   config = context.website.config.dup
214   config.set_values(values)
215   config.freeze
216   config
217 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
252 def tag_data(tag, context)
253   tdata = @extensions[tag.to_sym] || @extensions[:default]
254   if tdata && !tdata.initialized
255     tdata.object = resolve_class(tdata.object)
256     tdata.mandatory.each_with_index do |o, index|
257       next if context.website.config.option?(o)
258       o = tdata.config_prefix + '.' + o
259       if context.website.config.option?(o)
260         tdata.mandatory[index] = o
261       else
262         raise ArgumentError, "Invalid configuration option name '#{o}' specified as mandatory option for tag '#{tag}'"
263       end
264     end
265     tdata.initialized = true
266   end
267   tdata
268 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
239 def values_for_default_mandatory(tag, value, tdata, context)
240   if tdata.mandatory.first.nil?
241     context.website.logger.error do
242       ["No default mandatory option specified for tag '#{tag}' but set in <#{context.ref_node}>",
243        "Use the {key: value} syntax for assigning the value '#{value}' to the intended tag option!"]
244     end
245     {}
246   else
247     {tdata.mandatory.first => value}
248   end
249 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
220 def values_from_hash(tag, params, tdata, context)
221   result = {}
222   params.each do |key, value|
223     if context.website.config.option?(key)
224       result[key] = value
225     elsif context.website.config.option?(tdata.config_prefix + '.' + key)
226       result[tdata.config_prefix + '.' + key] = value
227     else
228       context.website.logger.warn do
229         ["Invalid configuration option '#{key}' for tag '#{tag}' found in <#{context.ref_node}>",
230          "Remove the invalid key '#{key}' to fix the warning."]
231       end
232     end
233   end
234   result
235 end