module MasterView # A DirectiveRegistry manages the directives available # for processing the MasterView directive attributes in # a template document. # # DirectiveRegistry is an internal mechanism of the # template engine, primarily used by the MasterView::Parser # to support directive attribute processing. # class DirectiveRegistry # Answer the list of directive classes attr_reader :loaded_classes def initialize() #:nodoc: @loaded_classes = [] clear_directive_maps() end # Register a directive implementation. # # A directive is ordinarily a subclass of MasterView::DirectiveBase. # #-- #TODO: A directive implementation must support the following services:.... #++ # def register_directive(directive_class) #TODO: ensure that the directive impl supports required prototcol (i/f check...)? @loaded_classes << directive_class end # Answer the (base) names of the loaded directive classes. # # By default, strips off module prefixes and returns just the # directive class name for brevity. # def loaded_class_names( simpleNames=true ) @loaded_classes.collect do |dc| simpleNames ? simple_class_name(dc) : dc.name end end # Answer the simple name of a directive class (without its module qualifier) def simple_class_name( dc ) dc.name.split(':').last end # Answer the attribute name implemented by a directive class def directive_attr_name( dc ) dc.respond_to?(:attr_name) ? dc.attr_name : simple_class_name(dc).downcase_first_letter end # Answer the fully qualified name of the attribute implemented by a directive class. # attr_qname ::= : def directive_full_attr_name( dc, mv_ns ) (dc.respond_to? :full_attr_name) ? dc.full_attr_name(mv_ns) : build_full_attribute_name(mv_ns, dc) end # this method is invoked to build the full attribute name (the attribute which will be watched for in # html attibutes. It concatenates the namespace prefix to the class name after first removing any module # prefixes and then downcasing the first letter def build_full_attribute_name(mv_ns, dc) #:nodoc: #ISSUE: need to allow directives to control their name space mv_ns + simple_class_name(dc).downcase_first_letter end # Ensure that all directives on the load path are loaded. # Build the directive processing tables used by the template parser. # def process_directives_load_path( directive_paths, mv_ns=nil ) load_directives( directive_paths ) build_directive_maps( mv_ns ) end # Ensure that all directives on the load path are loaded. # # require {directives_dir}/foo_directive.rb def load_directives( directive_paths=nil ) #:nodoc: directive_paths = MasterView::DefaultDirectiveLoadPaths if directive_paths.nil? directive_paths.each do | dir | # MV::Initializer now handles bad dir path entries, but leave old checks for now [DJL 04-Jul-2006] next if dir.nil? if File.directory?(dir) Dir.open( dir ).each { |fn| require "#{dir}/#{fn}" if fn =~ /[.]rb$/ } else #raise InvalidPathError.new('Directive load path dir does not exist:'+dir) unless File.directory? dir Log.error "Directive load path dir does not exist: '#{dir}'" if MasterView.const_defined?(:Log) #backstop for test case startup end end clear_directive_maps() # ensure we take a clean point of view on the matter at hand end # Build the directive processing tables used by the template parser. def build_directive_maps( mv_ns=nil ) #:nodoc: mv_ns = MasterView::NamespacePrefix if mv_ns.nil? clear_directive_maps() # ensure we take a clean point of view on the matter at hand Log.debug { 'directive plugins loaded:' + loaded_class_names.inspect } if MasterView.const_defined?(:Log) #backstop for test case startup loaded_classes.each do |dc| dc.on_load if dc.respond_to?(:on_load) attr_qname = directive_full_attr_name( dc, mv_ns ) qname_parts = attr_qname.split(':') raise NameError, "Directive qname requires namespace: '#{attr_qname} - #{dc.name}" if qname_parts.length != 2 ns_name, attr_name = qname_parts @directive_namespaces << ns_name if ! @directive_namespaces.include?( ns_name ) @directive_classes[attr_qname] = dc ###WHAT IS THIS NOTION of auto/global directive??? currently used (only) by inline_erb_directive dc_instance = dc.new(nil) @auto_directives << dc if dc_instance.respond_to?(:global_directive?) && dc_instance.global_directive? end Log.debug { 'directives='+@directive_classes.keys().sort!().inspect } if MasterView.const_defined?(:Log) #backstop for test case startup Log.debug { 'auto_directives='+@auto_directives.inspect } if MasterView.const_defined?(:Log) #backstop for test case startup end # Construct directive processors needed to handle the # attributes defined on a template document element. # # Constructs processors for all global directives # and for any directive attributes. # # def construct_directive_processors( attributes ) directive_processors = [] # always instantiate global directive handlers @auto_directives.each do | dc | directive_processors << dc.new(nil) end # instantiate the directive processor on the attribute value if its attr present # remove the MV attribute from the document so that it's only effect is from the processor action @directive_classes.each do | attr_qname, dc | directive_processors << dc.new(attributes.delete(attr_qname)) if attributes[attr_qname] end directive_processors end protected def clear_directive_maps() #:nodoc: @directive_namespaces = [] @directive_classes = {} #map fully qualified directive attr name to directive_class @auto_directives = [] end end # The DirectivesRegistry manages the loaded directives available # for processing template markup DirectivesRegistry = DirectiveRegistry.new() # Register a directive implementation. # # Registration is handled automatically for directives # implemented as subclasses of MasterView::DirectiveBase, # the usual technique, or by including the PluginLoadTracking # module in a directive implementation class. # # Directive registration ordinarily occurs during MasterView # initialization, when directive classes on the configured # directive_paths directories are automatically # loaded and registered with the template engine. # def self.register_directive(directive_class) DirectivesRegistry.register_directive(directive_class) end end