Compare commits

...

14 Commits

Author SHA1 Message Date
Pat Hawks
177c1ca09e Cache all the things! 2018-08-13 13:17:14 -05:00
Pat Hawks
07c5ddde4a Cache converted Markdown 2018-08-07 16:26:49 -05:00
Pat Hawks
4081044178 Cache highlights 2018-08-07 16:25:50 -05:00
Pat Hawks
139073430f Fix stupid test in a stupid way 2018-08-07 16:19:46 -05:00
Pat Hawks
575bf51fa2 Use disk for persistent cache 2018-08-07 16:19:32 -05:00
Pat Hawks
62a65cd358 Always clear cache when config changes 2018-08-04 14:27:11 -05:00
Pat Hawks
3e077bba84 Use key? instead of has_key? because Rubocop 2018-08-04 14:27:10 -05:00
Pat Hawks
2b33b3df23 Add getset method 2018-08-04 14:27:10 -05:00
Pat Hawks
f7d5559f10 Add basic Cache class 2018-08-04 14:27:07 -05:00
Ashwin Maroli
66ac9b8675 Merge 'master' into optimize-liquid-rendering 2018-07-24 23:23:16 +05:30
Ashwin Maroli
f034f624cc document change in behavior and its workaround 2018-07-17 09:58:18 +05:30
Ashwin Maroli
c58d747506 Merge branch 'master' of https://github.com/jekyll/jekyll into optimize-liquid-rendering 2018-07-17 08:50:53 +05:30
Ashwin Maroli
cf9176efba parse variables in our tags directly via Liquid 2018-07-16 14:15:35 +05:30
Ashwin Maroli
8256ff1757 cache templates parsed by Liquid 2018-07-16 14:14:58 +05:30
11 changed files with 189 additions and 28 deletions

View File

