module BuildMaster class TemplateRunner NAMESPACE = "http://buildmaster.rubyforge.org/xtemplate/1.0" def initialize(result, template, source, &evaluator) @result = result @template = template @source = source @evaluator = evaluator end def process process_children(@result, @template) end private def process_children(target, template) template.each do |element| if (element.kind_of? REXML::Element) process_element(target, element) else target.add(deep_clone(element)) end end end def process_element(target, element) if (template_directive?(element)) process_directive(target, element) else output_element = clone_element(element) target.add(output_element) process_children(output_element, element) end end def template_directive?(element) element.namespace == NAMESPACE end def process_directive(target, template_element) begin action = method("process_#{template_element.name}_directive") action.call(target, template_element) rescue NameError raise TemplateException, "unable to process template:#{template_element.name}: #{$!}" , caller end end def clone_element( template_element ) cloned_element = REXML::Element.new( template_element.expanded_name ) copy_attributes( template_element, cloned_element ) cloned_element end def copy_attributes( template_element, expanded_element ) template_element.attributes.each_attribute do |attribute| expanded_element.attributes.add( attribute.clone ) end end def deep_clone( node ) if node.kind_of? REXML::Parent node.deep_clone else node.clone end end def process_include_directive(target_element, template_element) elements_attribute = template_element.attributes["elements"] if (elements_attribute) REXML::XPath.match(@source, elements_attribute).each do |matched| target_element.add(deep_clone(matched)) end return end end def process_when_directive(target_element, template_element) if evaluate(template_element, 'test') process_children(target_element, template_element) end end def process_attribute_directive(target_element, template_element) name = template_element.attributes['name'] target_element.attributes[name]=evaluate(template_element, 'eval') end def process_text_directive(target_element, template_element) target_element.add(REXML::Text.new(evaluate(template_element, 'eval'))) end def process_each_directive(target_element, template_element) source_xml = evaluate!(template_element, 'source') matched_element = REXML::XPath.match(REXML::Document.new(source_xml), template_element.attributes['select']) count = attribute!(template_element, 'count') count.to_i.times do |i| TemplateRunner.new(target_element, template_element, matched_element[i], &@evaluator).process end end def evaluate!(xml_element, attribute_name) value = evaluate(xml_element, attribute_name) message = xml_element.attributes[attribute_name] if (not value) raise TemplateException, "#{message} from attribute #{attribute_name} cannot be nil" end return value end def evaluate(xml_element, attribute_name) message = attribute!(xml_element, attribute_name) return @evaluator.call(message) end def attribute!(xml_element, attribute_name) value = xml_element.attributes[attribute_name] if (not value) raise TemplateException, "attribute #{attribute_name} not found in #{xml_element}" end return value end end end