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.
@@ -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