more docs
This commit is contained in:
parent
3ef11f3add
commit
5cc204e848
|
@ -1,25 +1,37 @@
|
||||||
# Shutup Tilt Warnings
|
# Shutup Tilt Warnings
|
||||||
|
# @private
|
||||||
class Tilt::Template
|
class Tilt::Template
|
||||||
def warn(*args)
|
def warn(*args)
|
||||||
# Kernel.warn(*args)
|
# Kernel.warn(*args)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Rendering extension
|
||||||
module Middleman::CoreExtensions::Rendering
|
module Middleman::CoreExtensions::Rendering
|
||||||
|
|
||||||
|
# Setup extension
|
||||||
class << self
|
class << self
|
||||||
|
|
||||||
|
# Once registered
|
||||||
def registered(app)
|
def registered(app)
|
||||||
|
# Include methods
|
||||||
app.send :include, InstanceMethods
|
app.send :include, InstanceMethods
|
||||||
|
|
||||||
# Activate custom renderers
|
# Activate custom renderers
|
||||||
app.register Middleman::Renderers::ERb
|
app.register Middleman::Renderers::ERb
|
||||||
end
|
end
|
||||||
|
|
||||||
alias :included :registered
|
alias :included :registered
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Custom error class for handling
|
||||||
class TemplateNotFound < RuntimeError
|
class TemplateNotFound < RuntimeError
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Rendering instance methods
|
||||||
module InstanceMethods
|
module InstanceMethods
|
||||||
|
|
||||||
|
# Override init to clear cache on file removal
|
||||||
def initialize
|
def initialize
|
||||||
super
|
super
|
||||||
|
|
||||||
|
@ -32,39 +44,61 @@ module Middleman::CoreExtensions::Rendering
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Render a template, with layout, given a path
|
||||||
|
#
|
||||||
|
# @param [String] path
|
||||||
|
# @param [Hash] locs
|
||||||
|
# @param [Hash] opts
|
||||||
|
# @return [String]
|
||||||
def render_template(path, locs={}, opts={})
|
def render_template(path, locs={}, opts={})
|
||||||
|
# Detect the remdering engine from the extension
|
||||||
extension = File.extname(path)
|
extension = File.extname(path)
|
||||||
engine = extension[1..-1].to_sym
|
engine = extension[1..-1].to_sym
|
||||||
|
|
||||||
|
# Store last engine for later (could be inside nested renders)
|
||||||
@current_engine, engine_was = engine, @current_engine
|
@current_engine, engine_was = engine, @current_engine
|
||||||
|
|
||||||
# Use a dup of self as a context so that instance variables set within
|
# Use a dup of self as a context so that instance variables set within
|
||||||
# the template don't persist for other templates.
|
# the template don't persist for other templates.
|
||||||
context = self.dup
|
context = self.dup
|
||||||
|
|
||||||
|
# Store current locs/opts for later
|
||||||
@current_locs = locs, @current_opts = opts
|
@current_locs = locs, @current_opts = opts
|
||||||
|
|
||||||
|
# Keep rendering template until we've used up all extensions. This handles
|
||||||
|
# cases like `style.css.sass.erb`
|
||||||
while ::Tilt[path]
|
while ::Tilt[path]
|
||||||
content = render_individual_file(path, locs, opts, context)
|
content = render_individual_file(path, locs, opts, context)
|
||||||
path = File.basename(path, File.extname(path))
|
path = File.basename(path, File.extname(path))
|
||||||
cache.set([:raw_template, path], content)
|
cache.set([:raw_template, path], content)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Certain output file types don't use layouts
|
||||||
needs_layout = !%w(.js .css .txt).include?(extension)
|
needs_layout = !%w(.js .css .txt).include?(extension)
|
||||||
|
|
||||||
|
# If we need a layout and have a layout, use it
|
||||||
if needs_layout && layout_path = fetch_layout(engine, opts)
|
if needs_layout && layout_path = fetch_layout(engine, opts)
|
||||||
content = render_individual_file(layout_path, locs, opts, context) { content }
|
content = render_individual_file(layout_path, locs, opts, context) { content }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Return result
|
||||||
content
|
content
|
||||||
ensure
|
ensure
|
||||||
|
# Pop all the saved variables from earlier as we may be returning to a
|
||||||
|
# previous render (layouts, partials, nested layouts).
|
||||||
@current_engine = engine_was
|
@current_engine = engine_was
|
||||||
@content_blocks = nil
|
@content_blocks = nil
|
||||||
@current_locs = nil
|
@current_locs = nil
|
||||||
@current_opts = nil
|
@current_opts = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
# Sinatra/Padrino render method signature.
|
# Sinatra/Padrino compatible render method signature referenced by some view
|
||||||
|
# helpers. Especially partials.
|
||||||
|
#
|
||||||
|
# @param [String, Symbol] engine
|
||||||
|
# @param [String, Symbol] data
|
||||||
|
# @param [Hash] options
|
||||||
|
# @return [String]
|
||||||
def render(engine, data, options={}, &block)
|
def render(engine, data, options={}, &block)
|
||||||
data = data.to_s
|
data = data.to_s
|
||||||
|
|
||||||
|
@ -73,30 +107,37 @@ module Middleman::CoreExtensions::Rendering
|
||||||
found_partial = false
|
found_partial = false
|
||||||
engine = nil
|
engine = nil
|
||||||
|
|
||||||
|
# If the path is known to the sitemap
|
||||||
if sitemap.exists?(current_path)
|
if sitemap.exists?(current_path)
|
||||||
page = sitemap.page(current_path)
|
page = sitemap.page(current_path)
|
||||||
current_dir = File.dirname(page.source_file)
|
current_dir = File.dirname(page.source_file)
|
||||||
engine = File.extname(page.source_file)[1..-1].to_sym
|
engine = File.extname(page.source_file)[1..-1].to_sym
|
||||||
|
|
||||||
|
# Look for partials relative to the current path
|
||||||
if current_dir != self.source_dir
|
if current_dir != self.source_dir
|
||||||
relative_dir = File.join(current_dir.sub("#{self.source_dir}/", ""), data)
|
relative_dir = File.join(current_dir.sub("#{self.source_dir}/", ""), data)
|
||||||
|
|
||||||
|
# Try to use the current engine first
|
||||||
found_partial, found_engine = resolve_template(relative_dir, :preferred_engine => engine)
|
found_partial, found_engine = resolve_template(relative_dir, :preferred_engine => engine)
|
||||||
|
|
||||||
|
# Fall back to any engine available
|
||||||
if !found_partial
|
if !found_partial
|
||||||
found_partial, found_engine = resolve_template(relative_dir)
|
found_partial, found_engine = resolve_template(relative_dir)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Look in the root for the partial with the current engine
|
||||||
if !found_partial && !engine.nil?
|
if !found_partial && !engine.nil?
|
||||||
found_partial, found_engine = resolve_template(data, :preferred_engine => engine)
|
found_partial, found_engine = resolve_template(data, :preferred_engine => engine)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Look in the root with any engine
|
||||||
if !found_partial
|
if !found_partial
|
||||||
found_partial, found_engine = resolve_template(data)
|
found_partial, found_engine = resolve_template(data)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Render the partial if found, otherwide throw exception
|
||||||
if found_partial
|
if found_partial
|
||||||
render_individual_file(found_partial, locals, options, self, &block)
|
render_individual_file(found_partial, locals, options, self, &block)
|
||||||
else
|
else
|
||||||
|
@ -104,34 +145,53 @@ module Middleman::CoreExtensions::Rendering
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# @private
|
# Render an on-disk file. Used for everything, including layouts.
|
||||||
|
#
|
||||||
|
# @param [String, Symbol] path
|
||||||
|
# @param [Hash] locs
|
||||||
|
# @param [Hash] opts
|
||||||
|
# @param [Class] context
|
||||||
|
# @return [String]
|
||||||
def render_individual_file(path, locs = {}, opts = {}, context = self, &block)
|
def render_individual_file(path, locs = {}, opts = {}, context = self, &block)
|
||||||
path = path.to_s
|
path = path.to_s
|
||||||
|
|
||||||
|
# Save current buffere for later
|
||||||
@_out_buf, _buf_was = "", @_out_buf
|
@_out_buf, _buf_was = "", @_out_buf
|
||||||
|
|
||||||
|
# Read from disk or cache the contents of the file
|
||||||
body = cache.fetch(:raw_template, path) do
|
body = cache.fetch(:raw_template, path) do
|
||||||
File.read(path)
|
File.read(path)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Merge per-extension options from config
|
||||||
extension = File.extname(path)
|
extension = File.extname(path)
|
||||||
options = opts.merge(options_for_ext(extension))
|
options = opts.merge(options_for_ext(extension))
|
||||||
options[:outvar] ||= '@_out_buf'
|
options[:outvar] ||= '@_out_buf'
|
||||||
|
|
||||||
|
# Read compiled template from disk or cache
|
||||||
template = cache.fetch(:compiled_template, options, body) do
|
template = cache.fetch(:compiled_template, options, body) do
|
||||||
::Tilt.new(path, 1, options) { body }
|
::Tilt.new(path, 1, options) { body }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Render using Tilt
|
||||||
template.render(context, locs, &block)
|
template.render(context, locs, &block)
|
||||||
ensure
|
ensure
|
||||||
|
# Reset stored buffer
|
||||||
@_out_buf = _buf_was
|
@_out_buf = _buf_was
|
||||||
end
|
end
|
||||||
|
|
||||||
# @private
|
# Get a hash of configuration options for a given file extension, from
|
||||||
|
# config.rb
|
||||||
|
#
|
||||||
|
# @param [String] ext
|
||||||
|
# @return [Hash]
|
||||||
def options_for_ext(ext)
|
def options_for_ext(ext)
|
||||||
|
# Read options for extension from config/Tilt or cache
|
||||||
cache.fetch(:options_for_ext, ext) do
|
cache.fetch(:options_for_ext, ext) do
|
||||||
options = {}
|
options = {}
|
||||||
|
|
||||||
|
# Find all the engines which handle this extension in tilt. Look for
|
||||||
|
# config variables of that name and merge it
|
||||||
extension_class = ::Tilt[ext]
|
extension_class = ::Tilt[ext]
|
||||||
::Tilt.mappings.each do |ext, engines|
|
::Tilt.mappings.each do |ext, engines|
|
||||||
next unless engines.include? extension_class
|
next unless engines.include? extension_class
|
||||||
|
@ -143,13 +203,21 @@ module Middleman::CoreExtensions::Rendering
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# @private
|
# Find a layout for a given engine
|
||||||
|
#
|
||||||
|
# @param [Symbol] engine
|
||||||
|
# @param [Hash] opts
|
||||||
|
# @return [String]
|
||||||
def fetch_layout(engine, opts)
|
def fetch_layout(engine, opts)
|
||||||
|
# The layout name comes from either the system default or the options
|
||||||
local_layout = opts.has_key?(:layout) ? opts[:layout] : layout
|
local_layout = opts.has_key?(:layout) ? opts[:layout] : layout
|
||||||
return false unless local_layout
|
return false unless local_layout
|
||||||
|
|
||||||
|
# Look for engine-specific options
|
||||||
engine_options = respond_to?(engine) ? send(engine) : {}
|
engine_options = respond_to?(engine) ? send(engine) : {}
|
||||||
|
|
||||||
|
# The engine for the layout can be set in options, engine_options or passed
|
||||||
|
# into this method
|
||||||
layout_engine = if opts.has_key?(:layout_engine)
|
layout_engine = if opts.has_key?(:layout_engine)
|
||||||
opts[:layout_engine]
|
opts[:layout_engine]
|
||||||
elsif engine_options.has_key?(:layout_engine)
|
elsif engine_options.has_key?(:layout_engine)
|
||||||
|
@ -158,7 +226,7 @@ module Middleman::CoreExtensions::Rendering
|
||||||
engine
|
engine
|
||||||
end
|
end
|
||||||
|
|
||||||
# Automatic
|
# Automatic mode
|
||||||
if local_layout == :_auto_layout
|
if local_layout == :_auto_layout
|
||||||
# Look for :layout of any extension
|
# Look for :layout of any extension
|
||||||
# If found, use it. If not, continue
|
# If found, use it. If not, continue
|
||||||
|
@ -174,10 +242,15 @@ module Middleman::CoreExtensions::Rendering
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# @private
|
# Find a layout on-disk, optionally using a specific engine
|
||||||
|
# @param [String] name
|
||||||
|
# @param [Symbol] preferred_engine
|
||||||
|
# @return [String]
|
||||||
def locate_layout(name, preferred_engine=nil)
|
def locate_layout(name, preferred_engine=nil)
|
||||||
|
# Whether we've found the layout
|
||||||
layout_path = false
|
layout_path = false
|
||||||
|
|
||||||
|
# If we prefer a specific engine
|
||||||
if !preferred_engine.nil?
|
if !preferred_engine.nil?
|
||||||
# Check root
|
# Check root
|
||||||
layout_path, layout_engine = resolve_template(name, :preferred_engine => preferred_engine)
|
layout_path, layout_engine = resolve_template(name, :preferred_engine => preferred_engine)
|
||||||
|
@ -198,38 +271,52 @@ module Middleman::CoreExtensions::Rendering
|
||||||
layout_path, layout_engine = resolve_template(File.join("layouts", name.to_s))
|
layout_path, layout_engine = resolve_template(File.join("layouts", name.to_s))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Return the path
|
||||||
layout_path
|
layout_path
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Allow layouts to be wrapped in the contents of other layouts
|
||||||
|
# @param [String, Symbol] layout_name
|
||||||
|
# @return [void]
|
||||||
def wrap_layout(layout_name, &block)
|
def wrap_layout(layout_name, &block)
|
||||||
content = capture(&block) if block_given?
|
content = capture(&block) if block_given?
|
||||||
layout_path = locate_layout(layout_name, current_engine)
|
layout_path = locate_layout(layout_name, current_engine)
|
||||||
concat render_individual_file(layout_path, @current_locs || {}, @current_opts || {}, self) { content }
|
concat render_individual_file(layout_path, @current_locs || {}, @current_opts || {}, self) { content }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# The currently rendering engine
|
||||||
|
# @return [Symbol, nil]
|
||||||
def current_engine
|
def current_engine
|
||||||
@current_engine ||= nil
|
@current_engine ||= nil
|
||||||
end
|
end
|
||||||
|
|
||||||
# @private
|
# Find a template on disk given a output path
|
||||||
|
# @param [String] request_path
|
||||||
|
# @param [Hash] options
|
||||||
|
# @return [Array<String, Symbol>, Boolean]
|
||||||
def resolve_template(request_path, options={})
|
def resolve_template(request_path, options={})
|
||||||
|
# Find the path by searching or using the cache
|
||||||
request_path = request_path.to_s
|
request_path = request_path.to_s
|
||||||
cache.fetch(:resolve_template, request_path, options) do
|
cache.fetch(:resolve_template, request_path, options) do
|
||||||
relative_path = request_path.sub(%r{^/}, "")
|
relative_path = request_path.sub(%r{^/}, "")
|
||||||
on_disk_path = File.expand_path(relative_path, self.source_dir)
|
on_disk_path = File.expand_path(relative_path, self.source_dir)
|
||||||
|
|
||||||
|
# By default, any engine will do
|
||||||
preferred_engine = "*"
|
preferred_engine = "*"
|
||||||
|
|
||||||
|
# Unless we're specifically looking for a preferred engine
|
||||||
if options.has_key?(:preferred_engine)
|
if options.has_key?(:preferred_engine)
|
||||||
extension_class = ::Tilt[options[:preferred_engine]]
|
extension_class = ::Tilt[options[:preferred_engine]]
|
||||||
matched_exts = []
|
matched_exts = []
|
||||||
|
|
||||||
|
# Get a list of extensions for a preferred engine
|
||||||
# TODO: Cache this
|
# TODO: Cache this
|
||||||
::Tilt.mappings.each do |ext, engines|
|
::Tilt.mappings.each do |ext, engines|
|
||||||
next unless engines.include? extension_class
|
next unless engines.include? extension_class
|
||||||
matched_exts << ext
|
matched_exts << ext
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Change the glob to only look for the matched extensions
|
||||||
if matched_exts.length > 0
|
if matched_exts.length > 0
|
||||||
preferred_engine = "{" + matched_exts.join(",") + "}"
|
preferred_engine = "{" + matched_exts.join(",") + "}"
|
||||||
else
|
else
|
||||||
|
@ -237,11 +324,13 @@ module Middleman::CoreExtensions::Rendering
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Look for files that match
|
||||||
path_with_ext = on_disk_path + "." + preferred_engine
|
path_with_ext = on_disk_path + "." + preferred_engine
|
||||||
found_path = Dir[path_with_ext].find do |path|
|
found_path = Dir[path_with_ext].find do |path|
|
||||||
::Tilt[path]
|
::Tilt[path]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# If we found one, return it and the found engine
|
||||||
if found_path || (File.exists?(on_disk_path) && !File.directory?(on_disk_path))
|
if found_path || (File.exists?(on_disk_path) && !File.directory?(on_disk_path))
|
||||||
engine = found_path ? File.extname(found_path)[1..-1].to_sym : nil
|
engine = found_path ? File.extname(found_path)[1..-1].to_sym : nil
|
||||||
[ found_path || on_disk_path, engine ]
|
[ found_path || on_disk_path, engine ]
|
||||||
|
|
|
@ -1,17 +1,30 @@
|
||||||
|
# Routing extension
|
||||||
module Middleman::CoreExtensions::Routing
|
module Middleman::CoreExtensions::Routing
|
||||||
|
|
||||||
|
# Setup extension
|
||||||
class << self
|
class << self
|
||||||
|
|
||||||
|
# Once registered
|
||||||
def registered(app)
|
def registered(app)
|
||||||
|
# Include methods
|
||||||
app.send :include, InstanceMethods
|
app.send :include, InstanceMethods
|
||||||
end
|
end
|
||||||
|
|
||||||
alias :included :registered
|
alias :included :registered
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Routing instance methods
|
||||||
module InstanceMethods
|
module InstanceMethods
|
||||||
|
|
||||||
# Takes a block which allows many pages to have the same layout
|
# Takes a block which allows many pages to have the same layout
|
||||||
# with_layout :admin do
|
#
|
||||||
# page "/admin/"
|
# with_layout :admin do
|
||||||
# page "/admin/login.html"
|
# page "/admin/"
|
||||||
# end
|
# page "/admin/login.html"
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# @param [String, Symbol] layout_name
|
||||||
|
# @return [void]
|
||||||
def with_layout(layout_name, &block)
|
def with_layout(layout_name, &block)
|
||||||
old_layout = layout
|
old_layout = layout
|
||||||
|
|
||||||
|
@ -22,16 +35,26 @@ module Middleman::CoreExtensions::Routing
|
||||||
end
|
end
|
||||||
|
|
||||||
# The page method allows the layout to be set on a specific path
|
# The page method allows the layout to be set on a specific path
|
||||||
# page "/about.html", :layout => false
|
#
|
||||||
# page "/", :layout => :homepage_layout
|
# page "/about.html", :layout => false
|
||||||
|
# page "/", :layout => :homepage_layout
|
||||||
|
#
|
||||||
|
# @param [String] url
|
||||||
|
# @param [Hash] opts
|
||||||
|
# @return [void]
|
||||||
def page(url, opts={}, &block)
|
def page(url, opts={}, &block)
|
||||||
a_block = block_given? ? block : nil
|
a_block = block_given? ? block : nil
|
||||||
|
|
||||||
|
# If the url is a string with an asterisk, it is a glob and should
|
||||||
|
# be converted to a Regexp
|
||||||
if url.include?("*")
|
if url.include?("*")
|
||||||
url = Regexp.new(url.gsub("*", "(.*?)").gsub(/^\//, "^"))
|
url = Regexp.new(url.gsub("*", "(.*?)").gsub(/^\//, "^"))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# If the url is a regexp
|
||||||
if url.is_a?(Regexp)
|
if url.is_a?(Regexp)
|
||||||
|
|
||||||
|
# Use the metadata loop for matching against paths at runtime
|
||||||
provides_metadata_for_path url do |url|
|
provides_metadata_for_path url do |url|
|
||||||
{ :options => opts, :blocks => [a_block] }
|
{ :options => opts, :blocks => [a_block] }
|
||||||
end
|
end
|
||||||
|
@ -39,10 +62,13 @@ module Middleman::CoreExtensions::Routing
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Default layout
|
||||||
opts[:layout] = layout if opts[:layout].nil?
|
opts[:layout] = layout if opts[:layout].nil?
|
||||||
|
|
||||||
|
# Normalized path
|
||||||
url = full_path(url)
|
url = full_path(url)
|
||||||
|
|
||||||
|
# Setup proxy
|
||||||
if opts.has_key?(:proxy)
|
if opts.has_key?(:proxy)
|
||||||
reroute(url, opts[:proxy])
|
reroute(url, opts[:proxy])
|
||||||
|
|
||||||
|
@ -58,8 +84,11 @@ module Middleman::CoreExtensions::Routing
|
||||||
opts.delete(:ignore)
|
opts.delete(:ignore)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# If we have a block or opts
|
||||||
if a_block || !opts.empty?
|
if a_block || !opts.empty?
|
||||||
|
|
||||||
|
# Setup a metadata matcher for rendering those options
|
||||||
provides_metadata_for_path url do |url|
|
provides_metadata_for_path url do |url|
|
||||||
{ :options => opts, :blocks => [a_block] }
|
{ :options => opts, :blocks => [a_block] }
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue