first draft of the new template inheritance mechanism based on our fork of Liquid

This commit is contained in:
dinedine 2010-08-20 02:31:01 +02:00
parent eaf03be8d5
commit 53500bda9e
15 changed files with 109 additions and 196 deletions

19
Gemfile
View File

@ -3,17 +3,14 @@ source 'http://rubygems.org'
gem 'rails', '3.0.0.rc' gem 'rails', '3.0.0.rc'
# gem 'liquid', '2.0.0' gem 'liquid', :git => 'git://github.com/locomotivecms/liquid.git', :ref => 'a41213c77cbc81dab87d'
gem 'liquid', '2.1.2'
# i think we'll need to fork our templating language
# gem 'locomotive-liquid'
gem 'bson_ext', '>= 1.0.1' gem 'bson_ext', '>= 1.0.1'
gem 'mongoid', :git => "git://github.com/durran/mongoid.git", :ref => "e387a0d1dc74da057472" gem 'mongoid', :git => 'git://github.com/durran/mongoid.git', :ref => 'e387a0d1dc74da057472'
gem 'mongoid_acts_as_tree', '0.1.5' gem 'mongoid_acts_as_tree', '0.1.5'
gem 'mongo_session_store', '2.0.0.pre' gem 'mongo_session_store', '2.0.0.pre'
gem 'warden' gem 'warden'
gem 'devise', :git => "git://github.com/plataformatec/devise.git" gem 'devise', :git => 'git://github.com/plataformatec/devise.git'
gem 'haml', '3.0.15' gem 'haml', '3.0.15'
gem 'rmagick', '2.12.2' gem 'rmagick', '2.12.2'
gem 'aws' gem 'aws'
@ -40,16 +37,16 @@ group :test, :development do
end end
group :test do group :test do
gem "autotest" gem 'autotest'
gem "growl-glue" gem 'growl-glue'
gem 'rspec-rails', '2.0.0.beta.19' gem 'rspec-rails', '2.0.0.beta.19'
gem 'factory_girl_rails' gem 'factory_girl_rails'
gem "pickle", :git => "http://github.com/codegram/pickle.git" gem 'pickle', :git => 'http://github.com/codegram/pickle.git'
gem "pickle-mongoid" gem 'pickle-mongoid'
gem 'capybara' gem 'capybara'
# would be nice.. # would be nice..
# gem "capybara-envjs" # gem 'capybara-envjs'
gem 'database_cleaner' gem 'database_cleaner'
gem 'cucumber' gem 'cucumber'

View File

@ -3,6 +3,12 @@ GIT
revision: e387a0d revision: e387a0d
ref: e387a0d1dc74da057472 ref: e387a0d1dc74da057472
specs: specs:
mongoid (2.0.0.beta.15)
activemodel (= 3.0.0.rc)
bson (= 1.0.4)
mongo (= 1.0.6)
tzinfo (= 0.3.22)
will_paginate (~> 3.0.pre)
GIT GIT
remote: git://github.com/floehopper/mocha.git remote: git://github.com/floehopper/mocha.git
@ -11,6 +17,13 @@ GIT
mocha (0.9.8.20090918115329) mocha (0.9.8.20090918115329)
rake rake
GIT
remote: git://github.com/locomotivecms/liquid.git
revision: a41213c
ref: a41213c77cbc81dab87d
specs:
liquid (2.1.3)
GIT GIT
remote: git://github.com/plataformatec/devise.git remote: git://github.com/plataformatec/devise.git
revision: 01c272c revision: 01c272c
@ -133,7 +146,6 @@ GEM
configuration (>= 0.0.5) configuration (>= 0.0.5)
rake (>= 0.8.1) rake (>= 0.8.1)
linecache (0.43) linecache (0.43)
liquid (2.1.2)
mail (2.2.5) mail (2.2.5)
activesupport (>= 2.3.6) activesupport (>= 2.3.6)
mime-types mime-types
@ -144,12 +156,6 @@ GEM
bson (>= 1.0.4) bson (>= 1.0.4)
mongo_session_store (2.0.0.pre) mongo_session_store (2.0.0.pre)
actionpack (~> 3.0) actionpack (~> 3.0)
mongoid (2.0.0.beta.15)
activemodel (= 3.0.0.rc)
bson (= 1.0.4)
mongo (= 1.0.6)
tzinfo (= 0.3.22)
will_paginate (~> 3.0.pre)
mongoid_acts_as_tree (0.1.5) mongoid_acts_as_tree (0.1.5)
bson (>= 0.20.1) bson (>= 0.20.1)
mongoid (<= 2.0.0) mongoid (<= 2.0.0)
@ -252,7 +258,7 @@ DEPENDENCIES
inherited_resources (= 1.1.2) inherited_resources (= 1.1.2)
jeweler jeweler
launchy launchy
liquid (= 2.1.2) liquid!
mimetype-fu mimetype-fu
mocha! mocha!
mongo_session_store (= 2.0.0.pre) mongo_session_store (= 2.0.0.pre)

