Class: MasterView::DirectiveRegistry

Inherits:
Object
  • Object
show all
Defined in:
lib/masterview/directive_registry.rb

Overview

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.

Constant Summary collapse

DEBUG_TRACE_LOADING =

:nodoc: ##DEBUG##

false
TRACE_MD_HARDENING_SUBJECT =

‘MasterView::DirectiveTests::TestEventsDirective’

nil
DEBUG_BUILTIN_DIRECTIVE_REGISTRATION =

:nodoc:

false
DEBUG_CURRENT =

:nodoc:

false

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeDirectiveRegistry

Returns a new instance of DirectiveRegistry.



117
118
119
120
121
122
123
124
# File 'lib/masterview/directive_registry.rb', line 117

def initialize()
  @loaded_classes = []
  set_load_context( nil )
  clear_directive_maps()
  #STDOUT.puts "\n###Created DirectiveRegistry=#{self.object_id}"
  #STDOUT.puts "...loaded_classes=#{self.loaded_classes.inspect}"
  #STDOUT.puts "...load_context=#{self.load_context.inspect}"
end

Instance Attribute Details

#load_contextObject (readonly)

internal mechanism to provide directory/path load context during directive class loading



109
110
111
# File 'lib/masterview/directive_registry.rb', line 109

def load_context
  @load_context
end

#loaded_classesObject (readonly)

Answer the list of directive classes which are loaded in the current configuration.



79
80
81
# File 'lib/masterview/directive_registry.rb', line 79

def loaded_classes
  @loaded_classes
end

Class Method Details

.currentObject

The DirectiveRegistry for managing the loaded directives available for processing template markup. # DirectiveRegistry.current is ordinarily configured to load the directives registered on the DirectiveLoadPath.current.



23
24
25
# File 'lib/masterview/directive_registry.rb', line 23

def self.current
  @@current
end

.current=(registry) ⇒ Object

Set the current directives registry for template processing. Ordinarily done once during masterview initialization.



29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/masterview/directive_registry.rb', line 29

def self.current=(registry) #:nodoc:
  if self.class_variables.include?('@@current') && @@current && registry
    # HACK: need to preserve the list of loaded classes.
    # this shouldn't ordinarily happen, but repeated class loading
    # occurs during, ah, say... test suite runs, yes!!
    registry.update_loaded_classes_hack( @@current.loaded_classes )
  end
  @@current = registry
  if DEBUG_CURRENT
    STDOUT.puts "\n****#{self.name}.current set: #{current.object_id}"
    STDOUT.puts "...#{current.inspect}\n"
  end
end

.register_default_namespaces(ns_prefix_masterview, ns_prefix_extensions) ⇒ Object

Register default namespaces for directive metadata Should be invoked once during MasterView initialization

Raises:

  • (ArgumentError)


49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/masterview/directive_registry.rb', line 49

def self.register_default_namespaces(ns_prefix_masterview, ns_prefix_extensions) #:nodoc:
  raise ArgumentError, "Invalid masterview namespace prefix '#{ns_prefix_masterview}'" if ns_prefix_masterview[-1..-1] != ':'
  raise ArgumentError, "Invalid extensions namespace prefix '#{ns_prefix_extensions}'" if ns_prefix_extensions[-1..-1] != ':'
   = {
    :namespace => ns_prefix_masterview[0...-1],
    :namespace_prefix => ns_prefix_masterview,
    }
   = {
    :namespace => ns_prefix_extensions[0...-1],
    :namespace_prefix => ns_prefix_extensions,
    }
  #assert NotReallyNecessary, 'because we'll just make sure we code up the right stuff here'
  . 
  . 
end

Instance Method Details

#build_directive_mapsObject

Build the directive processing tables used by the template parser.



267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
# File 'lib/masterview/directive_registry.rb', line 267

def build_directive_maps()  #:nodoc:

  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)  #ISSUE: needs review (timing, intent) [DJL 08-Oct-2006]
    attr_qname = dc.attribute_qname
    ns_name = dc.namespace_name
    raise NameError, "Directive qname requires namespace: '#{attr_qname} - #{dc.name}" if ns_name.nil? || ns_name.strip().empty?
    if DEBUG_BUILTIN_DIRECTIVE_REGISTRATION and dc.name.starts_with?('MasterView::Directives::')
      if ns_name != 'mv'  #! attr_qname.starts_with(MasterView.mv_namespace_prefix)
        STDOUT.puts "****BUILTIN DIRECTIVE NAMESPACE PROBLEM: #{dc.name} qname='#{attr_qname}'"
        raise RuntimeError, "BUILTIN DIRECTIVE NS PROBLEM: #{dc.name} qname='#{attr_qname}"
      end
    end
    @directive_namespaces << ns_name if ! @directive_namespaces.include?( ns_name )
    @directive_classes[attr_qname] = dc
    # global directives can be automatically attached to all elements (e.g., generated comments)
    # or conditionally attached (e.g., a directive that wants to glom onto all elements of a specific type)
    dc_global_spec = dc..fetch(:global_directive?, false)
    if dc_global_spec == true  # unconditional global directive - applied to all elements
      @auto_directives << dc
    elsif dc_global_spec  # conditional global directive - applied when match condition satisfied
      @global_directives << dc
    end
  end

  if MasterView.const_defined?(:Log)  #backstop for test case startup
    Log.debug { "directives=#{@directive_classes.keys().sort!().inspect}" }
    Log.debug { "global_directives=#{@global_directives.inspect}" }
    Log.debug { "auto_directives=#{@auto_directives.inspect}" }
  end