@@ -19,9 +19,32 @@ If you're using Ruby >= 2.3.0, go ahead and fetch the latest version of Jekyll:
gem update jekyll
```
---
*Insert sections here*
### Template rendering
We've slightly altered the way Jekyll parses and renders your various templates to improve
the overall build times. Jekyll now parses a template once, caches it internally and then
renders the parsed template multiple times as required by your pages and documents.
The downside to this is that some of the community-authored plugins may not work as they
previously used to.
#### For Plugin-authors
* If your plugin depends on the following code: `site.liquid_renderer.file(path).parse(content)`,
note that the return value (`template`, an instance of *`Liquid::Template`*), from that line will
always be the **same object** for a given `path`. <br/>
The *`template`* instance is then rendered as previously, with respect to the `payload` passed to it.
You'll therefore have to ensure that *`payload`* is not memoized or cached in your plugin instance.
* If its a requirement that `template` you get from the above step *be different* at all times,
you can invoke *`Liquid::Template`* directly:
```diff
- template = site.liquid_renderer.file(path).parse(content)
+ template = Liquid::Template.parse(content)
```
---

View File

@@ -54,6 +54,7 @@ module Jekyll
autoload :FrontmatterDefaults, "jekyll/frontmatter_defaults"
autoload :Hooks, "jekyll/hooks"
autoload :Layout, "jekyll/layout"
autoload :Cache, "jekyll/cache"
autoload :CollectionReader, "jekyll/readers/collection_reader"
autoload :DataReader, "jekyll/readers/data_reader"
autoload :LayoutReader, "jekyll/readers/layout_reader"

127
lib/jekyll/cache.rb Normal file
View File

@@ -0,0 +1,127 @@
# frozen_string_literal: true
require "digest"
require "fileutils"
require "pstore"
module Jekyll
class Cache
extend Forwardable
# Get an existing named cache, or create a new one if none exists
#
# name - name of the cache
#
# Returns nothing.
# rubocop:disable Style/ClassVars
def initialize(name)
@@base_dir ||= File.expand_path(".jekyll-cache/Jekyll/Cache")
@@caches ||= {}
@cache = @@caches[name] ||= {}
@name = name
FileUtils.mkdir_p(path_to)
end
def self.clear
delete_cache_files
@@caches.each_value(&:clear)
end
# rubocop:enable Style/ClassVars
def clear
delete_cache_files
@cache.clear
end
def [](key)
return @cache[key] if @cache.key?(key)
path = path_to(hash(key))
if File.file?(path) && File.readable?(path)
@cache[key] = load(path)
else
raise
end
end
def getset(key)
return @cache[key] if @cache.key?(key)
path = path_to(hash(key))
if File.file?(path) && File.readable?(path)
value = load(path)
else
value = yield
dump(path, value)
end
@cache[key] = value
end
def []=(key, value)
@cache[key] = value
path = path_to(hash(key))
dump(path, value)
end
def delete(key)
@cache.delete(key)
path = path_to(hash(key))
File.delete(path)
end
def key?(key)
return true if @cache.key?(key)
path = path_to(hash(key))
File.file?(path) && File.readable?(path)
end
# rubocop:disable Style/ClassVars
def self.clear_if_config_changed(config)
config = config.inspect
cache = Jekyll::Cache.new "Jekyll::Cache"
unless cache.key?("config") && cache["config"] == config
delete_cache_files
@@caches = {}
cache = Jekyll::Cache.new "Jekyll::Cache"
cache["config"] = config
end
end
# rubocop:enable Style/ClassVars
private
def path_to(hash = nil)
@base_dir ||= File.join(@@base_dir, @name)
return @base_dir if hash.nil?
File.join(@base_dir, hash[0..1], hash[2..-1]).freeze
end
def hash(key)
Digest::SHA2.hexdigest(key).freeze
end
def delete_cache_files
FileUtils.rm_rf(path_to)
end
# rubocop:disable Security/MarshalLoad
def load(path)
cached_file = File.open(path, "rb")
value = Marshal.load(cached_file)
cached_file.close
value
end
# rubocop:enable Security/MarshalLoad
def dump(path, value)
dir, _file = File.split(path)
FileUtils.mkdir_p(dir)
cached_file = File.open(path, "wb")
Marshal.dump(value, cached_file)
cached_file.close
end
def self.delete_cache_files
FileUtils.rm_rf(@@base_dir)
end
private_class_method :delete_cache_files
end
end

View File

@@ -19,6 +19,8 @@ module Jekyll
raise Errors::FatalException, "Bailing out; invalid Markdown processor."
end
@cache = Jekyll::Cache.new("Jekyll::Converters::Markdown")
@setup = true
end
@@ -70,7 +72,9 @@ module Jekyll
def convert(content)
setup
@parser.convert(content)
@cache.getset(content) do
@parser.convert(content)
end
end
private

View File

@@ -53,7 +53,7 @@ module Jekyll
private
def filename_regex
@filename_regex ||= %r!\A(#{source_dir}/|#{theme_dir}/|\W*)(.*)!i
@filename_regex ||= %r!\A(#{source_dir}/|#{theme_dir}/|/*)(.*)!i
end
def new_profile_hash

View File

@@ -10,7 +10,9 @@ module Jekyll
def parse(content)
measure_time do
@template = Liquid::Template.parse(content, :line_numbers => true)
@template = Jekyll::Cache.new("Jekyll::LiquidRenderer::File").getset(content) do
Liquid::Template.parse(content, :line_numbers => true)
end
end
self
@@ -24,6 +26,7 @@ module Jekyll
end
end
# This method simply 'rethrows any error' before attempting to render the template.
def render!(*args)
measure_time do
measure_bytes do

View File

@@ -100,6 +100,7 @@ module Jekyll
raise ArgumentError, "limit_posts must be a non-negative number" if limit_posts.negative?
Jekyll::Cache.clear_if_config_changed config
Jekyll::Hooks.trigger :site, :after_reset, self
end

View File

@@ -31,22 +31,25 @@ module Jekyll
def render(context)
prefix = context["highlighter_prefix"] || ""
suffix = context["highlighter_suffix"] || ""
code = super.to_s.gsub(%r!\A(\n|\r)+|(\n|\r)+\z!, "")
key = super.to_s
cache.getset(key) do
code = key.gsub(%r!\A(\n|\r)+|(\n|\r)+\z!, "")
is_safe = !!context.registers[:site].safe
is_safe = !!context.registers[:site].safe
output =
case context.registers[:site].highlighter
when "pygments"
render_pygments(code, is_safe)
when "rouge"
render_rouge(code)
else
render_codehighlighter(code)
end
output =
case context.registers[:site].highlighter
when "pygments"
render_pygments(code, is_safe)
when "rouge"
render_rouge(code)
else
render_codehighlighter(code)
end
rendered_output = add_code_tag(output)
prefix + rendered_output + suffix
rendered_output = add_code_tag(output)
prefix + rendered_output + suffix
end
end
def sanitized_opts(opts, is_safe)
@@ -67,6 +70,10 @@ module Jekyll
OPTIONS_REGEX = %r!(?:\w="[^"]*"|\w=\w|\w)+!
def cache
Jekyll::Cache.new("Jekyll::Tags::HighlightBlock")
end
def parse_options(input)
options = {}
return options if input.empty?

View File

@@ -90,13 +90,7 @@ module Jekyll
# Render the variable if required
def render_variable(context)
if @file =~ VARIABLE_SYNTAX
partial = context.registers[:site]
.liquid_renderer
.file("(variable)")
.parse(@file)
partial.render!(context)
end
Liquid::Template.parse(@file).render(context) if @file =~ VARIABLE_SYNTAX
end
def tag_includes_dirs(context)

View File

@@ -17,9 +17,7 @@ module Jekyll
def render(context)
site = context.registers[:site]
liquid = site.liquid_renderer.file("(jekyll:link)")
relative_path = liquid.parse(@relative_path).render(context)
relative_path = Liquid::Template.parse(@relative_path).render(context)
site.each_site_file do |item|
return item.url if item.relative_path == relative_path

View File

@@ -76,6 +76,9 @@ class TestSite < JekyllUnitTest
allow(File).to receive(:directory?).with(theme_dir("_sass")).and_return(true)
allow(File).to receive(:directory?).with(theme_dir("_layouts")).and_return(true)
allow(File).to receive(:directory?).with(theme_dir("_includes")).and_return(false)
allow(File).to receive(:directory?).with(
File.expand_path(".jekyll-cache/Jekyll/Cache/Jekyll::Cache")
).and_return(true)
site = fixture_site("theme" => "test-theme")
assert_equal [source_dir("_includes")], site.includes_load_paths
end