IncludeTag: implement multiple load paths

This commit is contained in:
Parker Moore 2016-03-25 17:44:29 -07:00
parent 0920d2b48a
commit 33255e3ac3
No known key found for this signature in database
GPG Key ID: 193CDEBA72063C58
3 changed files with 42 additions and 36 deletions

View File

@ -11,7 +11,7 @@ module Jekyll
:gems, :plugin_manager, :theme
attr_accessor :converters, :generators, :reader
attr_reader :regenerator, :liquid_renderer
attr_reader :regenerator, :liquid_renderer, :includes_load_paths
# Public: Initialize a new Site.
#
@ -52,8 +52,12 @@ module Jekyll
self.plugin_manager = Jekyll::PluginManager.new(self)
self.plugins = plugin_manager.plugins_path
self.theme = nil
self.theme = Jekyll::Theme.new(config["theme"]) if config["theme"]
@includes_load_paths = Array(in_source_dir(config["includes_dir"].to_s))
@includes_load_paths << theme.includes_path if self.theme
self.file_read_opts = {}
self.file_read_opts[:encoding] = config['encoding'] if config['encoding']

View File

@ -12,8 +12,6 @@ module Jekyll
end
class IncludeTag < Liquid::Tag
attr_reader :includes_dir
VALID_SYNTAX = /([\w-]+)\s*=\s*(?:"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|([\w\.-]+))/
VARIABLE_SYNTAX = /(?<variable>[^{]*(\{\{\s*[\w\-\.]+\s*(\|.*)?\}\}[^\s{}]*)+)(?<params>.*)/
@ -98,20 +96,29 @@ eos
end
end
def tag_includes_dir(context)
context.registers[:site].config['includes_dir'].freeze
def tag_includes_dirs(context)
context.registers[:site].includes_load_paths.freeze
end
def locate_include_file(context, file, safe)
includes_dirs = tag_includes_dirs(context)
includes_dirs.each do |dir|
path = File.join(dir, file)
return path if valid_include_file?(path, dir, safe)
end
raise IOError, "Could not locate the included file '#{file}' in any of #{includes_dirs}." \
" Ensure it exists in one of those directories and, if it is a symlink, " \
"does not point outside your site source."
end
def render(context)
site = context.registers[:site]
@includes_dir = tag_includes_dir(context)
dir = resolved_includes_dir(context)
file = render_variable(context) || @file
validate_file_name(file)
path = File.join(dir, file)
validate_path(path, dir, site.safe)
path = locate_include_file(context, file, site.safe)
return unless path
# Add include to dependency tree
if context.registers[:page] && context.registers[:page].key?("path")
@ -121,16 +128,16 @@ eos
)
end
begin
#begin
partial = load_cached_partial(path, context)
context.stack do
context['include'] = parse_params(context) if @params
partial.render!(context)
end
rescue => e
raise IncludeTagError.new e.message, File.join(@includes_dir, @file)
end
#rescue => e
#raise IncludeTagError.new e.message, path
#end
end
def load_cached_partial(path, context)
@ -144,24 +151,18 @@ eos
end
end
def resolved_includes_dir(context)
context.registers[:site].in_source_dir(@includes_dir)
def valid_include_file?(path, dir, safe)
!(outside_site_source?(path, dir, safe) || !File.exist?(path))
end
def validate_path(path, dir, safe)
if safe && !realpath_prefixed_with?(path, dir)
raise IOError.new "The included file '#{path}' should exist and should not be a symlink"
elsif !File.exist?(path)
raise IOError.new "Included file '#{path_relative_to_source(dir, path)}' not found"
end
end
def path_relative_to_source(dir, path)
File.join(@includes_dir, path.sub(Regexp.new("^#{dir}"), ""))
def outside_site_source?(path, dir, safe)
safe && !realpath_prefixed_with?(path, dir)
end
def realpath_prefixed_with?(path, dir)
File.exist?(path) && File.realpath(path).start_with?(dir)
rescue
false
end
# This method allows to modify the file content by inheriting from the class.
@ -171,16 +172,17 @@ eos
end
class IncludeRelativeTag < IncludeTag
def tag_includes_dir(context)
'.'.freeze
def tag_includes_dirs(context)
Array(page_path(context)).freeze
end
def page_path(context)
context.registers[:page].nil? ? includes_dir : File.dirname(context.registers[:page]["path"])
if context.registers[:page].nil?
context.registers[:site].source
else
current_doc_dir = File.dirname(context.registers[:page]["path"])
context.registers[:site].in_source_dir current_doc_dir
end
def resolved_includes_dir(context)
context.registers[:site].in_source_dir(page_path(context))
end
end
end

View File

@ -615,7 +615,7 @@ title: Include symlink
CONTENT
create_post(content, {'permalink' => 'pretty', 'source' => source_dir, 'destination' => dest_dir, 'read_posts' => true, 'safe' => true })
end
assert_match /should exist and should not be a symlink/, ex.message
assert_match "Could not locate the included file 'tmp/pages-test-does-not-exist' in any of [\"/Users/parkr/jekyll/jekyll/test/source/_includes\"].", ex.message
end
end
@ -756,7 +756,7 @@ CONTENT
exception = assert_raises IOError do
create_post(@content, {'permalink' => 'pretty', 'source' => source_dir, 'destination' => dest_dir, 'read_posts' => true})
end
assert_equal 'Included file \'_includes/missing.html\' not found', exception.message
assert_match "Could not locate the included file 'missing.html' in any of [\"#{source_dir}/_includes\"].", exception.message
end
end
@ -838,7 +838,7 @@ CONTENT
exception = assert_raises IOError do
create_post(@content, {'permalink' => 'pretty', 'source' => source_dir, 'destination' => dest_dir, 'read_posts' => true})
end
assert_equal 'Included file \'./missing.html\' not found', exception.message
assert_match "Could not locate the included file 'missing.html' in any of [\"#{source_dir}\"].", exception.message
end
end
@ -892,7 +892,7 @@ title: Include symlink
CONTENT
create_post(content, {'permalink' => 'pretty', 'source' => source_dir, 'destination' => dest_dir, 'read_posts' => true, 'safe' => true })
end
assert_match /should exist and should not be a symlink/, ex.message
assert_match /Ensure it exists in one of those directories and, if it is a symlink, does not point outside your site source./, ex.message
end
end
end