View File

@ -1,5 +1,8 @@
# @deprecated
class Layout < LiquidTemplate class Layout < LiquidTemplate
# acts_as_tree
protected protected
# TODO: move that in the liquify_template module # TODO: move that in the liquify_template module

View File

@ -67,7 +67,7 @@ class Page
self.slug self.slug
else else
slugs = self.self_and_ancestors.map(&:slug) slugs = self.self_and_ancestors.map(&:slug)
slugs.shift slugs.shift unless slugs.size == 1
File.join slugs File.join slugs
end end
end end

View File

@ -1,6 +1,6 @@
%ul %ul
= admin_submenu_item 'site', edit_admin_current_site_url = admin_submenu_item 'site', edit_admin_current_site_url
= admin_submenu_item 'layouts', admin_layouts_url / = admin_submenu_item 'layouts', admin_layouts_url
= admin_submenu_item 'snippets', admin_snippets_url = admin_submenu_item 'snippets', admin_snippets_url
= admin_submenu_item 'theme_assets', admin_theme_assets_url = admin_submenu_item 'theme_assets', admin_theme_assets_url
= admin_submenu_item 'account', edit_admin_my_account_url = admin_submenu_item 'account', edit_admin_my_account_url

View File

@ -8,7 +8,7 @@ Background:
Given I have the site: "test site" set up Given I have the site: "test site" set up
Scenario: Liquid Inheritance with a single block Scenario: Liquid Inheritance with a single block
Given a layout named "above_and_below" with the source: Given a page named "above-and-below" with the template:
""" """
<div class="header"></div> <div class="header"></div>
<div class="body"> <div class="body">
@ -19,7 +19,7 @@ Scenario: Liquid Inheritance with a single block
And a page named "hello-world-with-layout" with the template: And a page named "hello-world-with-layout" with the template:
""" """
{% extends 'above_and_below' %} {% extends 'above-and-below' %}
{% block body %}Hello World{% endblock %} {% block body %}Hello World{% endblock %}
""" """
@ -34,7 +34,7 @@ Scenario: Liquid Inheritance with a single block
""" """
Scenario: Liquid Inheritance with multiple blocks Scenario: Liquid Inheritance with multiple blocks
Given a layout named "layout_with_sidebar" with the source: Given a page named "layout-with-sidebar" with the template:
""" """
<div class="header"></div> <div class="header"></div>
<div class="content"> <div class="content">
@ -49,7 +49,7 @@ Scenario: Liquid Inheritance with multiple blocks
""" """
And a page named "hello-world-multiblocks" with the template: And a page named "hello-world-multiblocks" with the template:
""" """
{% extends 'layout_with_sidebar' %} {% extends 'layout-with-sidebar' %}
{% block body %}Hello world{% endblock %} {% block body %}Hello world{% endblock %}
""" """
When I view the rendered page at "/hello-world-multiblocks" When I view the rendered page at "/hello-world-multiblocks"
@ -68,7 +68,7 @@ Scenario: Liquid Inheritance with multiple blocks
""" """
Scenario: Multiple inheritance (layout extending another layout) Scenario: Multiple inheritance (layout extending another layout)
Given a layout named "layout_with_sidebar" with the source: Given a page named "layout-with-sidebar" with the template:
""" """
<div class="header"></div> <div class="header"></div>
<div class="content"> <div class="content">
@ -79,15 +79,15 @@ Scenario: Multiple inheritance (layout extending another layout)
</div> </div>
<div class="footer"></div> <div class="footer"></div>
""" """
And a layout named "custom_layout_with_sidebar" with the source: And a page named "custom-layout-with-sidebar" with the template:
""" """
{% extends 'layout_with_sidebar' %} {% extends 'layout-with-sidebar' %}
{% block sidebar %}Custom sidebar{% endblock %} {% block sidebar %}Custom sidebar{% endblock %}
{% block body %}Hello{% endblock %} {% block body %}Hello{% endblock %}
""" """
And a page named "hello-world-multiblocks" with the template: And a page named "hello-world-multiblocks" with the template:
""" """
{% extends 'custom_layout_with_sidebar' %} {% extends 'custom-layout-with-sidebar' %}
{% block body %}{{ block.super }} world{% endblock %} {% block body %}{{ block.super }} world{% endblock %}
""" """
When I view the rendered page at "/hello-world-multiblocks" When I view the rendered page at "/hello-world-multiblocks"
@ -103,9 +103,8 @@ Scenario: Multiple inheritance (layout extending another layout)
<div class="footer"></div> <div class="footer"></div>
""" """
Scenario: Page extending a layout with multiple embedded blocks which extends another template Scenario: Page extending a layout with multiple embedded blocks which extends another template
Given a layout named "layout_with_sidebar" with the source: Given a page named "layout-with-sidebar" with the template:
""" """
<div class="header"></div> <div class="header"></div>
<div class="content"> <div class="content">
@ -116,14 +115,14 @@ Scenario: Page extending a layout with multiple embedded blocks which extends an
</div> </div>
<div class="footer"></div> <div class="footer"></div>
""" """
And a layout named "custom_layout_with_sidebar" with the source: And a page named "custom-layout-with-sidebar" with the template:
""" """
{% extends 'layout_with_sidebar' %} {% extends 'layout-with-sidebar' %}
{% block body %}{{ block.super }} {% block main %}mister{% endblock %}{% endblock %} {% block body %}{{ block.super }} {% block main %}mister{% endblock %}{% endblock %}
""" """
And a page named "hello-world-multiblocks" with the template: And a page named "hello-world-multiblocks" with the template:
""" """
{% extends 'custom_layout_with_sidebar' %} {% extends 'custom-layout-with-sidebar' %}
{% block main %}{{ block.super }} Jacques{% endblock %} {% block main %}{{ block.super }} Jacques{% endblock %}
""" """
When I view the rendered page at "/hello-world-multiblocks" When I view the rendered page at "/hello-world-multiblocks"

