flott 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,8 @@
1
+ module Flott
2
+ # Flott version
3
+ VERSION = '1.0.0'
4
+ VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc:
5
+ VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
6
+ VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
7
+ VERSION_BUILD = VERSION_ARRAY[2] # :nodoc:
8
+ end
data/make_doc.rb ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $outdir = 'doc/'
4
+ puts "Creating documentation in '#$outdir'."
5
+ system "rdoc -m doc-main.txt -o #$outdir doc-main.txt lib/flott.rb lib/flott/*.rb"
@@ -0,0 +1,8 @@
1
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2
+ "http://www.w3.org/TR/html4/strict.dtd">
3
+ <html>
4
+ <head>
5
+ <title>Hello [=@name]!</title>
6
+ <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-15">
7
+ </head>
8
+ <body>
@@ -0,0 +1,6 @@
1
+ <html>
2
+ <body>
3
+ [^subdir2/deepincluded2]
4
+ [include 'subdir2/deepincluded2']
5
+ [include '/toplevel2']</body>
6
+ </html>
@@ -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,2 @@
1
+ [^/toplevel2]
2
+ (deepincluded2)
@@ -0,0 +1 @@
1
+ Workdir (included2): [=workdir]
@@ -0,0 +1 @@
1
+ Workdir (included3): [=workdir]
@@ -0,0 +1,12 @@
1
+ [^header]
2
+ <h1>Hello [=@name]\[!</h1>
3
+ [for i in 1..6
4
+ if i % 2 == 0]
5
+ <b>Hello [=@name + "\]"]!</b>
6
+ [else]
7
+ <i>Hello [!@name + "\]"]!</i>
8
+ [end
9
+ end]
10
+ [a = [1, "\[", 3]]
11
+ </body>[#Commented out] ]
12
+ </html>
@@ -0,0 +1,6 @@
1
+ [^header-]
2
+ Workdir before (template2): [=workdir]
3
+ [^subdir/included]
4
+ Workdir after (template2): [=workdir]
5
+ </body>
6
+ </html>
@@ -0,0 +1,2 @@
1
+ Toplevel
2
+ Workdir (toplevel): [=workdir]
@@ -0,0 +1 @@
1
+ Toplevel2
@@ -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
@@ -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, &quot;3&quot;]\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, &quot;bar&quot;]\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, &quot;bar&quot;]\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&lt;BBB&gt;\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&lt;CCC', 'AAA[putc "<BBB>"]CCC'
144
+ assert_template_equal %Q'AAA&lt;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&lt;B42BB&gt;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&lt;BBB&gt;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&lt;BBB&gt;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&lt;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