Class: MasterView::TemplateProcessing::Renderer

Inherits:
Object
  • Object
show all
Includes:
DirectiveHelpers
Defined in:
lib/masterview/parser.rb

Overview

A Renderer is configured for a template parser to control the output generated from the template source document. The renderer is responsible for managing directive processing and output generation in response to document event notifications from the SAX source document parser.

Constant Summary collapse

XHTMLEmptyElementNameSet =

Set of element names that can be simplified. Used in simplify_empty_elements. Only simplify elements that are specified in the DTD as being empty, collapsing others can cause parsing or rendering problems in browsers. www.w3.org/TR/xhtml1/dtds.html#a_dtd_XHTML-1.0-Strict base, meta, link, hr, br, param, img, area, input, col

%w{ base meta link hr br param img area input col }.to_set

Constants included from DirectiveHelpers

DirectiveHelpers::CRLF, DirectiveHelpers::ERB_CONTENT_END, DirectiveHelpers::ERB_CONTENT_START, DirectiveHelpers::ERB_EVAL_END, DirectiveHelpers::ERB_EVAL_START

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from DirectiveHelpers

#delete_last_in_parent, #find_last_in_parent, #find_string_val_in_string_hash, #parse, #quote, #quote_if, #render_partial_name_to_file_name

Constructor Details

#initialize(options = {}) ⇒ Renderer



303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
# File 'lib/masterview/parser.rb', line 303

def initialize( options = {} )
  @options = options
  @template_pathname = options[:template_pathname]
  @template_full_pathname = IOMgr.template.path(self.template_pathname).full_pathname if self.template_pathname

  @default_render_handler = SimpleRenderHandler.new
  @render_levels = [
    RenderLevel.new( [RenderMode.new] )
  ]

  serializer = options[:serializer] || DefaultSerializer
  self.serializer = serializer.is_a?(Class) ? serializer.new(options) : serializer #one can pass in Serializer class or an instance

  @default_extension = (options[:output_mio_tree]) ? options[:output_mio_tree].default_extension : IOMgr.erb.default_extension
  @keyword_expander = KeywordExpander.new
  @keyword_expander.set_template_pathname(self.template_pathname, @default_extension)
  self.initialize_directives( options[:directive_load_path] )
end

Instance Attribute Details

#default_extensionObject (readonly)

Returns the value of attribute default_extension.



290
291
292
# File 'lib/masterview/parser.rb', line 290

def default_extension
  @default_extension
end

#default_render_handlerObject

Returns the value of attribute default_render_handler.



291
292
293
# File 'lib/masterview/parser.rb', line 291

def default_render_handler
  @default_render_handler
end

#directive_load_pathObject (readonly)

:nodoc:



293
294
295
# File 'lib/masterview/parser.rb', line 293

def directive_load_path
  @directive_load_path
end

#directives_registryObject (readonly)

:nodoc:



294
295
296
# File 'lib/masterview/parser.rb', line 294

def directives_registry
  @directives_registry
end

#keyword_expanderObject (readonly)

Returns the value of attribute keyword_expander.



290
291
292
# File 'lib/masterview/parser.rb', line 290

def keyword_expander
  @keyword_expander
end

#mv_gen_partial_attrObject (readonly)

Returns the value of attribute mv_gen_partial_attr.



296
297
298
# File 'lib/masterview/parser.rb', line 296

def mv_gen_partial_attr
  @mv_gen_partial_attr
end

#mv_gen_replace_attrObject (readonly)

Returns the value of attribute mv_gen_replace_attr.



296
297
298
# File 'lib/masterview/parser.rb', line 296

def mv_gen_replace_attr
  @mv_gen_replace_attr
end

#mv_generate_attrObject (readonly)

Returns the value of attribute mv_generate_attr.



296
297
298
# File 'lib/masterview/parser.rb', line 296

def mv_generate_attr
  @mv_generate_attr
end

#mv_insert_generated_comment_attrObject (readonly)

