require 'amrita2/template' module Amrita2 class Core::Template def add_macro(m) @macros ||= [] @macros << m end alias use_macro add_macro compile_old = instance_method(:compile) define_method(:compile) do |*args| macros = @macros if macros and macros.size > 0 filter_setup do |e, name ,filters| filters << MacroFilter.new(*macros) end end compile_old.bind(self).call(*args) end end module Macro # :nodoc: all class Base def initialize @mt = Template.new(get_macro_template, :amrita_prefix=>"macro:", :inline_ruby=>true) @option = {} @option = self.class.const_get(:Option) if self.class.const_defined?(:Option) raise "Macro Option is not defined propery in #{self.class} #{@option.inspect}" unless @option.kind_of?(Hash) end def get_macro_template self.class.const_get(:TemplateText) end def get_element_name #self.class.const_get(:ElementName) @option[:tag] || underscore(self.class.name) end def process(de, element) preprocess_element(@mt, element) end def match_element(element) element.name == get_element_name.to_s end def macro_data(element) element.as_amrita_dictionary(@option) end def preprocess_element(mt, element) if @option[:trace] mt.set_trace(@option[:trace]) @option[:trace] << (macro_data(element)).inspect end mt.amrita_prefix = "macro:" mt.render_with(macro_data(element)) end private # from activesupport def underscore(camel_cased_word) camel_cased_word.to_s.gsub(/::/, '/'). gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2'). gsub(/([a-z\d])([A-Z])/,'\1_\2'). tr("-", "_"). downcase end end end module Filters class MacroFilter < Base include Amrita2 include Util include OptionSupport attr_reader :macros def initialize(*macros) @macros = macros.collect do |m| case m when Class m.new else m end end @element_names = {} @macros.each do |m| @element_names[m.get_element_name.to_s] = true end end def check_element(element) return true if @element_names[element.name] element.each_child do |c| next unless c.kind_of?(Hpricot::Elem) return true if @element_names[c.name] or check_element(c) end false end def filter_element(de, element) return element unless check_element(element) @macros.each do |m| if m.match_element(element) ret = m.process(de, element) ret = Core::PreProcessor.new.process(ret.dup) ret.gsub!('<%%', '<%') ret.gsub!('%%>', '%>') root = Hpricot.make("<_ />").first Hpricot.make(ret, :xml=>true).each do |e| root.insert_after(e, nil) end element = replace_target_src(root) else element.each_child do |c| next unless c.kind_of?(Hpricot::Elem) cc = filter_element(de, c) element.replace_child(c, cc) if c != cc end end end element end def replace_target_src(e) %w(src filter v skipif for).each do |k| e.set_attribute("am:#{k}", e.attributes["target_#{k}"]) if e.attributes["target_#{k}"] e.delete_attribute("target_#{k}") end e.each_child do |c| next unless c.kind_of?(Hpricot::Elem) replace_target_src(c) end e end def element_render_code(de, cg, element, &block) if (element.name == 'macroroot') yield else super end end end end end