diff --git a/app/models/layout.rb b/app/models/layout.rb index 37974d1d..41ca0f69 100644 --- a/app/models/layout.rb +++ b/app/models/layout.rb @@ -1,6 +1,50 @@ class Layout < LiquidTemplate + ## fields ## + # field :blocks, :type => Hash + + # def unmarshalled_blocks + # # puts "self.blocks = #{self.blocks.class.inspect}" + # @unmarshalled_blocks ||= self.blocks.inject({}) do |b, (name, node)| + # # puts "b= #{b.inspect} / name = #{name.inspect} / node = #{node.inspect}" + # b[name] = Marshal.load(node) + # b + # end + # end + + protected + + + + def after_parse_template + blocks = self.find_blocks(self.template.root) + self.template.send(:instance_variable_set, :"@parent_blocks", blocks) + + # TODO: include parent blocks in self.template before marshalling it + + # puts "[Layout / blocks] #{self.blocks.inspect}" + end + + def find_blocks(node, blocks = {}) + # puts "[Layout/#{self.slug}] ** find_blocks #{node.class.inspect} / #{blocks.keys.inspect}" + if node.respond_to?(:nodelist) && node.nodelist + # puts " ==> find_blocks nodelist = #{node.nodelist.inspect}" + node.nodelist.inject(blocks) do |b, node| + if node.is_a?(Locomotive::Liquid::Tags::Block) + # b[node.name] = Marshal.dump(node) + b[node.name] = node + end + # else + self.find_blocks(node, b) # FIXME: add nested blocks + # end + b + end + end + blocks + end + ## associations ## + # references_many :pages # embeds_many :parts, :class_name => 'PagePart' diff --git a/lib/locomotive/liquid/liquify_template.rb b/lib/locomotive/liquid/liquify_template.rb index f309ed56..66ccec51 100644 --- a/lib/locomotive/liquid/liquify_template.rb +++ b/lib/locomotive/liquid/liquify_template.rb @@ -37,15 +37,25 @@ module Locomotive module InstanceMethods def template - Marshal.load(read_attribute(:serialized_template).to_s) rescue nil + @template ||= Marshal.load(read_attribute(:serialized_template).to_s) rescue nil end protected def store_template begin - template = ::Liquid::Template.parse(self.liquify_template_source) - self.serialized_template = BSON::Binary.new(Marshal.dump(template)) + @template = ::Liquid::Template.parse(self.liquify_template_source) + + if self.respond_to?(:after_parse_template) # kind of callback + self.send(:after_parse_template) + end + + self.serialized_template = BSON::Binary.new(Marshal.dump(@template)) + + if self.respond_to?(:after_store_template) # kind of callback + self.send(:after_store_template) + end + rescue ::Liquid::SyntaxError => error self.errors.add :template, :liquid_syntax_error end diff --git a/lib/locomotive/liquid/tags/extends.rb b/lib/locomotive/liquid/tags/extends.rb index c38121f6..4ce3049d 100644 --- a/lib/locomotive/liquid/tags/extends.rb +++ b/lib/locomotive/liquid/tags/extends.rb @@ -4,6 +4,8 @@ module Locomotive class Extends < ::Liquid::Block Syntax = /(#{::Liquid::QuotedFragment})/ + # attr_accessor :blocks + def initialize(tag_name, markup, tokens) if markup =~ Syntax @template_name = $1 @@ -17,25 +19,35 @@ module Locomotive m[node.name] = node if node.is_a?(Locomotive::Liquid::Tags::Block); m end - # puts "** Extends #{@template_name} / #{@blocks.inspect} / #{@nodelist.inspect}" + # puts "\n** initialize ** Extends #{@template_name} / #{@blocks.inspect}" end def parse(tokens) + # puts "[#{@template_name}] parsing...#{tokens.inspect}" parse_all(tokens) end def render(context) - template = load_template(context) - parent_blocks = find_blocks(template.root) + if OPTIMIZATION + template, parent_blocks = load_template(context) + else + template = load_template(context) + parent_blocks = find_blocks(template.root) + end - # puts "** [Extends/render] parent_blocks = #{parent_blocks.inspect}" + # puts "** [Extends/render] @blocks = #{@blocks.inspect} / @nodelist = #{@nodelist.inspect} / parent_blocks = #{parent_blocks.inspect}" + # BUG: parent blocks and parent template blocks are disconnected (OPTIMIZATION). need to resync them along with @nodelist @blocks.each do |name, block| # puts "** [Extends/render] #{name}, #{block.inspect}" if pb = parent_blocks[name] + # puts "[#{name}]...found parent block ! #{pb.inspect}" pb.parent = block.parent + # puts "[#{name}] pb.parent = #{pb.parent.inspect} / block.parent = #{block.parent.inspect}" pb.add_parent(pb.nodelist) + # puts "[#{name}] pb.nodelist = #{pb.nodelist.inspect}" pb.nodelist = block.nodelist + # puts "[#{name}] block.nodelist = #{block.nodelist.inspect}" else if is_extending?(template) template.root.nodelist << block @@ -83,7 +95,12 @@ module Locomotive def load_template(context) # puts "** load_template (#{context[@template_name]})" layout = context.registers[:site].layouts.where(:slug => context[@template_name]).first - layout.template + if OPTIMIZATION + # [layout.template, layout.unmarshalled_blocks] + [layout.template, layout.template.send(:instance_variable_get, :"@parent_blocks")] + else + layout.template + end end def find_blocks(node, blocks={}) diff --git a/perf/benchmark.rb b/perf/benchmark.rb index 08fb5f9b..2a4301df 100755 --- a/perf/benchmark.rb +++ b/perf/benchmark.rb @@ -9,6 +9,8 @@ require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ # config.master = Mongo::Connection.new.db("locomotive_perf_test") # end +OPTIMIZATION = false + %w{sites pages layouts}.each do |collection| Mongoid.master.collection(collection).drop end @@ -35,13 +37,15 @@ layout_with_sidebar = site.layouts.create :name => 'with_sidebar', :value => %{ } +# puts layout_with_sidebar.unmarshalled_blocks.inspect + custom_layout_with_sidebar = site.layouts.create :name => 'custom_with_sidebar', :value => %{ \{% extends 'with_sidebar' %\} \{% block sidebar %\}A sidebar here\{% endblock %\} \{% block body %\}
\{% block main %\}DEFAULT MAIN CONTENT\{% endblock %\}
\{% endblock %\} } -page = site.pages.create :title => 'Index', :slug => 'index', :layout_template => %{ +page = site.pages.create :title => 'Benchmark', :slug => 'benchmark', :layout_template => %{ \{% extends 'custom_with_sidebar' %\} \{% block sidebar %\}\{\{ block.super \}\} / INDEX sidebar\{% endblock %\} \{% block main %\}Lorem ipsum\{% endblock %\} @@ -49,18 +53,21 @@ page = site.pages.create :title => 'Index', :slug => 'index', :layout_template = context = Liquid::Context.new({}, { 'site' => site }, { :site => site }) -puts "====> \n#{page.render(context)}" +puts "====> OUTPUT \n#{page.render(context)}" Benchmark.bm do |bm| bm.report("Rendering page 10k times") do 10000.times do - Page.first.render(context) + Page.last.render(context) end end end -# without liquify (macbook white): User System Total Real -# Rendering page 10k times 22.650000 6.220000 28.870000 ( 30.294338) +# # empty page (imac 27'): User System Total Real +# # Rendering page 10k times 13.390000 1.700000 15.090000 ( 15.654966) -# without liquify (imac 27'): User System Total Real -# Rendering page 10k times 13.390000 1.700000 15.090000 ( 15.654966) +# # page with inherited template (imac 27'): User System Total Real +# Rendering page 10k times 85.840000 7.600000 93.440000 ( 97.841248) + +# # with optimization (imac 27'): User System Total Real +# Rendering page 10k times 84.240000 7.280000 91.520000 ( 95.475565)