liquid 1.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +38 -0
- data/MIT-LICENSE +20 -0
- data/Manifest.txt +60 -0
- data/README +38 -0
- data/Rakefile +24 -0
- data/example/server/example_servlet.rb +37 -0
- data/example/server/liquid_servlet.rb +28 -0
- data/example/server/server.rb +12 -0
- data/example/server/templates/index.liquid +6 -0
- data/example/server/templates/products.liquid +45 -0
- data/init.rb +6 -0
- data/lib/extras/liquid_view.rb +27 -0
- data/lib/liquid.rb +66 -0
- data/lib/liquid/block.rb +101 -0
- data/lib/liquid/condition.rb +91 -0
- data/lib/liquid/context.rb +216 -0
- data/lib/liquid/document.rb +17 -0
- data/lib/liquid/drop.rb +48 -0
- data/lib/liquid/errors.rb +7 -0
- data/lib/liquid/extensions.rb +56 -0
- data/lib/liquid/file_system.rb +62 -0
- data/lib/liquid/htmltags.rb +64 -0
- data/lib/liquid/standardfilters.rb +125 -0
- data/lib/liquid/strainer.rb +43 -0
- data/lib/liquid/tag.rb +25 -0
- data/lib/liquid/tags/assign.rb +22 -0
- data/lib/liquid/tags/capture.rb +22 -0
- data/lib/liquid/tags/case.rb +68 -0
- data/lib/liquid/tags/comment.rb +9 -0
- data/lib/liquid/tags/cycle.rb +46 -0
- data/lib/liquid/tags/for.rb +81 -0
- data/lib/liquid/tags/if.rb +51 -0
- data/lib/liquid/tags/ifchanged.rb +20 -0
- data/lib/liquid/tags/include.rb +56 -0
- data/lib/liquid/tags/unless.rb +29 -0
- data/lib/liquid/template.rb +150 -0
- data/lib/liquid/variable.rb +39 -0
- data/test/block_test.rb +50 -0
- data/test/context_test.rb +340 -0
- data/test/drop_test.rb +139 -0
- data/test/error_handling_test.rb +65 -0
- data/test/extra/breakpoint.rb +547 -0
- data/test/extra/caller.rb +80 -0
- data/test/file_system_test.rb +30 -0
- data/test/filter_test.rb +98 -0
- data/test/helper.rb +20 -0
- data/test/html_tag_test.rb +24 -0
- data/test/if_else_test.rb +95 -0
- data/test/include_tag_test.rb +91 -0
- data/test/output_test.rb +121 -0
- data/test/parsing_quirks_test.rb +14 -0
- data/test/regexp_test.rb +39 -0
- data/test/security_test.rb +41 -0
- data/test/standard_filter_test.rb +101 -0
- data/test/standard_tag_test.rb +336 -0
- data/test/statements_test.rb +137 -0
- data/test/strainer_test.rb +16 -0
- data/test/template_test.rb +26 -0
- data/test/unless_else_test.rb +19 -0
- data/test/variable_test.rb +135 -0
- metadata +114 -0
data/CHANGELOG
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
Changelog
|
2
|
+
|
3
|
+
Implemented .to_liquid for all objects which can be passed to liquid like Strings Arrays Hashes Numerics and Booleans.
|
4
|
+
To export new objects to liquid just implement .to_liquid on them and return objects which themselves have .to_liquid methods.
|
5
|
+
|
6
|
+
Added more tags to standard library
|
7
|
+
|
8
|
+
Added include tag ( like partials in rails )
|
9
|
+
|
10
|
+
[...] Gazillion of detail improvements
|
11
|
+
|
12
|
+
Added strainers as filter hosts for better security [Tobias Luetke]
|
13
|
+
|
14
|
+
Fixed that rails integration would call filter with the wrong "self" [Michael Geary]
|
15
|
+
|
16
|
+
Fixed bad error reporting when a filter called a method which doesn't exist. Liquid told you that it couldn't find the
|
17
|
+
filter which was obviously misleading [Tobias Luetke]
|
18
|
+
|
19
|
+
Removed count helper from standard lib. use size [Tobias Luetke]
|
20
|
+
|
21
|
+
Fixed bug with string filter parameters failing to tolerate commas in strings. [Paul Hammond]
|
22
|
+
|
23
|
+
Improved filter parameters. Filter parameters are now context sensitive; Types are resolved according to the rules of the context. Multiple parameters are now separated by the Liquid::ArgumentSeparator: , by default [Paul Hammond]
|
24
|
+
{{ 'Typo' | link_to: 'http://typo.leetsoft.com', 'Typo - a modern weblog engine' }}
|
25
|
+
|
26
|
+
|
27
|
+
Added Liquid::Drop. A base class which you can use for exporting proxy objects to liquid which can acquire more data when used in liquid. [Tobias Luetke]
|
28
|
+
|
29
|
+
class ProductDrop < Liquid::Drop
|
30
|
+
def top_sales
|
31
|
+
Shop.current.products.find(:all, :order => 'sales', :limit => 10 )
|
32
|
+
end
|
33
|
+
end
|
34
|
+
t = Liquid::Template.parse( ' {% for product in product.top_sales %} {{ product.name }} {% endfor %} ' )
|
35
|
+
t.render('product' => ProductDrop.new )
|
36
|
+
|
37
|
+
|
38
|
+
Added filter parameters support. Example: {{ date | format_date: "%Y" }} [Paul Hammond]
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2005, 2006 Tobias Luetke
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOa AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SaALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Manifest.txt
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
CHANGELOG
|
2
|
+
MIT-LICENSE
|
3
|
+
Manifest.txt
|
4
|
+
README
|
5
|
+
Rakefile
|
6
|
+
example/server/example_servlet.rb
|
7
|
+
example/server/liquid_servlet.rb
|
8
|
+
example/server/server.rb
|
9
|
+
example/server/templates/index.liquid
|
10
|
+
example/server/templates/products.liquid
|
11
|
+
init.rb
|
12
|
+
lib/extras/liquid_view.rb
|
13
|
+
lib/liquid.rb
|
14
|
+
lib/liquid/block.rb
|
15
|
+
lib/liquid/condition.rb
|
16
|
+
lib/liquid/context.rb
|
17
|
+
lib/liquid/document.rb
|
18
|
+
lib/liquid/drop.rb
|
19
|
+
lib/liquid/errors.rb
|
20
|
+
lib/liquid/extensions.rb
|
21
|
+
lib/liquid/file_system.rb
|
22
|
+
lib/liquid/htmltags.rb
|
23
|
+
lib/liquid/standardfilters.rb
|
24
|
+
lib/liquid/strainer.rb
|
25
|
+
lib/liquid/tag.rb
|
26
|
+
lib/liquid/tags/assign.rb
|
27
|
+
lib/liquid/tags/capture.rb
|
28
|
+
lib/liquid/tags/case.rb
|
29
|
+
lib/liquid/tags/comment.rb
|
30
|
+
lib/liquid/tags/cycle.rb
|
31
|
+
lib/liquid/tags/for.rb
|
32
|
+
lib/liquid/tags/if.rb
|
33
|
+
lib/liquid/tags/ifchanged.rb
|
34
|
+
lib/liquid/tags/include.rb
|
35
|
+
lib/liquid/tags/unless.rb
|
36
|
+
lib/liquid/template.rb
|
37
|
+
lib/liquid/variable.rb
|
38
|
+
test/block_test.rb
|
39
|
+
test/context_test.rb
|
40
|
+
test/drop_test.rb
|
41
|
+
test/error_handling_test.rb
|
42
|
+
test/extra/breakpoint.rb
|
43
|
+
test/extra/caller.rb
|
44
|
+
test/file_system_test.rb
|
45
|
+
test/filter_test.rb
|
46
|
+
test/helper.rb
|
47
|
+
test/html_tag_test.rb
|
48
|
+
test/if_else_test.rb
|
49
|
+
test/include_tag_test.rb
|
50
|
+
test/output_test.rb
|
51
|
+
test/parsing_quirks_test.rb
|
52
|
+
test/regexp_test.rb
|
53
|
+
test/security_test.rb
|
54
|
+
test/standard_filter_test.rb
|
55
|
+
test/standard_tag_test.rb
|
56
|
+
test/statements_test.rb
|
57
|
+
test/strainer_test.rb
|
58
|
+
test/template_test.rb
|
59
|
+
test/unless_else_test.rb
|
60
|
+
test/variable_test.rb
|
data/README
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
= Liquid template engine
|
2
|
+
|
3
|
+
Liquid is a template engine which I wrote for very specific requirements
|
4
|
+
|
5
|
+
* It has to have beautiful and simple markup.
|
6
|
+
Template engines which don't produce good looking markup are no fun to use.
|
7
|
+
* It needs to be non evaling and secure. Liquid templates are made so that users can edit them. You don't want to run code on your server which your users wrote.
|
8
|
+
* It has to be stateless. Compile and render steps have to be seperate so that the expensive parsing and compiling can be done once and later on you can
|
9
|
+
just render it passing in a hash with local variables and objects.
|
10
|
+
|
11
|
+
== Why should i use Liquid
|
12
|
+
|
13
|
+
* You want to allow your users to edit the appearance of your application but don't want them to run insecure code on your server.
|
14
|
+
* You want to render templates directly from the database
|
15
|
+
* You like smarty style template engines
|
16
|
+
* You need a template engine which does HTML just as well as Emails
|
17
|
+
* You don't like the markup of your current one
|
18
|
+
|
19
|
+
== What does it look like?
|
20
|
+
|
21
|
+
<ul id="products">
|
22
|
+
{% for product in products %}
|
23
|
+
<li>
|
24
|
+
<h2>{{product.name}}</h2>
|
25
|
+
Only {{product.price | price }}
|
26
|
+
|
27
|
+
{{product.description | prettyprint | paragraph }}
|
28
|
+
</li>
|
29
|
+
{% endfor %}
|
30
|
+
</ul>
|
31
|
+
|
32
|
+
== Howto use Liquid
|
33
|
+
|
34
|
+
Liquid supports a very simple API based around the Liquid::Template class.
|
35
|
+
For standard use you can just pass it the content of a file and call render with a parameters hash.
|
36
|
+
|
37
|
+
@template = Liquid::Template.parse("hi {{name}}") # Parses and compiles the template
|
38
|
+
@template.render( 'name' => 'tobi' ) # => "hi tobi"
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rake'
|
4
|
+
require 'hoe'
|
5
|
+
|
6
|
+
PKG_VERSION = "1.7.0"
|
7
|
+
PKG_NAME = "liquid"
|
8
|
+
PKG_DESC = "A secure non evaling end user template engine with aesthetic markup."
|
9
|
+
|
10
|
+
Rake::TestTask.new(:test) do |t|
|
11
|
+
t.libs << "lib"
|
12
|
+
t.libs << "test"
|
13
|
+
t.pattern = 'test/*_test.rb'
|
14
|
+
t.verbose = false
|
15
|
+
end
|
16
|
+
|
17
|
+
Hoe.new(PKG_NAME, PKG_VERSION) do |p|
|
18
|
+
p.rubyforge_name = PKG_NAME
|
19
|
+
p.summary = PKG_DESC
|
20
|
+
p.description = nil
|
21
|
+
p.author = "Tobias Luetke"
|
22
|
+
p.email = "tobi@leetsoft.com"
|
23
|
+
p.url = "http://home.leetsoft.com/liquid"
|
24
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module ProductsFilter
|
2
|
+
def price(integer)
|
3
|
+
sprintf("$%.2d USD", integer / 100.0)
|
4
|
+
end
|
5
|
+
|
6
|
+
def prettyprint(text)
|
7
|
+
text.gsub( /\*(.*)\*/, '<b>\1</b>' )
|
8
|
+
end
|
9
|
+
|
10
|
+
def count(array)
|
11
|
+
array.size
|
12
|
+
end
|
13
|
+
|
14
|
+
def paragraph(p)
|
15
|
+
"<p>#{p}</p>"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Servlet < LiquidServlet
|
20
|
+
|
21
|
+
def index
|
22
|
+
{ 'date' => Time.now }
|
23
|
+
end
|
24
|
+
|
25
|
+
def products
|
26
|
+
{ 'products' => products_list, 'section' => 'Snowboards', 'cool_products' => true}
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def products_list
|
32
|
+
[{'name' => 'Arbor Draft', 'price' => 39900, 'description' => 'the *arbor draft* is a excellent product' },
|
33
|
+
{'name' => 'Arbor Element', 'price' => 40000, 'description' => 'the *arbor element* rocks for freestyling'},
|
34
|
+
{'name' => 'Arbor Diamond', 'price' => 59900, 'description' => 'the *arbor diamond* is a made up product because im obsessed with arbor and have no creativity'}]
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class LiquidServlet < WEBrick::HTTPServlet::AbstractServlet
|
2
|
+
|
3
|
+
def do_GET(req, res)
|
4
|
+
handle(:get, req, res)
|
5
|
+
end
|
6
|
+
|
7
|
+
def do_POST(req, res)
|
8
|
+
handle(:post, req, res)
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def handle(type, req, res)
|
14
|
+
@request, @response = req, res
|
15
|
+
|
16
|
+
@request.path_info =~ /(\w+)$/
|
17
|
+
@action = $1 || 'index'
|
18
|
+
@assigns = send(@action) if respond_to?(@action)
|
19
|
+
|
20
|
+
@response['Content-Type'] = "text/html"
|
21
|
+
@response.status = 200
|
22
|
+
@response.body = Liquid::Template.parse(read_template).render(@assigns, :filters => [ProductsFilter])
|
23
|
+
end
|
24
|
+
|
25
|
+
def read_template(filename = @action)
|
26
|
+
File.read( File.dirname(__FILE__) + "/templates/#{filename}.liquid" )
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'webrick'
|
2
|
+
require 'rexml/document'
|
3
|
+
|
4
|
+
require File.dirname(__FILE__) + '/../../lib/liquid'
|
5
|
+
require File.dirname(__FILE__) + '/liquid_servlet'
|
6
|
+
require File.dirname(__FILE__) + '/example_servlet'
|
7
|
+
|
8
|
+
# Setup webrick
|
9
|
+
server = WEBrick::HTTPServer.new( :Port => ARGV[1] || 3000 )
|
10
|
+
server.mount('/', Servlet)
|
11
|
+
trap("INT"){ server.shutdown }
|
12
|
+
server.start
|
@@ -0,0 +1,45 @@
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
2
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
3
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
4
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
5
|
+
<head>
|
6
|
+
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
7
|
+
<meta http-equiv="Content-Language" content="en-us" />
|
8
|
+
|
9
|
+
<title>products</title>
|
10
|
+
|
11
|
+
<meta name="ROBOTS" content="ALL" />
|
12
|
+
<meta http-equiv="imagetoolbar" content="no" />
|
13
|
+
<meta name="MSSmartTagsPreventParsing" content="true" />
|
14
|
+
<meta name="Copyright" content="(c) 2005 Copyright content: Copyright design: Tobias Luetke" />
|
15
|
+
<!-- (c) Copyright 2005 by Tobias Luetke All Rights Reserved. -->
|
16
|
+
</head>
|
17
|
+
|
18
|
+
<body>
|
19
|
+
|
20
|
+
<h1>There are currently {{products | count}} products in the {{section}} catalog</h1>
|
21
|
+
|
22
|
+
{% if cool_products %}
|
23
|
+
Cool products :)
|
24
|
+
{% else %}
|
25
|
+
Uncool products :(
|
26
|
+
{% endif %}
|
27
|
+
|
28
|
+
<ul id="products">
|
29
|
+
|
30
|
+
{% for product in products %}
|
31
|
+
<li>
|
32
|
+
<h2>{{product.name}}</h2>
|
33
|
+
Only {{product.price | price }}
|
34
|
+
|
35
|
+
{{product.description | prettyprint | paragraph }}
|
36
|
+
|
37
|
+
{{ 'it rocks!' | paragraph }}
|
38
|
+
|
39
|
+
</li>
|
40
|
+
{% endfor %}
|
41
|
+
|
42
|
+
</ul>
|
43
|
+
|
44
|
+
</body>
|
45
|
+
</html>
|
data/init.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# LiquidView is a action view extension class. You can register it with rails
|
2
|
+
# and use liquid as an template system for .liquid files
|
3
|
+
#
|
4
|
+
# Example
|
5
|
+
#
|
6
|
+
# ActionView::Base::register_template_handler :liquid, LiquidView
|
7
|
+
class LiquidView
|
8
|
+
|
9
|
+
def initialize(action_view)
|
10
|
+
@action_view = action_view
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
def render(template, local_assigns)
|
15
|
+
@action_view.controller.headers["Content-Type"] ||= 'text/html; charset=utf-8'
|
16
|
+
assigns = @action_view.assigns.dup
|
17
|
+
|
18
|
+
if content_for_layout = @action_view.instance_variable_get("@content_for_layout")
|
19
|
+
assigns['content_for_layout'] = content_for_layout
|
20
|
+
end
|
21
|
+
assigns.merge!(local_assigns)
|
22
|
+
|
23
|
+
liquid = Liquid::Template.parse(template)
|
24
|
+
liquid.render(assigns, :filters => [@action_view.controller.master_helper_module], :registers => {:action_view => @action_view, :controller => @action_view.controller})
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
data/lib/liquid.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# Copyright (c) 2005 Tobias Luetke
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
# a copy of this software and associated documentation files (the
|
5
|
+
# "Software"), to deal in the Software without restriction, including
|
6
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
# the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOa AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SaALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
23
|
+
|
24
|
+
module Liquid
|
25
|
+
FilterSperator = /\|/
|
26
|
+
ArgumentSeparator = ','
|
27
|
+
FilterArgumentSeparator = ':'
|
28
|
+
VariableAttributeSeparator = '.'
|
29
|
+
TagStart = /\{\%/
|
30
|
+
TagEnd = /\%\}/
|
31
|
+
VariableSignature = /[\w\-\.\[\]]/
|
32
|
+
VariableSegment = /[\w\-]/
|
33
|
+
VariableStart = /\{\{/
|
34
|
+
VariableEnd = /\}\}/
|
35
|
+
QuotedFragment = /"[^"]+"|'[^']+'|[^\s,|]+/
|
36
|
+
TagAttributes = /(\w+)\s*\:\s*(#{QuotedFragment})/
|
37
|
+
TemplateParser = /(#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableEnd})/
|
38
|
+
VariableParser = /(?=\[)#{VariableSegment}+(?=\])|#{VariableSegment}+/
|
39
|
+
end
|
40
|
+
|
41
|
+
require 'liquid/drop'
|
42
|
+
require 'liquid/extensions'
|
43
|
+
require 'liquid/errors'
|
44
|
+
require 'liquid/strainer'
|
45
|
+
require 'liquid/context'
|
46
|
+
require 'liquid/tag'
|
47
|
+
require 'liquid/block'
|
48
|
+
require 'liquid/document'
|
49
|
+
require 'liquid/variable'
|
50
|
+
require 'liquid/file_system'
|
51
|
+
require 'liquid/template'
|
52
|
+
require 'liquid/htmltags'
|
53
|
+
require 'liquid/standardfilters'
|
54
|
+
require 'liquid/condition'
|
55
|
+
|
56
|
+
# Load all the tags of the standard library
|
57
|
+
#
|
58
|
+
Dir[File.dirname(__FILE__) + '/liquid/tags/*.rb'].each { |f| require f }
|
59
|
+
|
60
|
+
|
61
|
+
|
62
|
+
|
63
|
+
|
64
|
+
|
65
|
+
|
66
|
+
|
data/lib/liquid/block.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
module Liquid
|
2
|
+
|
3
|
+
class Block < Tag
|
4
|
+
def parse(tokens)
|
5
|
+
@nodelist ||= []
|
6
|
+
@nodelist.clear
|
7
|
+
|
8
|
+
while token = tokens.shift
|
9
|
+
|
10
|
+
case token
|
11
|
+
when /^#{TagStart}/
|
12
|
+
if token =~ /^#{TagStart}\s*(\w+)\s*(.*)?#{TagEnd}$/
|
13
|
+
|
14
|
+
# if we found the proper block delimitor just end parsing here and let the outer block
|
15
|
+
# proceed
|
16
|
+
if block_delimiter == $1
|
17
|
+
end_tag
|
18
|
+
return
|
19
|
+
end
|
20
|
+
|
21
|
+
# fetch the tag from registered blocks
|
22
|
+
if tag = Template.tags[$1]
|
23
|
+
@nodelist << tag.new($2, tokens)
|
24
|
+
else
|
25
|
+
# this tag is not registered with the system
|
26
|
+
# pass it to the current block for special handling or error reporting
|
27
|
+
unknown_tag($1, $2, tokens)
|
28
|
+
end
|
29
|
+
else
|
30
|
+
raise SyntaxError, "Tag '#{token}' was not properly terminated with regexp: #{TagEnd.inspect} "
|
31
|
+
end
|
32
|
+
when /^#{VariableStart}/
|
33
|
+
@nodelist << create_variable(token)
|
34
|
+
when ''
|
35
|
+
# pass
|
36
|
+
else
|
37
|
+
@nodelist << token
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Make sure that its ok to end parsing in the current block.
|
42
|
+
# Effectively this method will throw and exception unless the current block is
|
43
|
+
# of type Document
|
44
|
+
assert_missing_delimitation!
|
45
|
+
end
|
46
|
+
|
47
|
+
def end_tag
|
48
|
+
end
|
49
|
+
|
50
|
+
def unknown_tag(tag, params, tokens)
|
51
|
+
case tag
|
52
|
+
when 'else'
|
53
|
+
raise SyntaxError, "#{block_name} tag does not expect else tag"
|
54
|
+
when 'end'
|
55
|
+
raise SyntaxError, "'end' is not a valid delimiter for #{block_name} tags. use #{block_delimiter}"
|
56
|
+
else
|
57
|
+
raise SyntaxError, "Unknown tag '#{tag}'"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def block_delimiter
|
62
|
+
"end#{block_name}"
|
63
|
+
end
|
64
|
+
|
65
|
+
def block_name
|
66
|
+
self.class.name.scan(/\w+$/).first.downcase
|
67
|
+
end
|
68
|
+
|
69
|
+
def create_variable(token)
|
70
|
+
token.scan(/^#{VariableStart}(.*)#{VariableEnd}$/) do |content|
|
71
|
+
return Variable.new(content.first)
|
72
|
+
end
|
73
|
+
raise SyntaxError.new("Variable '#{token}' was not properly terminated with regexp: #{VariableEnd.inspect} ")
|
74
|
+
end
|
75
|
+
|
76
|
+
def render(context)
|
77
|
+
render_all(@nodelist, context)
|
78
|
+
end
|
79
|
+
|
80
|
+
protected
|
81
|
+
|
82
|
+
def assert_missing_delimitation!
|
83
|
+
raise SyntaxError.new("#{block_name} tag was never closed")
|
84
|
+
end
|
85
|
+
|
86
|
+
def render_all(list, context)
|
87
|
+
list.collect do |token|
|
88
|
+
begin
|
89
|
+
if token.respond_to?(:render)
|
90
|
+
token.render(context)
|
91
|
+
else
|
92
|
+
token.to_s
|
93
|
+
end
|
94
|
+
rescue Exception => e
|
95
|
+
context.template.handle_error(e)
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|