require 'awestruct/handler_chain' require 'awestruct/util/yaml_load' require 'awestruct/handlers/base_tilt_handler' require 'awestruct/handlers/file_handler' require 'awestruct/handlers/layout_handler' require 'awestruct/handlers/template/asciidoc' require 'yaml' require 'tilt' module Awestruct module Handlers class AsciidoctorTiltMatcher < TiltMatcher # Use a lightweight lookup to avoid loading Tilt templates for # non-matching paths. Once we are sure this is a match, then # attempt to load the Tilt template for AsciiDoc files. def match(path) extensions = ['ad', 'adoc', 'asciidoc'] if (extensions.include? File.extname(path)[1..-1]) return true end false end end class AsciidoctorHandler < TiltHandler CHAIN = Awestruct::HandlerChain.new( Awestruct::Handlers::AsciidoctorTiltMatcher.new(), Awestruct::Handlers::FileHandler, Awestruct::Handlers::FrontMatterHandler, Awestruct::Handlers::InterpolationHandler, Awestruct::Handlers::AsciidoctorHandler, Awestruct::Handlers::LayoutHandler ) def initialize(site, delegate) super( site, delegate ) @site = site @front_matter = front_matter end def front_matter @front_matter ||= {} parse_header() if @delegate @front_matter.update @delegate.front_matter # push front matter forward as well @delegate.front_matter.replace @front_matter @front_matter end end def raw_content parse_header() @delegate.raw_content if @delegate end def content_line_offset parse_header() @delegate.content_line_offset if @delegate end def rendered_content(context, with_layouts) parse_header() front_matter_ref = front_matter types = [String, Numeric, TrueClass, FalseClass, Array] front_matter_ref.update(context.page.inject({}) {|hash, (k,v)| hash[k.to_s] = v if not k.to_s.start_with?('__') and types.detect { |t| v.kind_of? t } hash }) if with_layouts && !context.page.layout front_matter_ref['header_footer'] = true end super end def options opts = super opts[:attributes] ||= {} opts[:attributes].update(@front_matter.inject({}) {|collector, (key,val)| collector["page-#{key}"] = "#{val}@" collector }) # Keep only values that can be coerced to as string types = [String, Numeric, TrueClass, FalseClass, Date, Time] opts[:attributes].update(@site.inject({}) {|collector, (key,val)| collector["site-#{key}"] = "#{val}@" if types.detect {|t| val.kind_of? t } collector }) opts[:attributes]['env'] = @site opts[:attributes]['env-site'] = true opts[:attributes]['awestruct-version'] = Awestruct::VERSION if @front_matter['header_footer'] opts[:header_footer] = true end path_expanded = File.expand_path path opts[:attributes]['docdir'] = File.dirname path_expanded opts[:attributes]['docfile'] = path_expanded opts[:attributes]['docname'] = simple_name path_mtime = path.mtime opts[:attributes]['docdate'] = docdate = path_mtime.strftime('%Y-%m-%d') opts[:attributes]['doctime'] = doctime = path_mtime.strftime('%H:%M:%S %Z') opts[:attributes]['docdatetime'] = %(#{docdate} #{doctime}) # TODO once Asciidoctor 1.5.0 is release, we should set the base_dir as a jail # we can't do this before 1.5.0 due to a bug in how includes are resolved if (Object.const_defined? 'Asciidoctor') && Asciidoctor::VERSION >= '1.5.0' opts[:base_dir] ||= @site.config.dir end opts end def content_line_offset parse_header() @content_line_offset end def inherit_front_matter(page) parse_header() page.inherit_front_matter_from(front_matter) super end def parse_header return if @parsed_parts content = delegate.raw_content unless content.nil? @front_matter = parse_document_attributes(content) end @parsed_parts = true end def parse_document_attributes(content) template = ::Tilt::new(delegate.path.to_s, delegate.content_line_offset + 1, options) headers = template.parse_headers(content, /^(?:page|awestruct)\-(?=.)/).inject({'interpolate' => false}) do |hash, (k,v)| unless v.nil? hash[k] = v.empty? ? v : Awestruct.yaml_load(v) if hash[k].kind_of? Time # use DateTime to preserve timezone information hash[k] = DateTime.parse(v) end end hash end headers end end end end