require 'noumenon' require 'liquid' # Templates specify the structure and presentation of a particular piece of content in # a Noumenon site, and are usually provided by a theme. # # A template is split into two parts, the metadata, which defines what data is needed for # the template, and a Liquid template, which defines how the data should be presented. When # loaded from a file, a template will look like this: # # title: # label: Page title # required: true # type: string # help: The title displayed at the top of the page # author: # label: Author # required: false # type: string # help: The author of the article. If not provided, no byline will be shown. # body: # label: Body text # required: true # type: text # help: The main article body. Will be processed with Textile for formatting. # --- #

{{ title }}

# {% if author %} #

By {{ author }}

# {% endif %} # {{ body | textilize }} # # And can be rendered like this: # # Template.from_file("/path/to/template").render("title" => "An Example Page", "author" => "Jon Wood", "body" => "This is an article...") # # If any required fields are missing from the data provided then a MissingContentError will be raised. # # @api public class Noumenon::Template # Indicates one or more required fields were not provided to the template. # # The missing fields are listed in the error message. # # @api public class MissingContentError < StandardError; end # Indicates the requested template could not be found. # # @api public class NotFoundError < StandardError; end # The location this template was loaded from. # @api public attr_accessor :source # The template view. # @api public attr_accessor :content # The fields used by this template. # @api public attr_accessor :fields # Loads a template from the specified path. # # @api public # # @param [ String, #to_s ] path the file to load the template from # @raise [ Noumenon::Template::NotFoundError ] the template could not be found # @return [ Noumenon::Template ] the loaded template # @api public def self.from_file(path) raise NotFoundError.new("No template could be found at #{path}.") unless File.exists?(path) content = File.read(path) parts = content.split("\n---\n") if parts.size == 1 fields = {} else fields = YAML.load(parts.first) content = parts.last end self.new(path, content, fields) end # Creates a new Template instance. # # @api public # # @param [ #to_s ] source the location the template was loaded from # @param [ #to_s ] content the Liquid template to render # @param [ Hash, #each ] fields the list of fields used within the template # # @example Loading a template from a database # fields = load_field_rows.inject({}) do |fields, row| # fields[row[:name]] = { "required" => row[:required], "type" => row[:type] } # end # # Noumenon::Template.new("mysql://localhost/database/table#row_32", row[:content], fields) # # @api public def initialize(source = nil, content = nil, fields = {}) @source = source @content = content @fields = fields end # Renders the template. # # @param [ Hash, #each ] page_content the content to provide to the template # @raise [ Noumenon::Template::MissingContentError ] one or more required fields were not provided # @return [ #to_s ] the rendered template # @api public def render(page_content = {}) fields_from_page = page_content.stringify_keys missing_fields = [] fields.each do |key, field| missing_fields << key if field["required"] && !fields_from_page.key?(key) end raise MissingContentError.new("The following fields were missing from your content: #{missing_fields.sort.join(", ")}") unless missing_fields.empty? Liquid::Template.parse(content).render(fields_from_page) end end