end

#construct_directive_processors(tag_name, element_attrs) ⇒ Object

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 defined on the element.

Removes all directive attributes from the element’s attributes.



310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
# File 'lib/masterview/directive_registry.rb', line 310

def construct_directive_processors( tag_name, element_attrs )
  directive_processors = []
  # always instantiate global directive handlers
  @auto_directives.each do | dc |
    directive_processors << dc.new(nil)
  end
  # conditionally instantiate global directives which are interested in this element
  @global_directives.each do | dc |
    attr_value = dc.[:global_directive?].call( tag_name, element_attrs )
    if attr_value
      directive_processors << dc.new(attr_value)
    end
  end
  # instantiate the directive processor on the attribute value if its attr present
  # remove the MV attribute from the document so that its only effect is from the processor action
  @directive_classes.each do | attr_qname, dc |
    directive_attr_value = element_attrs[attr_qname]
    if directive_attr_value
      # convert the directive into a processing handler for this element
      element_attrs.delete(attr_qname)
      directive_processors << dc.new(directive_attr_value)
    end
  end
  directive_processors
end

#load_directive_file(directive_file_path) ⇒ Object

Load a specific directive implementation class

Mainly provided for use by unit tests



202
203
204
205
206
# File 'lib/masterview/directive_registry.rb', line 202

def load_directive_file(directive_file_path)
  dir_path = File.dirname(directive_file_path)
  dpe = MasterView::DirectiveLoadPath::PathEntry.new(dir_path)
  load_from_directive_path_entry( dpe, directive_file_path )
end

#load_directives(load_path = nil) ⇒ Object

Ensure that all directives on the load path are loaded.

require directives_dir/foo_directive.rb



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/masterview/directive_registry.rb', line 183

def load_directives( load_path=nil ) #:nodoc:
  load_path = MasterView::DirectiveLoadPath.current if load_path.nil?
  STDOUT.puts "\n-------- LOAD DIRECTIVES ON PATH -------" if DEBUG_TRACE_LOADING
  load_path.each do | dpe |
    STDOUT.puts "DIRECTORY PATH ENTRY: #{dpe.inspect}" if DEBUG_TRACE_LOADING
    if dpe.exists?
      self.load_from_directive_path_entry( dpe )
    else
      #raise InvalidPathError.new('Directive load path dir does not exist:'+dir_path)
      Log.error "Directive load path dir does not exist: '#{dpe.dir_path}'" if MasterView.const_defined?(:Log)  #backstop for test case startup
    end        
  end
  clear_directive_maps()  # ensure we take a clean point of view on whatever just got loaded
  STDOUT.puts "------ END LOAD DIRECTIVES ------" if DEBUG_TRACE_LOADING
end

#load_from_directive_path_entry(dpe, directive_file_path = nil) ⇒ Object

:nodoc:



208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/masterview/directive_registry.rb', line 208

def load_from_directive_path_entry(dpe, directive_file_path=nil) #:nodoc:
  dir_md_specs = dpe. # :create_if_not_defined => true
  is_mv_directives_dir = dir_md_specs.fetch(:use_masterview_namespace, false)
  app_md_defaults = is_mv_directives_dir ?  : 
  md_defaults = MasterView::DirectiveLoadPath.compute_md_defaults(
                        app_md_defaults, 
                        dir_md_specs[:default], 
                        dpe. )
  begin
    dir_load_context = { :metadata_defaults => md_defaults, }
    set_load_context( dir_load_context )
    if directive_file_path
      # load specific file from a directive path entry directory
      dir_load_context[:directive_file] = directive_file_path
      load_single_directive_file(directive_file_path, md_defaults) #WAS: require directive_file_path
    else
      # load all directives in the directory
      dir_path = dpe.dir_path
      #??Log.debug { "Loading directives from #{dir_path} with metadata defaults #{md_defaults.inspect}" }
      #??load_context[:synonyms] = dir_md_specs.fetch(:synonyms, {})??
      files_to_ignore = dir_md_specs.fetch(:ignore, []).collect { |fn| fn.ends_with?('.rb') ? fn : "#{fn}.rb" }
      Dir.entries( dir_path ).each { |fn| 
        # we assume all .rb files in a directives dir are directive impls
        if fn =~ /[.]rb$/ 
          next if files_to_ignore.include?(fn) # skip directives in the ignore list
          directive_file_path = "#{dir_path}/#{fn}"
          dir_load_context[:directive_file] = directive_file_path
          #??@load_context[:directive_synonyms] = dir_synonyms_specs.fetch(fn, [])??
          load_single_directive_file(directive_file_path, md_defaults) #WAS: require directive_file_path
        end
      }
    end
  ensure
    set_load_context( nil )
  end
