Liquid profiler
This commit is contained in:
parent
a134e05fda
commit
1e9163fdf4
|
@ -24,6 +24,7 @@ Mercenary.program(:jekyll) do |p|
|
||||||
p.option 'safe', '--safe', 'Safe mode (defaults to false)'
|
p.option 'safe', '--safe', 'Safe mode (defaults to false)'
|
||||||
p.option 'plugins', '-p', '--plugins PLUGINS_DIR1[,PLUGINS_DIR2[,...]]', Array, 'Plugins directory (defaults to ./_plugins)'
|
p.option 'plugins', '-p', '--plugins PLUGINS_DIR1[,PLUGINS_DIR2[,...]]', Array, 'Plugins directory (defaults to ./_plugins)'
|
||||||
p.option 'layouts', '--layouts DIR', String, 'Layouts directory (defaults to ./_layouts)'
|
p.option 'layouts', '--layouts DIR', String, 'Layouts directory (defaults to ./_layouts)'
|
||||||
|
p.option 'profile', '--profile', 'Generate a Liquid rendering profile'
|
||||||
|
|
||||||
Jekyll::Command.subclasses.each { |c| c.init_with_program(p) }
|
Jekyll::Command.subclasses.each { |c| c.init_with_program(p) }
|
||||||
|
|
||||||
|
|
|
@ -66,6 +66,7 @@ module Jekyll
|
||||||
autoload :Regenerator, 'jekyll/regenerator'
|
autoload :Regenerator, 'jekyll/regenerator'
|
||||||
autoload :RelatedPosts, 'jekyll/related_posts'
|
autoload :RelatedPosts, 'jekyll/related_posts'
|
||||||
autoload :Renderer, 'jekyll/renderer'
|
autoload :Renderer, 'jekyll/renderer'
|
||||||
|
autoload :LiquidRenderer, 'jekyll/liquid_renderer'
|
||||||
autoload :Site, 'jekyll/site'
|
autoload :Site, 'jekyll/site'
|
||||||
autoload :StaticFile, 'jekyll/static_file'
|
autoload :StaticFile, 'jekyll/static_file'
|
||||||
autoload :Stevenson, 'jekyll/stevenson'
|
autoload :Stevenson, 'jekyll/stevenson'
|
||||||
|
|
|
@ -108,8 +108,8 @@ module Jekyll
|
||||||
# info - the info for Liquid
|
# info - the info for Liquid
|
||||||
#
|
#
|
||||||
# Returns the converted content
|
# Returns the converted content
|
||||||
def render_liquid(content, payload, info, path = nil)
|
def render_liquid(content, payload, info, path)
|
||||||
Liquid::Template.parse(content).render!(payload, info)
|
site.liquid_renderer.file(path).parse(content).render(payload, info)
|
||||||
rescue Tags::IncludeTagError => e
|
rescue Tags::IncludeTagError => e
|
||||||
Jekyll.logger.error "Liquid Exception:", "#{e.message} in #{e.path}, included in #{path || self.path}"
|
Jekyll.logger.error "Liquid Exception:", "#{e.message} in #{e.path}, included in #{path || self.path}"
|
||||||
raise e
|
raise e
|
||||||
|
@ -243,7 +243,7 @@ module Jekyll
|
||||||
payload["highlighter_prefix"] = converters.first.highlighter_prefix
|
payload["highlighter_prefix"] = converters.first.highlighter_prefix
|
||||||
payload["highlighter_suffix"] = converters.first.highlighter_suffix
|
payload["highlighter_suffix"] = converters.first.highlighter_suffix
|
||||||
|
|
||||||
self.content = render_liquid(content, payload, info) if render_with_liquid?
|
self.content = render_liquid(content, payload, info, path) if render_with_liquid?
|
||||||
self.content = transform
|
self.content = transform
|
||||||
|
|
||||||
# output keeps track of what will finally be written
|
# output keeps track of what will finally be written
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
require 'jekyll/liquid_renderer/file'
|
||||||
|
require 'jekyll/liquid_renderer/table'
|
||||||
|
|
||||||
|
module Jekyll
|
||||||
|
class LiquidRenderer
|
||||||
|
def initialize(site)
|
||||||
|
@site = site
|
||||||
|
reset
|
||||||
|
end
|
||||||
|
|
||||||
|
def reset
|
||||||
|
@stats = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def file(filename)
|
||||||
|
filename = @site.in_source_dir(filename).sub(/\A#{Regexp.escape(@site.source)}\//, '')
|
||||||
|
|
||||||
|
LiquidRenderer::File.new(self, filename).tap do |file|
|
||||||
|
@stats[filename] ||= {}
|
||||||
|
@stats[filename][:count] ||= 0
|
||||||
|
@stats[filename][:count] += 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def increment_time(filename, time)
|
||||||
|
@stats[filename][:time] ||= 0.0
|
||||||
|
@stats[filename][:time] += time
|
||||||
|
end
|
||||||
|
|
||||||
|
def stats_table(n = 50)
|
||||||
|
LiquidRenderer::Table.new(@stats).to_s(n)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,40 @@
|
||||||
|
module Jekyll
|
||||||
|
class LiquidRenderer
|
||||||
|
class File
|
||||||
|
def initialize(renderer, filename)
|
||||||
|
@renderer = renderer
|
||||||
|
@filename = filename
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse(content)
|
||||||
|
measure_time do
|
||||||
|
@template = Liquid::Template.parse(content)
|
||||||
|
end
|
||||||
|
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def render(*args)
|
||||||
|
measure_time do
|
||||||
|
@template.render(*args)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def render!(*args)
|
||||||
|
measure_time do
|
||||||
|
@template.render!(*args)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def measure_time
|
||||||
|
before = Time.now
|
||||||
|
yield
|
||||||
|
ensure
|
||||||
|
after = Time.now
|
||||||
|
@renderer.increment_time(@filename, after - before)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,88 @@
|
||||||
|
module Jekyll
|
||||||
|
class LiquidRenderer::Table
|
||||||
|
def initialize(stats)
|
||||||
|
@stats = stats
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s(n = 50)
|
||||||
|
data = data_for_table(n)
|
||||||
|
widths = table_widths(data)
|
||||||
|
generate_table(data, widths)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def generate_table(data, widths)
|
||||||
|
str = "\n"
|
||||||
|
|
||||||
|
table_head = data.shift
|
||||||
|
str << generate_row(table_head, widths)
|
||||||
|
str << generate_table_head_border(table_head, widths)
|
||||||
|
|
||||||
|
data.each do |row_data|
|
||||||
|
str << generate_row(row_data, widths)
|
||||||
|
end
|
||||||
|
|
||||||
|
str << "\n"
|
||||||
|
str
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_table_head_border(row_data, widths)
|
||||||
|
str = ""
|
||||||
|
|
||||||
|
row_data.each_index do |cell_index|
|
||||||
|
str << '-' * widths[cell_index]
|
||||||
|
str << '-+-' unless cell_index == row_data.length-1
|
||||||
|
end
|
||||||
|
|
||||||
|
str << "\n"
|
||||||
|
str
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_row(row_data, widths)
|
||||||
|
str = ''
|
||||||
|
|
||||||
|
row_data.each_with_index do |cell_data, cell_index|
|
||||||
|
if cell_index == 0
|
||||||
|
str << cell_data.ljust(widths[cell_index], ' ')
|
||||||
|
else
|
||||||
|
str << cell_data.rjust(widths[cell_index], ' ')
|
||||||
|
end
|
||||||
|
|
||||||
|
str << ' | ' unless cell_index == row_data.length-1
|
||||||
|
end
|
||||||
|
|
||||||
|
str << "\n"
|
||||||
|
str
|
||||||
|
end
|
||||||
|
|
||||||
|
def table_widths(data)
|
||||||
|
widths = [ 0, 0, 0 ]
|
||||||
|
|
||||||
|
data.each do |row|
|
||||||
|
row.each_with_index do |cell, index|
|
||||||
|
widths[index] = [ cell.length, widths[index] ].max
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
widths
|
||||||
|
end
|
||||||
|
|
||||||
|
def data_for_table(n)
|
||||||
|
sorted = @stats.sort_by{ |filename, file_stats| -file_stats[:time] }
|
||||||
|
sorted = sorted.slice(0, n)
|
||||||
|
|
||||||
|
table = [[ 'Filename', 'Count', 'Total time' ]]
|
||||||
|
|
||||||
|
sorted.each do |filename, file_stats|
|
||||||
|
row = []
|
||||||
|
row << filename
|
||||||
|
row << file_stats[:count].to_s
|
||||||
|
row << "%.3f" % file_stats[:time]
|
||||||
|
table << row
|
||||||
|
end
|
||||||
|
|
||||||
|
table
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -49,7 +49,7 @@ module Jekyll
|
||||||
output = document.content
|
output = document.content
|
||||||
|
|
||||||
if document.render_with_liquid?
|
if document.render_with_liquid?
|
||||||
output = render_liquid(output, payload, info)
|
output = render_liquid(output, payload, info, document.path)
|
||||||
end
|
end
|
||||||
|
|
||||||
output = convert(output)
|
output = convert(output)
|
||||||
|
@ -92,7 +92,7 @@ module Jekyll
|
||||||
#
|
#
|
||||||
# Returns the content, rendered by Liquid.
|
# Returns the content, rendered by Liquid.
|
||||||
def render_liquid(content, payload, info, path = nil)
|
def render_liquid(content, payload, info, path = nil)
|
||||||
Liquid::Template.parse(content).render!(payload, info)
|
site.liquid_renderer.file(path).parse(content).render!(payload, info)
|
||||||
rescue Tags::IncludeTagError => e
|
rescue Tags::IncludeTagError => e
|
||||||
Jekyll.logger.error "Liquid Exception:", "#{e.message} in #{e.path}, included in #{path || document.relative_path}"
|
Jekyll.logger.error "Liquid Exception:", "#{e.message} in #{e.path}, included in #{path || document.relative_path}"
|
||||||
raise e
|
raise e
|
||||||
|
|
|
@ -11,7 +11,7 @@ module Jekyll
|
||||||
:gems, :plugin_manager
|
:gems, :plugin_manager
|
||||||
|
|
||||||
attr_accessor :converters, :generators, :reader
|
attr_accessor :converters, :generators, :reader
|
||||||
attr_reader :regenerator
|
attr_reader :regenerator, :liquid_renderer
|
||||||
|
|
||||||
# Public: Initialize a new Site.
|
# Public: Initialize a new Site.
|
||||||
#
|
#
|
||||||
|
@ -33,6 +33,8 @@ module Jekyll
|
||||||
# Initialize incremental regenerator
|
# Initialize incremental regenerator
|
||||||
@regenerator = Regenerator.new(self)
|
@regenerator = Regenerator.new(self)
|
||||||
|
|
||||||
|
@liquid_renderer = LiquidRenderer.new(self)
|
||||||
|
|
||||||
self.plugin_manager = Jekyll::PluginManager.new(self)
|
self.plugin_manager = Jekyll::PluginManager.new(self)
|
||||||
self.plugins = plugin_manager.plugins_path
|
self.plugins = plugin_manager.plugins_path
|
||||||
|
|
||||||
|
@ -57,6 +59,13 @@ module Jekyll
|
||||||
render
|
render
|
||||||
cleanup
|
cleanup
|
||||||
write
|
write
|
||||||
|
print_stats
|
||||||
|
end
|
||||||
|
|
||||||
|
def print_stats
|
||||||
|
if @config['profile']
|
||||||
|
puts @liquid_renderer.stats_table
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Reset Site details.
|
# Reset Site details.
|
||||||
|
@ -70,7 +79,8 @@ module Jekyll
|
||||||
self.static_files = []
|
self.static_files = []
|
||||||
self.data = {}
|
self.data = {}
|
||||||
@collections = nil
|
@collections = nil
|
||||||
@regenerator.clear_cache()
|
@regenerator.clear_cache
|
||||||
|
@liquid_renderer.reset
|
||||||
|
|
||||||
if limit_posts < 0
|
if limit_posts < 0
|
||||||
raise ArgumentError, "limit_posts must be a non-negative number"
|
raise ArgumentError, "limit_posts must be a non-negative number"
|
||||||
|
@ -319,7 +329,6 @@ module Jekyll
|
||||||
end.to_a
|
end.to_a
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def each_site_file
|
def each_site_file
|
||||||
%w(posts pages static_files docs_to_write).each do |type|
|
%w(posts pages static_files docs_to_write).each do |type|
|
||||||
send(type).each do |item|
|
send(type).each do |item|
|
||||||
|
|
|
@ -95,7 +95,7 @@ eos
|
||||||
# Render the variable if required
|
# Render the variable if required
|
||||||
def render_variable(context)
|
def render_variable(context)
|
||||||
if @file.match(VARIABLE_SYNTAX)
|
if @file.match(VARIABLE_SYNTAX)
|
||||||
partial = Liquid::Template.parse(@file)
|
partial = context.registers[:site].liquid_renderer.file("(variable)").parse(@file)
|
||||||
partial.render!(context)
|
partial.render!(context)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -123,7 +123,7 @@ eos
|
||||||
end
|
end
|
||||||
|
|
||||||
begin
|
begin
|
||||||
partial = Liquid::Template.parse(read_file(path, context))
|
partial = site.liquid_renderer.file(path).parse(read_file(path, context))
|
||||||
|
|
||||||
context.stack do
|
context.stack do
|
||||||
context['include'] = parse_params(context) if @params
|
context['include'] = parse_params(context) if @params
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
require 'helper'
|
||||||
|
|
||||||
|
class TestLiquidRenderer < JekyllUnitTest
|
||||||
|
context "profiler" do
|
||||||
|
setup do
|
||||||
|
@site = Site.new(site_configuration)
|
||||||
|
@renderer = @site.liquid_renderer
|
||||||
|
end
|
||||||
|
|
||||||
|
should "return a table with profiling results" do
|
||||||
|
@site.process
|
||||||
|
|
||||||
|
output = @renderer.stats_table
|
||||||
|
|
||||||
|
expected = [
|
||||||
|
/^Filename\s+|\s+Count\s+|\s+Total time$/,
|
||||||
|
/^-+\++-+\++-+$/,
|
||||||
|
/^_posts\/2010-01-09-date-override\.markdown\s+|\s+\d+\s+|\s+\d+\.\d{3}$/,
|
||||||
|
]
|
||||||
|
|
||||||
|
expected.each do |regexp|
|
||||||
|
assert_match regexp, output
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -310,7 +310,7 @@ class TestSite < JekyllUnitTest
|
||||||
|
|
||||||
custom_processor = "CustomMarkdown"
|
custom_processor = "CustomMarkdown"
|
||||||
s = Site.new(site_configuration('markdown' => custom_processor))
|
s = Site.new(site_configuration('markdown' => custom_processor))
|
||||||
assert !!s.process
|
s.process
|
||||||
|
|
||||||
# Do some cleanup, we don't like straggling stuff's.
|
# Do some cleanup, we don't like straggling stuff's.
|
||||||
Jekyll::Converters::Markdown.send(:remove_const, :CustomMarkdown)
|
Jekyll::Converters::Markdown.send(:remove_const, :CustomMarkdown)
|
||||||
|
@ -459,6 +459,17 @@ class TestSite < JekyllUnitTest
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "with liquid profiling" do
|
||||||
|
setup do
|
||||||
|
@site = Site.new(site_configuration('profile' => true))
|
||||||
|
end
|
||||||
|
|
||||||
|
should "print profile table" do
|
||||||
|
@site.liquid_renderer.should_receive(:stats_table)
|
||||||
|
@site.process
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context "incremental build" do
|
context "incremental build" do
|
||||||
setup do
|
setup do
|
||||||
@site = Site.new(site_configuration({
|
@site = Site.new(site_configuration({
|
||||||
|
|
Loading…
Reference in New Issue