Returns the value of attribute mv_insert_generated_comment_attr.



297
298
299
# File 'lib/masterview/parser.rb', line 297

def mv_insert_generated_comment_attr
  @mv_insert_generated_comment_attr
end

#optionsObject (readonly)

Returns the value of attribute options.



287
288
289
# File 'lib/masterview/parser.rb', line 287

def options
  @options
end

#prologObject

array of strings containing the xml prolog for a doc, on the root this might contain xml declaration and doctype, etc.



301
302
303
# File 'lib/masterview/parser.rb', line 301

def prolog
  @prolog
end

#render_levelsObject

Returns the value of attribute render_levels.



291
292
293
# File 'lib/masterview/parser.rb', line 291

def render_levels
  @render_levels
end

#serializerObject

Returns the value of attribute serializer.



291
292
293
# File 'lib/masterview/parser.rb', line 291

def serializer
  @serializer
end

#template_full_pathnameObject (readonly)

Returns the value of attribute template_full_pathname.



288
289
290
# File 'lib/masterview/parser.rb', line 288

def template_full_pathname
  @template_full_pathname
end

#template_pathnameObject (readonly)

Returns the value of attribute template_pathname.



288
289
290
# File 'lib/masterview/parser.rb', line 288

def template_pathname
  @template_pathname
end

Instance Method Details

#append_content(type, content) ⇒ Object



377
378
379
380
381
382
383
# File 'lib/masterview/parser.rb', line 377

def append_content(type, content)
  modes.each do |mode|
    if mode.tag
      mode.tag.content << mode.render_directives( type, mode.tag.create_context(:content_part => content) )
    end
  end
end

#append_raw(raw_output) ⇒ Object

does not call any directives, direct output



386
387
388
389
390
391
392
# File 'lib/masterview/parser.rb', line 386

def append_raw(raw_output)
  modes.each do |mode|
    if mode.tag
      mode.tag.content << raw_output
    end
  end
end

#append_to_prolog(str) ⇒ Object

initialize (if necessary) and append string to xml_prolog array



468
469
470
471
# File 'lib/masterview/parser.rb', line 468

def append_to_prolog(str)
  self.prolog ||= []
  self.prolog << str
end

#capitalize_first_letter(string) ⇒ Object



453
454
455
# File 'lib/masterview/parser.rb', line 453

def capitalize_first_letter(string)
  string[0,1].upcase + string[1..-1]
end

#initialize_directives(load_path) ⇒ Object

Process the directive load_path, re-requiring any new entries and preparing processing context for the current template.

Note that any directives that were already required (and loaded) will still be in memory because these are not reset. (i.e., there is not currently a cache vs. reload config option for directive implementations in the way that rails provides for class loading).



330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
# File 'lib/masterview/parser.rb', line 330

def initialize_directives( load_path ) #:nodoc:

  using_current_load_path = (load_path == MasterView::DirectiveLoadPath.current)

  # ordinary configuration sets up std load path and directives registry for the app session
  # ??provide hook here for testing alt load paths and config overrides? is this right??
  @directives_registry = using_current_load_path ? MasterView::DirectiveRegistry.current : MasterView::DirectiveRegistry.new

  #ISSUE: do we really need/want to run this o n *every* parse?
  # Probably ought to have some cache config option like dev/production class loading
  # [DJL 02-Oct-2006]
  @directives_registry.process_directives_load_path( load_path ) #?? unless using_current_load_path??

  # bind the names of the standard mv template-manipulation directives
  mv_ns = @directives_registry.mv_namespace_prefix
  @mv_generate_attr = mv_ns+'generate'
  @mv_gen_partial_attr = mv_ns+'gen_partial'
  @mv_gen_replace_attr = mv_ns+'gen_replace'
  @mv_insert_generated_comment_attr  = mv_ns+'insert_generated_comment'
  #??@mv_import_attr = mv_ns+'import'
  #??@mv_import_render_attr = mv_ns+'import_render'