View File

@ -1,16 +0,0 @@
module Locomotive
module Liquid
module Drops
class Block < ::Liquid::Drop
def initialize(block)
@block = block
end
def super
@block.call_super(@context)
end
end
end
end
end

View File

@ -44,7 +44,11 @@ module Locomotive
def store_template def store_template
begin begin
@template = ::Liquid::Template.parse(self.liquify_template_source) # puts "self.liquify_template_source = #{self.liquify_template_source.inspect}"
@template = ::Liquid::Template.parse(self.liquify_template_source, { :site => self.site })
@template.root.context.clear
# puts "@template = #{@template.inspect}"
# @template = Locomotive::Liquid::Template.parse(self)
if self.respond_to?(:after_parse_template) # kind of callback if self.respond_to?(:after_parse_template) # kind of callback
self.send(:after_parse_template) self.send(:after_parse_template)

View File

@ -1,50 +0,0 @@
module Locomotive
module Liquid
module Tags
class Block < ::Liquid::Block
Syntax = /(\w+)/
attr_accessor :parent
attr_reader :name
def initialize(tag_name, markup, tokens)
if markup =~ Syntax
@name = $1
else
raise ::Liquid::SyntaxError.new("Syntax Error in 'block' - Valid syntax: block [name]")
end
super if tokens
end
def render(context)
context.stack do
context['block'] = Locomotive::Liquid::Drops::Block.new(self)
render_all(@nodelist, context)
end
end
def add_parent(nodelist)
if parent
parent.add_parent(nodelist)
else
self.parent = self.class.new(@tag_name, @name, nil)
parent.nodelist = nodelist
end
end
def call_super(context)
if parent
parent.render(context)
else
''
end
end
end
::Liquid::Template.register_tag('block', Block)
end
end
end

View File

