Class: Coradoc::Html::Renderer

Inherits:
Object
  • Object
show all
Defined in:
lib/coradoc/html/renderer.rb

Overview

Unified template renderer using Liquid templates for CoreModel types

This class provides a template-based rendering system where:

  • Users provide template directories (checked in order, first wins)

  • System falls back to default templates if enabled

  • CoreModel objects are automatically converted to Liquid drops via to_liquid

Examples:

Basic usage with default templates only

renderer = Coradoc::Html::Renderer.new
html = renderer.render(bibliography_element)

With custom template directory (falls back to defaults)

renderer = Coradoc::Html::Renderer.new(
  template_dirs: ["./my_templates"]
)
html = renderer.render(bibliography_element)

Multiple template dirs with priority (first wins)

renderer = Coradoc::Html::Renderer.new(
  template_dirs: ["./project_templates", "./shared_templates"],
  include_default_templates: true
)

Custom templates only, no defaults

renderer = Coradoc::Html::Renderer.new(
  template_dirs: ["./my_templates"],
  include_default_templates: false
)

Constant Summary collapse

TEMPLATE_TYPE_MAP =

Mapping of CoreModel class names to template type names

{
  'Coradoc::CoreModel::Bibliography' => 'bibliography',
  'Coradoc::CoreModel::BibliographyEntry' => 'bibliography_entry',
  'Coradoc::CoreModel::StructuralElement' => 'structural_element',
  'Coradoc::CoreModel::Block' => 'block',
  'Coradoc::CoreModel::SourceBlock' => 'source_block',
  'Coradoc::CoreModel::ExampleBlock' => 'example_block',
  'Coradoc::CoreModel::QuoteBlock' => 'quote_block',
  'Coradoc::CoreModel::SidebarBlock' => 'sidebar_block',
  'Coradoc::CoreModel::LiteralBlock' => 'literal_block',
  'Coradoc::CoreModel::PassBlock' => 'pass_block',
  'Coradoc::CoreModel::ListingBlock' => 'listing_block',
  'Coradoc::CoreModel::OpenBlock' => 'open_block',
  'Coradoc::CoreModel::VerseBlock' => 'verse_block',
  'Coradoc::CoreModel::ReviewerBlock' => 'reviewer_block',
  'Coradoc::CoreModel::AnnotationBlock' => 'annotation_block',
  'Coradoc::CoreModel::ListBlock' => 'list_block',
  'Coradoc::CoreModel::ListItem' => 'list_item',
  'Coradoc::CoreModel::Table' => 'table',
  'Coradoc::CoreModel::TableRow' => 'table_row',
  'Coradoc::CoreModel::TableCell' => 'table_cell',
  'Coradoc::CoreModel::Image' => 'image',
  'Coradoc::CoreModel::InlineElement' => 'inline_element',
  'Coradoc::CoreModel::Paragraph' => 'paragraph',
  'Coradoc::CoreModel::Term' => 'term',
  'Coradoc::CoreModel::Footnote' => 'footnote',
  'Coradoc::CoreModel::FootnoteReference' => 'footnote_reference',
  'Coradoc::CoreModel::Toc' => 'toc',
  'Coradoc::CoreModel::TocEntry' => 'toc_entry',
  'Coradoc::CoreModel::DefinitionList' => 'definition_list',
  'Coradoc::CoreModel::DefinitionItem' => 'definition_item',
  'Coradoc::CoreModel::Abbreviation' => 'abbreviation'
}.freeze
DEFAULT_TEMPLATE_DIR =

Default template directory (built-in templates)

Pathname.new(File.join(File.dirname(__FILE__), 'templates', 'core_model'))

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(template_dirs: nil, include_default_templates: true, options: {}) ⇒ Renderer

Initialize the renderer

Parameters:

  • template_dirs (Array<String>, String, nil) (defaults to: nil)

    Custom template directories Searched in order (first match wins). Can be a single path or array.

  • include_default_templates (Boolean) (defaults to: true)

    Whether to fall back to built-in templates when not found in template_dirs. Default: true

  • options (Hash) (defaults to: {})

    Additional options

Options Hash (options:):

  • :strict (Boolean)

    Raise error if template not found (default: false)

  • :cache_templates (Boolean)

    Cache parsed templates (default: true)



89
90
91
92
93
94
95
# File 'lib/coradoc/html/renderer.rb', line 89

def initialize(template_dirs: nil, include_default_templates: true, options: {})
  @template_dirs = normalize_template_dirs(template_dirs)
  @include_default_templates = include_default_templates
  @options = { cache_templates: true, strict: false }.merge(options)
  @template_cache = {}
  ensure_core_model_drops
