require 'sinatra' require 'liquid' require 'yaml' # :nodoc: module Noumenon # The base of all Noumenon sites, responsible for building the URL structure, and providing # access to the content repository. # # By default any request will be routed to the content repository to find an item of content # using #locate_content, which will then be rendered using the template specified by the content # file. # # A piece of content takes the form of a YAML file, which should provide any fields required by # template specified. If any required fields are missing then a MissingContentError will be raised. # class Core < Sinatra::Base def config Noumenon.config end # Get the path to a file in the content repository. def content_path(path) File.join(config.content_repository_path, path) end # Get the path to a file provided by the current theme. def theme_path(path) File.join(Noumenon.themes[config.theme][:path], path) end # Render a template, surrounding it with the theme layout if a theme has been set, and # it provides a layout. # # Any locals provided will be passed onto Liquid for use in the template. # # Valid options: # # layout: If set to a string, an attempt will be made to use that layout. # If set to false no layout will be used. # If set to true (the default) the default layout will be used. def render_template(path, locals = {}, options = {}) options[:layout] = "layout" if options[:layout] == true || options[:layout].nil? template = Template.from_file(path) body = template.render(locals) if options[:layout] begin body = render_template(theme_path("templates/#{options[:layout]}.html"), { 'body' => body }, :layout => false) rescue Noumenon::Template::NotFoundError => ignore_missing_layouts end end body end # Render a piece of content from the content repository, using the template specified within # the content item. # # If no template was specified then the template "default" will be used. def render_content(path) content = File.read(content_path(path)) item = YAML.load( content ) [ 200, render_template( theme_path("templates/#{item["template"]}.html"), item ) ] end # Locate a piece of content in the content repository based on the URL it is being accessed from. # # The following files will satisfy a request for "/example", in the order shown: # # 1. "repository/example.yml # 2. "repository/example/index.yml # def locate_content_for(path) if File.exists?(content_path("#{path}.yml")) return "#{path}.yml" else if File.exists?(content_path(path)) && File.exists?(content_path("#{path}/index.yml")) return "#{path}/index.yml" end end nil end get '*' do |path| path = locate_content_for(path) path ? render_content(path) : [ 404, "Not Found" ] end end end