@ -15,7 +15,7 @@ module Locomotive
Syntax = /(#{::Liquid::VariableSignature}+)\s*from\s*(#{::Liquid::QuotedString}+)/ Syntax = /(#{::Liquid::VariableSignature}+)\s*from\s*(#{::Liquid::QuotedString}+)/
def initialize(tag_name, markup, tokens) def initialize(tag_name, markup, tokens, context)
if markup =~ Syntax if markup =~ Syntax
@target = $1 @target = $1
@url = $2.gsub(/['"]/, '') @url = $2.gsub(/['"]/, '')

View File

@ -1,83 +1,26 @@
module Locomotive module Locomotive
module Liquid module Liquid
module Tags module Tags
class Extends < ::Liquid::Block class Extends < ::Liquid::Extends
Syntax = /(#{::Liquid::QuotedFragment})/
def initialize(tag_name, markup, tokens) class PageNotFound < ::Liquid::Error; end
if markup =~ Syntax
@template_name = $1 def parse_parent_template(context)
else page = context[:site].pages.where(:fullpath => @template_name.gsub("'", '')).first
raise ::Liquid::SyntaxError.new("Syntax Error in 'extends' - Valid syntax: extends [template]")
raise PageNotFound.new("Page with fullpath '#{@template_name}' was not found") if page.nil?
template = page.template
# merge blocks ?
blocks = find_blocks(template.root.nodelist)
blocks.each_value do |block|
block.send(:instance_variable_set, :"@context", context)
block.end_tag
end end
super template
@blocks = @nodelist.inject({}) do |m, node|
m[node.name] = node if node.is_a?(Locomotive::Liquid::Tags::Block); m
end
end
def parse(tokens)
parse_all(tokens)
end
def render(context)
template, parent_blocks = load_template(context)
@blocks.each do |name, block|
if pb = parent_blocks[name]
pb.parent = block.parent
pb.add_parent(pb.nodelist)
pb.nodelist = block.nodelist
else
if is_extending?(template)
template.root.nodelist << block
end
end
end
template.render(context)
end
private
def parse_all(tokens)
@nodelist ||= []
@nodelist.clear
while token = tokens.shift
case token
when /^#{::Liquid::TagStart}/
if token =~ /^#{::Liquid::TagStart}\s*(\w+)\s*(.*)?#{::Liquid::TagEnd}$/
# fetch the tag from registered blocks
if tag = ::Liquid::Template.tags[$1]
@nodelist << tag.new($1, $2, tokens)
else
# this tag is not registered with the system
# pass it to the current block for special handling or error reporting
unknown_tag($1, $2, tokens)
end
else
raise ::Liquid::SyntaxError, "Tag '#{token}' was not properly terminated with regexp: #{TagEnd.inspect} "
end
when /^#{::Liquid::VariableStart}/
@nodelist << create_variable(token)
when ''
# pass
else
@nodelist << token
end
end
end
def load_template(context)
layout = context.registers[:site].layouts.where(:slug => context[@template_name]).first
[layout.template, layout.template.send(:instance_variable_get, :"@parent_blocks")]
end
def is_extending?(template)
template.root.nodelist.any? { |node| node.is_a?(Extends) }
end end
end end

View File

@ -12,7 +12,7 @@ module Locomotive
Syntax = /(#{::Liquid::Expression}+)?/ Syntax = /(#{::Liquid::Expression}+)?/
def initialize(tag_name, markup, tokens) def initialize(tag_name, markup, tokens, context)
if markup =~ Syntax if markup =~ Syntax
@site_or_page = $1 || 'page' @site_or_page = $1 || 'page'
@options = {} @options = {}

View File

@ -19,7 +19,7 @@ module Locomotive
Syntax = /(#{::Liquid::Expression}+)\s+by\s+([0-9]+)/ Syntax = /(#{::Liquid::Expression}+)\s+by\s+([0-9]+)/
def initialize(tag_name, markup, tokens) def initialize(tag_name, markup, tokens, context)
if markup =~ Syntax if markup =~ Syntax
@collection_name = $1 @collection_name = $1
@per_page = $2.to_i @per_page = $2.to_i

View File

@ -3,7 +3,7 @@ module Locomotive
module Tags module Tags
class WithScope < ::Liquid::Block class WithScope < ::Liquid::Block
def initialize(tag_name, markup, tokens) def initialize(tag_name, markup, tokens, context)
@attributes = {} @attributes = {}
markup.scan(::Liquid::TagAttributes) do |key, value| markup.scan(::Liquid::TagAttributes) do |key, value|
@attributes[key] = value @attributes[key] = value

View File

@ -7,21 +7,21 @@ require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_
Mongoid.master.collection(collection).drop Mongoid.master.collection(collection).drop
end end
puts "Starting benchmark..." puts "Starting test..."
site = Site.create :name => 'Benchmark Website', :subdomain => 'benchmark' site = Site.create :name => 'Benchmark Website', :subdomain => 'benchmark'
layout_with_sidebar = site.layouts.create :name => 'with_sidebar', :value => %{ simple = site.pages.create :title => 'Simple', :slug => 'simple', :layout_template => %{
<html> <html>
<head></head> <head></head>
<body> <body>
<div class="header"></div> <div class="header"></div>
<div class="content"> <div class="content">
<div class="sidebar"> <div class="sidebar">
{% block sidebar %}DEFAULT SIDEBAR CONTENT{% endblock %} A sidebar here / INDEX sidebar
</div> </div>
<div class="body"> <div class="body">
{% block body %}DEFAULT BODY CONTENT{% endblock %} <div class="wrapper">Lorem ipsum</div>
</div> </div>
</div> </div>
<div class="footer"></div> <div class="footer"></div>
@ -29,24 +29,48 @@ layout_with_sidebar = site.layouts.create :name => 'with_sidebar', :value => %{
</html> </html>
} }
custom_layout_with_sidebar = site.layouts.create :name => 'custom_with_sidebar', :value => %{ base = site.pages.create :title => 'Base page', :slug => 'base', :layout_template => %{
\{% extends 'with_sidebar' %\} <html>
\{% block sidebar %\}A sidebar here\{% endblock %\} <head></head>
\{% block body %\}<div class="wrapper">\{% block main %\}DEFAULT MAIN CONTENT\{% endblock %\}</div>\{% endblock %\} <body>
<div class="header"></div>
<div class="content">
<div class="sidebar">
{% block sidebar %}My simple sidebar{% endblock %}
</div>
<div class="body">
{% block body %}Just to say hi{% endblock %}
</div>
</div>
<div class="footer"></div>
</body>
</html>
} }
page = site.pages.create :title => 'Benchmark', :slug => 'benchmark', :layout_template => %{ page_1 = site.pages.create :title => 'Page 1', :slug => 'page_1', :layout_template => %{
\{% extends 'custom_with_sidebar' %\} {% extends base %}
\{% block sidebar %\}\{\{ block.super \}\} / INDEX sidebar\{% endblock %\} {% block sidebar %}A sidebar here{% endblock %}
\{% block main %\}Lorem ipsum\{% endblock %\} {% block body %}<div class="wrapper">{% block main %}DEFAULT MAIN CONTENT{% endblock %}</div>{% endblock %}
} }
page_2 = site.pages.create :title => 'Page 2', :slug => 'page_2', :layout_template => %{
{% extends page_1 %}
{% block sidebar %}{{ block.super }} / INDEX sidebar{% endblock %}
{% block main %}Lorem ipsum{% endblock %}
}
puts "OUTPUT = #{page_2.render(Liquid::Context.new)}"
context = Liquid::Context.new({}, { 'site' => site }, { :site => site }) context = Liquid::Context.new({}, { 'site' => site }, { :site => site })
puts "====> OUTPUT \n#{page.render(context)}"
Benchmark.bm do |bm| Benchmark.bm do |bm|
bm.report("Rendering page 10k times") do bm.report("Rendering a simple page 10k times") do
10000.times do
Page.where(:title => 'Simple').first.render(context)
end
end
bm.report("Rendering a complex page 10k times") do
10000.times do 10000.times do
Page.last.render(context) Page.last.render(context)
end end
@ -54,10 +78,13 @@ Benchmark.bm do |bm|
end end
# # empty page (imac 27'): User System Total Real # # empty page (imac 27'): User System Total Real
# # Rendering page 10k times 13.390000 1.700000 15.090000 ( 15.654966) # Rendering page 10k times 21.390000 1.820000 23.210000 ( 24.120529)
# # page with inherited template (imac 27'): User System Total Real # # page with inherited template (imac 27'): User System Total Real
# Rendering page 10k times 85.840000 7.600000 93.440000 ( 97.841248) # Rendering page 10k times 85.840000 7.600000 93.440000 ( 97.841248)
# # with optimization (imac 27'): User System Total Real # # with optimization (imac 27'): User System Total Real
# Rendering page 10k times 84.240000 7.280000 91.520000 ( 95.475565) # Rendering page 10k times 84.240000 7.280000 91.520000 ( 95.475565)
# # with locomotive liquid (imac 27'): User System Total Real
# Rendering page 10k times 38.750000 3.050000 41.800000 ( 42.880022)