end

#loaded_class_names(simpleNames = true) ⇒ Object

Answer the (base) names of the loaded directive classes.

By default, strips off module prefixes and returns just the directive class name for brevity.



160
161
162
163
164
# File 'lib/masterview/directive_registry.rb', line 160

def loaded_class_names( simpleNames=true )
  @loaded_classes.collect do |dc| 
    simpleNames ? simple_class_name(dc) : dc.name
  end
end

#loaded_namespacesObject

Answer a list of all namespaces in use



82
83
84
# File 'lib/masterview/directive_registry.rb', line 82

def loaded_namespaces
  @directive_namespaces #??.clone for paranoid safety?
end

#metadata_defaultsObject

Hash containing default directive metadata properties Used during directive path loading to exploit default config specs.



98
99
100
# File 'lib/masterview/directive_registry.rb', line 98

def   #:nodoc:
  
end

#metadata_defaults_extensionsObject

Hash containing default directive metadata properties Used during directive path loading to exploit default config specs.



104
105
106
# File 'lib/masterview/directive_registry.rb', line 104

def   #:nodoc:
  
end

#mv_extensions_namespace_prefixObject

Answer the namespace prefix of directives in the default MasterView extensions directives namespace.



73
74
75
76
# File 'lib/masterview/directive_registry.rb', line 73

def mv_extensions_namespace_prefix
  ##ISSUE: needs work??  Does this need to be an inst var of the registry??
  [:namespace_prefix]
end

#mv_namespace_prefixObject

Answer the namespace prefix of directives in the standard MasterView namespace.



66
67
68
69
# File 'lib/masterview/directive_registry.rb', line 66

def mv_namespace_prefix
  ##ISSUE: needs work??  Does this need to be an inst var of the registry??
  [:namespace_prefix]
end

#process_directives_load_path(load_path = nil) ⇒ Object

Ensure that all directives on the load path are loaded. Build the directive processing tables used by the template parser.



174
175
176
177
178
# File 'lib/masterview/directive_registry.rb', line 174

def process_directives_load_path( load_path=nil )
  load_path = MasterView::DirectiveLoadPath.current if load_path.nil?
  load_directives( load_path )
  build_directive_maps
end

#register_directive(directive_class) ⇒ Object

Register a directive implementation.

A directive is ordinarily a subclass of MasterView::DirectiveBase.



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/masterview/directive_registry.rb', line 139

def register_directive(directive_class)
  # harden the DirectiveMetadata by filling in unspecified values from defaults
  #assert directive_class.ancestors.include? DirectiveMetadata
  ##AARGH: can't do this yet, the timing is premature [DJL 06-Oct-2006]
  #PUNT: md_defaults = load_context.nil? ? metadata_defaults_extensions : load_context[:metadata_defaults]
  #PUNT: directive_class.harden_metadata( md_defaults )
  @loaded_classes << directive_class
  ##DEBUG##
  if TRACE_MD_HARDENING_SUBJECT and directive_class.name == TRACE_MD_HARDENING_SUBJECT
    STDOUT.puts "\nREGISTERED DIRECTIVE #{directive_class.name}"
    STDOUT.puts "...TOO SOON TO HARDEN METADATA"
    STDOUT.puts "...#{directive_class.metadata_values.object_id}: #{directive_class.metadata_values.inspect}\n"
  end
  ##DEBUG##
end

#registered_directive_namesObject

Answer the fully-qualified names of the registered directives



87
88
89
# File 'lib/masterview/directive_registry.rb', line 87

def registered_directive_names
  @directive_classes.keys
end

#registered_directivesObject

Answer the the registered directives



92
93
94
# File 'lib/masterview/directive_registry.rb', line 92

def registered_directives
  @directive_classes.values
end

#set_load_context(context_settings, check_for_conflict = true) ⇒ Object

:nodoc:



110
111
112
113
114
115
# File 'lib/masterview/directive_registry.rb', line 110

def set_load_context(context_settings, check_for_conflict=true) #:nodoc:
  if check_for_conflict && context_settings && @load_context
    raise RuntimeError, "Directive load operation already in progress: current context=#{@load_context.inspect}, requested context=#{context_settings.inspect}"
  end
  @load_context = context_settings
end

#simple_class_name(dc) ⇒ Object

Answer the simple name of a directive class (without its module qualifier)



167
168
169
# File 'lib/masterview/directive_registry.rb', line 167

def simple_class_name( dc )
  dc.name.split(':').last
end

#update_loaded_classes_hack(already_loaded_classes) ⇒ Object

:nodoc:



43
44
45
# File 'lib/masterview/directive_registry.rb', line 43

def update_loaded_classes_hack(already_loaded_classes) #:nodoc:
  @loaded_classes.concat( already_loaded_classes )
end