end

#modesObject



354
355
356
# File 'lib/masterview/parser.rb', line 354

def modes
  @render_levels.last.render_modes
end

#pop_levelObject



394
395
396
# File 'lib/masterview/parser.rb', line 394

def pop_level
  @render_levels.pop
end

#pop_tagObject



398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
# File 'lib/masterview/parser.rb', line 398

def pop_tag
  need_to_pop_level = false
  modes.each do |mode|
    mode.tag.etag << mode.render_directives(:etag)
    content = []
    content << mode.tag.stag << mode.tag.content << mode.tag.etag
    content = simplify_empty_elements content
    last_tag = mode.tags.pop
    if mode.tags.empty?
      unless mode.output.nil?
        @serializer.serialize(mode, last_tag)
        need_to_pop_level = true
      end
    else #add it to the parent
      mode.tag.content << content
    end
  end
  pop_level if need_to_pop_level
end

#push_level(render_level) ⇒ Object



358
359
360
# File 'lib/masterview/parser.rb', line 358

def push_level(render_level)
  @render_levels.push render_level
end

#push_tag(tag_name, attributes) ⇒ Object



362
363
364
365
366
367
368
369
370
371
372
373
374
375
# File 'lib/masterview/parser.rb', line 362

def push_tag(tag_name, attributes)
  tag_prolog = nil
  unless prolog.nil?
    tag_prolog = prolog.join("\n")
    self.prolog = nil # clear now that we have used it, only needs to be applied to this tag
  end
  modes.each do |mode|
    attributes_copy = attributes.clone #these get changed in select_active_directives
    directives = select_active_directives(tag_name, attributes_copy, mode)
    parent = (mode.tags.empty?) ? nil : mode.tag
    mode.tags.push Tag.new(directives, tag_name, attributes_copy, mode.mode_type, parent, tag_prolog, self)
    mode.tag.stag << mode.render_directives(:stag)
  end
end

#select_active_directives(tag_name, attributes, mode) ⇒ Object



457
458
459
460
461
462
463
464
465
# File 'lib/masterview/parser.rb', line 457

def select_active_directives(tag_name, attributes, mode)
  selected = DirectiveSet.new
  directive_processors = directives_registry.construct_directive_processors( tag_name, attributes )
  sorted_directives = directive_processors.sort do |dp1,dp2|
    dp1.priority <=> dp2.priority
  end
  sorted_directives << @default_render_handler #insure this is last
  selected << sorted_directives
end

#simplify_empty_elements(content) ⇒ Object

Simplify (collapse) empty elements so that <foo></foo> from rexml parsing ends up being <foo/> . Only simplify elements that are specified in the DTD as being empty, collapsing others can cause parsing or rendering problems in browsers. Uses constant XHTMLEmptyElementNameSet to find elements that should be collapsed. www.w3.org/TR/xhtml1/dtds.html#a_dtd_XHTML-1.0-Strict xhtml-1.0-Strict empty elements are: base, meta, link, hr, br, param, img, area, input, col



426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
# File 'lib/masterview/parser.rb', line 426

def simplify_empty_elements(content) #relies on the fact that > and </ are individual strings and are back to back with nothing in between
  ret = []
  current_element_name = nil
  next_to_last = nil
  last = nil
  content.flatten!
  content.each do |item|
    if next_to_last == '>' && last == '</' && XHTMLEmptyElementNameSet.include?(current_element_name) # collapse
      ret.pop #remove '>'
      ret.pop #remove '</'
      ret << ' />' # adding in a space to make xhtml more compatible with html editors and older browsers
    else
      ret << item
    end
    unless item.nil?
      if !item.starts_with?('</') && item.starts_with?('<')
        current_element_name = /^<(\S*)/.match(item)[1] # depending on what is after < this might be empty string
      elsif last && last.starts_with?('</')
        current_element_name = nil
      end
    end
    next_to_last = last
    last = item
  end
  ret
end