flott 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/.travis.yml +16 -0
- data/Gemfile +9 -0
- data/README.rdoc +76 -0
- data/Rakefile +25 -0
- data/VERSION +1 -0
- data/benchmarks/data/.keep +0 -0
- data/benchmarks/flott_benchmark.rb +165 -0
- data/benchmarks/runner.rb +6 -0
- data/doc-main.txt +76 -0
- data/flott.gemspec +41 -0
- data/install.rb +16 -0
- data/lib/flott.rb +1016 -0
- data/lib/flott/cache.rb +99 -0
- data/lib/flott/version.rb +8 -0
- data/make_doc.rb +5 -0
- data/tests/templates/header +8 -0
- data/tests/templates/subdir/deeptemplate +6 -0
- data/tests/templates/subdir/included +9 -0
- data/tests/templates/subdir/subdir2/deepincluded2 +2 -0
- data/tests/templates/subdir/subdir2/included2 +1 -0
- data/tests/templates/subdir/subdir3/included3 +1 -0
- data/tests/templates/template +12 -0
- data/tests/templates/template2 +6 -0
- data/tests/templates/toplevel +2 -0
- data/tests/templates/toplevel2 +1 -0
- data/tests/test_cache.rb +70 -0
- data/tests/test_flott.rb +200 -0
- data/tests/test_flott_file.rb +155 -0
- data/tests/test_helper.rb +3 -0
- metadata +153 -0
data/lib/flott/cache.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
module Flott
|
2
|
+
class Cache
|
3
|
+
# A Page object that encapsulates a compiled template.
|
4
|
+
class Page
|
5
|
+
# Creates a Page object for the template that can be found at
|
6
|
+
# path _path_.
|
7
|
+
def initialize(cache, path)
|
8
|
+
@cache = cache
|
9
|
+
@path = path
|
10
|
+
compile
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns the compiled template or nil if the template
|
14
|
+
# hasn't been compiled.
|
15
|
+
attr_reader :template
|
16
|
+
|
17
|
+
# If the file at _path_ has changed this method
|
18
|
+
# returns true. It always returns true, if the attribute
|
19
|
+
# _reload_time_ is set to a value < 0. If
|
20
|
+
# _reload_time_ is set to a value >= 0, it will
|
21
|
+
# return true only after _reload_time_ seconds (before that
|
22
|
+
# time has passed nil is returned), even if the file has changed.
|
23
|
+
def changed?
|
24
|
+
return true if @cache.reload_time and @cache.reload_time < 0
|
25
|
+
if @cache.reload_time and Time.now - @template.mtime < @cache.reload_time
|
26
|
+
return
|
27
|
+
end
|
28
|
+
@template.mtime != @mtime
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns the path to this page, with the prepended rootdir of the Cache
|
32
|
+
# instance of this page.
|
33
|
+
def rootpath
|
34
|
+
File.join(@cache.rootdir, @path)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Compile the file at _path_ with a Flott instance and return it. This
|
38
|
+
# implies that the _template_ attribute is set to the return value for
|
39
|
+
# later reuse without compiling.
|
40
|
+
def compile
|
41
|
+
@template = Flott.compile(File.read(rootpath), File.dirname(rootpath), @cache.rootdir, rootpath)
|
42
|
+
@template.page_cache = @cache
|
43
|
+
@mtime = @template.mtime
|
44
|
+
@template
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Creates a Cache that compiles and caches the template files under
|
49
|
+
# _rootdir_. If _reload_time_ in seconds is given,
|
50
|
+
# the template files are only reloaded after _reload_time_
|
51
|
+
# seconds even if changed. Negative _reload_time_ means, reload
|
52
|
+
# the file always, even if unchanged. (This is good for development, think
|
53
|
+
# of changed included templates.)
|
54
|
+
def initialize(rootdir, reload_time = nil)
|
55
|
+
@rootdir = rootdir
|
56
|
+
@reload_time = reload_time
|
57
|
+
@pages = {}
|
58
|
+
end
|
59
|
+
|
60
|
+
# Reload time in seconds for the template files of this Cache.
|
61
|
+
attr_accessor :reload_time
|
62
|
+
|
63
|
+
attr_reader :rootdir
|
64
|
+
|
65
|
+
# Return the page that was compiled and/or cached with the name
|
66
|
+
# _name_. If block is given the page is yielded to instead.
|
67
|
+
def get(name)
|
68
|
+
page = @pages[name]
|
69
|
+
if page
|
70
|
+
if page.changed?
|
71
|
+
page.compile
|
72
|
+
end
|
73
|
+
else
|
74
|
+
page = Page.new(self, name)
|
75
|
+
page.compile
|
76
|
+
put(name, page)
|
77
|
+
end
|
78
|
+
if block_given?
|
79
|
+
yield page.template
|
80
|
+
self
|
81
|
+
else
|
82
|
+
return page.template
|
83
|
+
end
|
84
|
+
rescue Errno::ENOENT, Errno::EISDIR
|
85
|
+
end
|
86
|
+
|
87
|
+
# Return the cached page _name_, evaluated in the Environment _env_.
|
88
|
+
def evaluate(name, env = Environment.new)
|
89
|
+
get(name) { |template| template.evaluate(env) } or return
|
90
|
+
self
|
91
|
+
end
|
92
|
+
|
93
|
+
# Puts _page_ into the cache using the key _name_.
|
94
|
+
def put(name, page)
|
95
|
+
@pages[name] = page
|
96
|
+
end
|
97
|
+
private :put
|
98
|
+
end
|
99
|
+
end
|
data/make_doc.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
Workdir 1 (included): [=workdir]
|
2
|
+
[^/toplevel]
|
3
|
+
Workdir 2 (included): [=workdir]
|
4
|
+
[^subdir2/included2]
|
5
|
+
Workdir 3 (included): [=workdir]
|
6
|
+
[include "subdir3/included3"]
|
7
|
+
Workdir 4 (included): [=workdir]
|
8
|
+
[include "/toplevel"]
|
9
|
+
Workdir 5 (included): [=workdir]
|
@@ -0,0 +1 @@
|
|
1
|
+
Workdir (included2): [=workdir]
|
@@ -0,0 +1 @@
|
|
1
|
+
Workdir (included3): [=workdir]
|
@@ -0,0 +1 @@
|
|
1
|
+
Toplevel2
|
data/tests/test_cache.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'flott'
|
3
|
+
require 'stringio'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
class TC_Cache < Test::Unit::TestCase
|
7
|
+
include FileUtils
|
8
|
+
include Flott
|
9
|
+
|
10
|
+
TEMPLATES_DIR = File.join(File.dirname(__FILE__), 'templates')
|
11
|
+
|
12
|
+
def setup
|
13
|
+
@cache = Cache.new(TEMPLATES_DIR, 2)
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_kind
|
17
|
+
assert_kind_of Cache, @cache
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_get
|
21
|
+
template = @cache.get('template')
|
22
|
+
assert_kind_of Template, template
|
23
|
+
assert_equal template, @cache.get('template')
|
24
|
+
assert_equal template, @cache.get('template')
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_reload
|
28
|
+
template = @cache.get('template')
|
29
|
+
assert_equal template, @cache.get('template')
|
30
|
+
assert_equal template, @cache.get('template')
|
31
|
+
sleep 1.0
|
32
|
+
touch File.join(TEMPLATES_DIR, 'template')
|
33
|
+
assert_equal template, @cache.get('template')
|
34
|
+
sleep 3.0
|
35
|
+
assert template != @cache.get('template')
|
36
|
+
template = @cache.get('template')
|
37
|
+
touch File.join(TEMPLATES_DIR, 'template')
|
38
|
+
assert_equal template, @cache.get('template')
|
39
|
+
template2 = @cache.get('template2')
|
40
|
+
assert_kind_of Template, template2
|
41
|
+
assert_equal template2, @cache.get('template2')
|
42
|
+
@cache.reload_time = -1
|
43
|
+
template = @cache.get('template')
|
44
|
+
assert template != @cache.get('template')
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_get_deep
|
48
|
+
template = @cache.get('subdir/deeptemplate')
|
49
|
+
assert_kind_of Template, template
|
50
|
+
assert_equal template, @cache.get('subdir/deeptemplate')
|
51
|
+
assert_equal template, @cache.get('subdir/deeptemplate')
|
52
|
+
output = ''
|
53
|
+
template.evaluate(Environment.new(output))
|
54
|
+
assert_equal <<__EOT, output
|
55
|
+
<html>
|
56
|
+
<body>
|
57
|
+
Toplevel2
|
58
|
+
|
59
|
+
(deepincluded2)
|
60
|
+
|
61
|
+
Toplevel2
|
62
|
+
|
63
|
+
(deepincluded2)
|
64
|
+
|
65
|
+
Toplevel2
|
66
|
+
</body>
|
67
|
+
</html>
|
68
|
+
__EOT
|
69
|
+
end
|
70
|
+
end
|
data/tests/test_flott.rb
ADDED
@@ -0,0 +1,200 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'flott'
|
3
|
+
require 'stringio'
|
4
|
+
|
5
|
+
class TC_Flott < Test::Unit::TestCase
|
6
|
+
include Flott
|
7
|
+
|
8
|
+
def assert_template_equal(expected, template, hash = {})
|
9
|
+
output = ''
|
10
|
+
env = Environment.new(output)
|
11
|
+
env.update hash
|
12
|
+
parser = Parser.new(template)
|
13
|
+
parser.evaluate(env)
|
14
|
+
assert_equal expected, output
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_error
|
18
|
+
assert_raises(CallError) do
|
19
|
+
Parser.new('<bla>[= [^/bla>').evaluate
|
20
|
+
end
|
21
|
+
assert_raises(CallError) do
|
22
|
+
Parser.new('<bla>[^/bla>').evaluate
|
23
|
+
end
|
24
|
+
assert_raises(CompileError) do
|
25
|
+
Parser.new('<bla>[^does_not_exist]</bla>').evaluate
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_for_errors
|
30
|
+
assert_template_equal 'puts "\n"', 'puts "\n"'
|
31
|
+
if RUBY_VERSION =~ /\A1.[0-8]/
|
32
|
+
assert_template_equal "123\n", '[print [1, 2, "3"], "\n" ]'
|
33
|
+
else
|
34
|
+
assert_template_equal "[1, 2, "3"]\n", '[print [1, 2, "3"], "\n" ]'
|
35
|
+
end
|
36
|
+
assert_template_equal "a\b", '[print "a\b" ]' # 1
|
37
|
+
assert_template_equal "a\b", '[print "a\\b" ]' # 2
|
38
|
+
assert_template_equal "a\\b", '[print "a\\\b" ]' # 3
|
39
|
+
assert_template_equal "a\\b", '[print "a\\\\b" ]' # 4
|
40
|
+
assert_template_equal "a\\\b", '[print "a\\\\\b" ]' # 5
|
41
|
+
assert_template_equal "a\\\b", '[print "a\\\\\\b" ]' # 6
|
42
|
+
assert_template_equal "a\\\\b", '[print "a\\\\\\\\b" ]' # 7
|
43
|
+
assert_template_equal "a\\\\\b", '[print "a\\\\\\\\\b" ]' # 8
|
44
|
+
assert_template_equal "a\\\\\b", '[print "a\\\\\\\\\\b" ]' # 9
|
45
|
+
assert_template_equal "puts [1, 2, '3']", "puts \\[1, 2, '3']"
|
46
|
+
assert_template_equal "puts {1, 2}", "puts {1, 2}"
|
47
|
+
assert_template_equal "puts {1, 2}", "puts {1, [=1+1]}"
|
48
|
+
assert_template_equal "puts {'1', 2}", "puts {'1', [=1+1]}"
|
49
|
+
assert_template_equal "lambda { |x| '", "lambda { |x| '"
|
50
|
+
assert_template_equal "}", "}"
|
51
|
+
assert_template_equal "{}}", "{}}"
|
52
|
+
assert_template_equal "", "[lambda {}]"
|
53
|
+
assert_template_equal "foo", "[=lambda {:foo}.call]"
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_fun
|
57
|
+
assert_template_equal("\nAAA3628800BBB\n", <<__EOT)
|
58
|
+
[fun :fac do |n|
|
59
|
+
if n < 2
|
60
|
+
1
|
61
|
+
else
|
62
|
+
n * fac(n - 1)
|
63
|
+
end
|
64
|
+
end]
|
65
|
+
AAA[=fac(10)]BBB
|
66
|
+
__EOT
|
67
|
+
assert_template_equal("false\n\ntrue\n", <<__EOT)
|
68
|
+
[=respond_to? :foobar]
|
69
|
+
[=fun :foobar do end]
|
70
|
+
[=respond_to? :foobar]
|
71
|
+
__EOT
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_environment_instance_variables
|
75
|
+
env = Environment.new
|
76
|
+
env.output = output = ''
|
77
|
+
assert_equal ["@__escape__", "@__output__"].sort,
|
78
|
+
env.instance_variables.map { |x| x.to_s }.sort
|
79
|
+
env[:foo] = :foo
|
80
|
+
assert_equal ["@__escape__", "@__output__", "@foo"].sort,
|
81
|
+
env.instance_variables.map { |x| x.to_s }.sort
|
82
|
+
assert_equal env[:foo], :foo
|
83
|
+
assert_equal env[:@foo], :foo
|
84
|
+
env[:@bar] = :bar
|
85
|
+
assert_equal ["@__escape__", "@__output__", "@bar", "@foo"].sort,
|
86
|
+
env.instance_variables.map { |x| x.to_s }.sort
|
87
|
+
assert_equal env[:bar], :bar
|
88
|
+
assert_equal env[:@bar], :bar
|
89
|
+
env.update({ :baz => :baz })
|
90
|
+
assert_equal ["@__escape__", "@__output__", "@bar", "@baz", "@foo"].sort,
|
91
|
+
env.instance_variables.map { |x| x.to_s }.sort
|
92
|
+
assert_equal env[:baz], :baz
|
93
|
+
assert_equal env[:@baz], :baz
|
94
|
+
assert_equal env[:__output__], output
|
95
|
+
assert_equal env.output, output
|
96
|
+
end
|
97
|
+
|
98
|
+
class MyEnvironment < Array
|
99
|
+
include Flott::EnvironmentMixin
|
100
|
+
|
101
|
+
# or explicitly:
|
102
|
+
#def initialize(*)
|
103
|
+
# super
|
104
|
+
#end
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_my_environment
|
108
|
+
env = MyEnvironment.new
|
109
|
+
assert_kind_of Array, env
|
110
|
+
assert_kind_of Flott::EnvironmentMixin, env
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_p
|
114
|
+
assert_template_equal %Q'AAA[1, :foo, "bar"]\nBBB', 'AAA[p [1, :foo, "bar"]]BBB'
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_p_bang
|
118
|
+
assert_template_equal %Q'AAA[1, :foo, "bar"]\nBBB', 'AAA[p! [1, :foo, "bar"]]BBB'
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_pp
|
122
|
+
assert_template_equal %Q'AAA[1, :foo, "bar"]\nBBB', 'AAA[pp [1, :foo, "bar"]]BBB'
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_pp_bang
|
126
|
+
assert_template_equal %Q'AAA[1, :foo, "bar"]\nBBB', 'AAA[pp! [1, :foo, "bar"]]BBB'
|
127
|
+
end
|
128
|
+
|
129
|
+
def test_puts_bang
|
130
|
+
assert_template_equal %Q'AAA<BBB>\nCCC', 'AAA[puts! "<BBB>"]CCC'
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_puts
|
134
|
+
assert_template_equal %Q'AAA<BBB>\nCCC', 'AAA[puts "<BBB>"]CCC'
|
135
|
+
end
|
136
|
+
|
137
|
+
def test_putc_bang
|
138
|
+
assert_template_equal %Q'AAA<CCC', 'AAA[putc! "<BBB>"]CCC'
|
139
|
+
assert_template_equal %Q'AAA<CCC', 'AAA[putc! ?<]CCC'
|
140
|
+
end
|
141
|
+
|
142
|
+
def test_putc
|
143
|
+
assert_template_equal %Q'AAA<CCC', 'AAA[putc "<BBB>"]CCC'
|
144
|
+
assert_template_equal %Q'AAA<CCC', 'AAA[putc ?<]CCC'
|
145
|
+
end
|
146
|
+
|
147
|
+
def test_printf_bang
|
148
|
+
assert_template_equal %Q'AAA<B42BB>CCC', 'AAA[printf! "<B%xBB>", 66]CCC'
|
149
|
+
end
|
150
|
+
|
151
|
+
def test_printf
|
152
|
+
assert_template_equal %Q'AAA<B42BB>CCC', 'AAA[printf "<B%xBB>", 66]CCC'
|
153
|
+
end
|
154
|
+
|
155
|
+
def test_print_bang
|
156
|
+
assert_template_equal %Q'AAA<BBB>CCC', 'AAA[print! "<BBB>"]CCC'
|
157
|
+
end
|
158
|
+
|
159
|
+
def test_print
|
160
|
+
assert_template_equal %Q'AAA<BBB>CCC', 'AAA[print "<BBB>"]CCC'
|
161
|
+
end
|
162
|
+
|
163
|
+
def test_write_bang
|
164
|
+
assert_template_equal %Q'AAA<BBB>CCC', 'AAA[write! "<BBB>"]CCC'
|
165
|
+
end
|
166
|
+
|
167
|
+
def test_write
|
168
|
+
assert_template_equal %Q'AAA<BBB>CCC', 'AAA[write "<BBB>"]CCC'
|
169
|
+
end
|
170
|
+
|
171
|
+
def test_minus
|
172
|
+
assert_template_equal "a\nb\nc", "a\n[print 'b']\nc"
|
173
|
+
assert_template_equal "a \t b \t c", "a \t [print 'b'] \t c"
|
174
|
+
assert_template_equal "a\nb\nc", "a\n \t [-print 'b']\nc"
|
175
|
+
assert_template_equal "a\nb \t c", "a\n \t [-print 'b'] \t c"
|
176
|
+
assert_template_equal "a\n \t bc", "a\n \t [print 'b'-] \t \nc"
|
177
|
+
assert_template_equal "abc", "a \t [-print 'b'-]\nc"
|
178
|
+
assert_template_equal "a\n<bc", "a\n \t [-='<b'-] \t \nc"
|
179
|
+
assert_template_equal "a\n<bc", "a\n \t [-!'<b'-] \t \nc"
|
180
|
+
assert_template_equal "a\nc", "a\n \t [-#'<b'-] \t \nc"
|
181
|
+
assert_template_equal <<DST, <<SRC
|
182
|
+
<ul>
|
183
|
+
<li>1</li>
|
184
|
+
<li>2</li>
|
185
|
+
<li>3</li>
|
186
|
+
</ul>
|
187
|
+
DST
|
188
|
+
<ul>
|
189
|
+
[-3.times do |i|-]
|
190
|
+
<li>[=i + 1]</li>
|
191
|
+
[-end-]
|
192
|
+
</ul>
|
193
|
+
SRC
|
194
|
+
end
|
195
|
+
|
196
|
+
def test_function
|
197
|
+
assert_template_equal "AAABB", "[=function :multiple do |x, n| x * n end;multiple 'A', 3][=multiple 'B', 2]"
|
198
|
+
assert_template_equal "AAABB", "[=function :multiple, :memoize => 3 do |x, n| x * n end;multiple 'A', 3][=multiple 'B', 2]"
|
199
|
+
end
|
200
|
+
end
|