flott 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|