rawinclude
In jekyll, the tag include
can be used to include a parameterized template file. These parameters, however, are constrained to being supplied inline and constant, hence no captured text blocks can be passed. The custom tag rawinclude
(see code below) allows for the instantiation of template files using the standard include
parameters, but also passes a whole text block (without rendering liquid code in between) to the template file. Additionally, the block content is also passed as URI data encoded string which further allows for client-side download links for e.g. code snippets.
Example
In the following example, rawinclude
is exploited to pass the given python code to a template called sourcefile
which, in turn, highlights the syntax and provides a download link for the raw code.
Implementation
The implementation is largely based on raw.rb to read the content in between without modification. After that necessary encoding and escaping of the passed arguments takes place and finally include
is used with the block contents wrapped into in-line parameters.
As include
disallows ‘%}’ to be contained in the string, unliquidify
is required to be run on the contents to restore the original format.
require 'uri'
module Jekyll
module LiquidFilters
def liquidify(str)
str.gsub('%\\}', '%}')
end
def unliquidify(str)
str.gsub('%}', '%\\}')
end
end
class RawInclude < Liquid::Block
include LiquidFilters
FullTokenPossiblyInvalid = /\A(.*)#{Liquid::TagStart}\s*(\w+)\s*(.*)?#{Liquid::TagEnd}\z/om
def initialize(tag_name, params, tokens)
super
@params = params
end
def parse(tokens)
@body = ''
while token = tokens.shift
if token =~ FullTokenPossiblyInvalid
@body << Regexp.last_match(1) if Regexp.last_match(1) != ''.freeze
return if block_delimiter == Regexp.last_match(2)
end
@body << token unless token.empty?
end
raise Liquid::SyntaxError,
parse_context.locale.t('errors.syntax.tag_never_closed'.freeze, block_name: block_name)
end
def render(context)
uri_data = escape_quotes(URI.escape(@body.slice(1, @body.length)))
Liquid::Template.parse("{% include #{@params}
block='#{escape_quotes(unliquidify(@body))}'
uri_data='#{uri_data}' %}").render(context)
end
def nodelist
[@body]
end
def blank?
@body.empty?
end
protected
def escape_quotes(str)
str.gsub("'", "\\\\'")
end
end
Liquid::Template.register_filter(LiquidFilters)
Liquid::Template.register_tag('rawinclude'.freeze, RawInclude)
end