middleman/lib/middleman/core_extensions/front_matter.rb
2011-11-17 13:21:05 -08:00

138 lines
3.4 KiB
Ruby

require "yaml"
require "tilt"
module Middleman::CoreExtensions::FrontMatter
class << self
def registered(app)
app.extend ClassMethods
app.send :include, InstanceMethods
app.file_changed FrontMatter.matcher do |file|
frontmatter.touch_file(file)
end
app.file_deleted do |file|
frontmatter.remove_file(file)
end
app.after_configuration do
app.before_processing(:front_matter) do |result|
if result && Tilt.mappings.has_key?(result[1].to_s)
extensionless_path, template_engine = result
full_file_path = "#{extensionless_path}.#{template_engine}"
if app.frontmatter.has_data?(full_file_path)
data = app.frontmatter.data(full_file_path).first
request['custom_options'] = {}
%w(layout layout_engine).each do |opt|
if data.has_key?(opt)
request['custom_options'][opt.to_sym] = data[opt]
end
end
else
data = {}
end
# Forward remaining data to helpers
app.data_content("page", data)
end
true
end
end
end
alias :included :registered
end
module ClassMethods
def frontmatter
@frontmatter ||= FrontMatter.new(self)
end
end
module InstanceMethods
def frontmatter
settings.frontmatter
end
end
class FrontMatter
def self.matcher
%r{source/.*\.html}
end
def initialize(app)
@app = app
@source ||= File.expand_path(@app.views, @app.root)
@local_data = {}
Dir[File.join(@source, "**/*")].each do |file|
next if file.match(/\/\./) ||
(file.match(/\/_/) && !file.match(/\/__/)) ||
!file.match(self.class.matcher)
touch_file(file.sub(@app.root, "").sub(/^\//, ""))
end
end
def has_data?(path)
@local_data.has_key?(path.to_s)
end
def touch_file(file)
extension = File.extname(file).sub(/\./, "")
return unless ::Tilt.mappings.has_key?(extension)
file = File.expand_path(file, @app.root)
content = File.read(file)
file = file.sub(@source, "")
@app.logger.debug :frontmatter_update, Time.now, file if @app.settings.logging?
result = parse_front_matter(content)
if result
@local_data[file] = result
path = @app.extensionless_path(file)
@app.settings.templates[path.to_sym] = [result[1], path.to_s, 1]
end
end
def remove_file(file)
file = File.expand_path(file, @app.root)
file = file.sub(@source, "")
@app.logger.debug :frontmatter_remove, Time.now, file if @app.settings.logging?
if @local_data.has_key?(file)
@local_data.delete(file)
end
end
def data(path)
if @local_data.has_key?(path.to_s)
@local_data[path.to_s]
else
nil
end
end
private
def parse_front_matter(content)
yaml_regex = /^(---\s*\n.*?\n?)^(---\s*$\n?)/m
if content =~ yaml_regex
begin
data = YAML.load($1)
rescue => e
puts "YAML Exception: #{e.message}"
return false
end
content = content.split(yaml_regex).last
else
return false
end
[data, content]
end
end
end