end

Instance Attribute Details

#include_default_templatesObject (readonly)

Returns the value of attribute include_default_templates.



77
78
79
# File 'lib/coradoc/html/renderer.rb', line 77

def include_default_templates
  @include_default_templates
end

#optionsObject (readonly)

Returns the value of attribute options.



77
78
79
# File 'lib/coradoc/html/renderer.rb', line 77

def options
  @options
end

#template_dirsObject (readonly)

Returns the value of attribute template_dirs.



77
78
79
# File 'lib/coradoc/html/renderer.rb', line 77

def template_dirs
  @template_dirs
end

Class Method Details

.custom_type_mapObject

Get custom type mappings



213
214
215
# File 'lib/coradoc/html/renderer.rb', line 213

def self.custom_type_map
  @custom_type_map ||= {}
end

.register_type(class_name, type_name) ⇒ Object

Register a custom template type mapping

Parameters:

  • class_name (String)

    Full class name (e.g., “Coradoc::CoreModel::Bibliography”)

  • type_name (String)

    Template type name (e.g., “bibliography”)



207
208
209
210
# File 'lib/coradoc/html/renderer.rb', line 207

def self.register_type(class_name, type_name)
  @custom_type_map ||= {}
  @custom_type_map[class_name] = type_name
end

Instance Method Details

#available_templatesArray<String>

Get list of all available template names

Returns:

  • (Array<String>)

    List of template names (without .liquid extension)



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/coradoc/html/renderer.rb', line 172

def available_templates
  templates = Set.new

  # Scan user template directories
  @template_dirs.each do |dir|
    core_model_dir = File.join(dir, 'core_model')
    next unless File.directory?(core_model_dir)

    Dir.glob(File.join(core_model_dir, '*.liquid')).each do |file|
      templates << File.basename(file, '.liquid')
    end
  end

  # Scan default templates if included
  if @include_default_templates && File.directory?(DEFAULT_TEMPLATE_DIR)
    Dir.glob(File.join(DEFAULT_TEMPLATE_DIR, '*.liquid')).each do |file|
      templates << File.basename(file, '.liquid')
    end
  end

  templates.to_a.sort
end

#render(element, context = {}) ⇒ String

Render a CoreModel element to HTML

Parameters:

  • element (Coradoc::CoreModel::Base)

    The element to render

  • context (Hash) (defaults to: {})

    Additional context for the template

Returns:

  • (String)

    Rendered HTML



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/coradoc/html/renderer.rb', line 102

def render(element, context = {})
  return '' if element.nil?

  # Ensure liquid drop class exists for lutaml-model elements
  ensure_drop_class(element)

  # Handle arrays
  return element.map { |e| render(e, context) }.join("\n") if element.is_a?(Array)

  # Handle primitives
  case element
  when String
    return escape_html(element)
  when Numeric, TrueClass, FalseClass
    return element.to_s
  when NilClass
    return ''
  end

  # Get template type name for this element
  type_name = template_type_for(element)
  return render_fallback(element, context) unless type_name

  # Find the template file
  template_path = find_template(type_name)
  return render_fallback(element, context) if template_path.nil?

  # Load and render the template
  template = load_template(template_path)
  if template
    render_with_template(template, element, context)
  else
    render_fallback(element, context)
  end
end

#render_html5(element, options = {}) ⇒ String

Render a CoreModel element as a complete HTML5 document

Wraps the fragment output of #render in a proper HTML5 document with DOCTYPE, charset, and viewport meta tags.

Parameters:

  • element (Coradoc::CoreModel::Base)

    The element to render

  • options (Hash) (defaults to: {})

    Document-level options

Options Hash (options):

  • :lang (String)

    Document language (default: “en”)

  • :title (String)

    Document title (default: extracted from element)

Returns:

  • (String)

    Complete HTML5 document



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/coradoc/html/renderer.rb', line 148

def render_html5(element, options = {})
  body_html = render(element)

  lang = options[:lang] || 'en'
  title = options[:title] || extract_title(element) || 'Untitled Document'

  <<~HTML
    <!DOCTYPE html>
    <html lang="#{lang}">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>#{escape_html(title)}</title>
    </head>
    <body>
    #{body_html}
    </body>
    </html>
  HTML
end

#template_exists?(type_name) ⇒ Boolean

Check if a template exists for a given type

Parameters:

  • type_name (String)

    Template type name (e.g., “bibliography”)

Returns:

  • (Boolean)

    True if template exists



199
200
201
# File 'lib/coradoc/html/renderer.rb', line 199

def template_exists?(type_name)
  !find_template(type_name).nil?
end