tilt 1.0.1 → 1.2.2
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/README.md +11 -33
- data/lib/tilt.rb +245 -76
- data/test/markaby/locals.mab +1 -0
- data/test/markaby/markaby.mab +1 -0
- data/test/markaby/markaby_other_static.mab +1 -0
- data/test/markaby/render_twice.mab +1 -0
- data/test/markaby/scope.mab +1 -0
- data/test/markaby/yielding.mab +2 -0
- data/test/tilt_blueclothtemplate_test.rb +64 -0
- data/test/tilt_buildertemplate_test.rb +16 -1
- data/test/tilt_cache_test.rb +2 -3
- data/test/tilt_coffeescripttemplate_test.rb +30 -0
- data/test/tilt_compilesite_test.rb +2 -37
- data/test/tilt_erbtemplate_test.rb +30 -7
- data/test/tilt_erubistemplate_test.rb +5 -0
- data/test/tilt_hamltemplate_test.rb +7 -7
- data/test/tilt_lesstemplate_test.rb +5 -0
- data/test/tilt_liquidtemplate_test.rb +5 -0
- data/test/tilt_markaby_test.rb +88 -0
- data/test/tilt_nokogiritemplate_test.rb +76 -0
- data/test/tilt_radiustemplate_test.rb +9 -0
- data/test/tilt_rdiscounttemplate_test.rb +5 -0
- data/test/tilt_rdoctemplate_test.rb +6 -1
- data/test/tilt_redclothtemplate_test.rb +4 -0
- data/test/tilt_sasstemplate_test.rb +21 -0
- data/test/tilt_stringtemplate_test.rb +7 -7
- data/test/tilt_template_test.rb +0 -11
- data/tilt.gemspec +15 -2
- metadata +57 -15
data/README.md
CHANGED
|
@@ -34,6 +34,15 @@ Support for these template engines is included with the package:
|
|
|
34
34
|
RedCloth .textile redcloth
|
|
35
35
|
RDoc .rdoc rdoc
|
|
36
36
|
Radius .radius radius
|
|
37
|
+
Markaby .mab markaby
|
|
38
|
+
Nokogiri .nokogiri nokogiri
|
|
39
|
+
CoffeeScript .coffee coffee-script (+node coffee)
|
|
40
|
+
|
|
41
|
+
These template engines ship with their own Tilt integration:
|
|
42
|
+
|
|
43
|
+
ENGINE FILE EXTENSIONS REQUIRED LIBRARIES
|
|
44
|
+
-------------------------- ----------------- ----------------------------
|
|
45
|
+
Slim .slim slim (>= 0.7)
|
|
37
46
|
|
|
38
47
|
See [TEMPLATES.md][t] for detailed information on template engine
|
|
39
48
|
options and supported features.
|
|
@@ -150,44 +159,13 @@ Or, use BlueCloth for markdown instead of RDiscount:
|
|
|
150
159
|
Template Compilation
|
|
151
160
|
--------------------
|
|
152
161
|
|
|
153
|
-
Tilt
|
|
154
|
-
|
|
162
|
+
Tilt compiles generated Ruby source code produced by template engines and reuses
|
|
163
|
+
it on subsequent template invocations. Benchmarks show this yields a 5x-10x
|
|
155
164
|
performance increase over evaluating the Ruby source on each invocation.
|
|
156
165
|
|
|
157
166
|
Template compilation is currently supported for these template engines:
|
|
158
167
|
StringTemplate, ERB, Erubis, Haml, and Builder.
|
|
159
168
|
|
|
160
|
-
To enable template compilation, the `Tilt::CompileSite` module must be mixed in
|
|
161
|
-
to the scope object passed to the template's `#render` method. This can be
|
|
162
|
-
accomplished by including (with `Module#include`) the module in the class used
|
|
163
|
-
for scope objects or by extending (with `Object#extend`) scope objects before
|
|
164
|
-
passing to `Template#render`:
|
|
165
|
-
|
|
166
|
-
require 'tilt'
|
|
167
|
-
|
|
168
|
-
template = Tilt::ERBTemplate.new('foo.erb')
|
|
169
|
-
|
|
170
|
-
# Slow. Uses Object#instance_eval to process template
|
|
171
|
-
class Scope
|
|
172
|
-
end
|
|
173
|
-
scope = Scope.new
|
|
174
|
-
template.render(scope)
|
|
175
|
-
|
|
176
|
-
# Fast. Uses compiled template and Object#send to process template
|
|
177
|
-
class Scope
|
|
178
|
-
include Tilt::CompileSite
|
|
179
|
-
end
|
|
180
|
-
scope = Scope.new
|
|
181
|
-
template.render(scope)
|
|
182
|
-
|
|
183
|
-
# Also fast, though a bit a slower due to having to extend each time
|
|
184
|
-
scope = Object.new
|
|
185
|
-
scope.extend Tilt::CompileSite
|
|
186
|
-
template.render(scope)
|
|
187
|
-
|
|
188
|
-
When the `Tilt::CompileSite` module is not present, template execution falls
|
|
189
|
-
back to evaluating the template from source on each invocation.
|
|
190
|
-
|
|
191
169
|
LICENSE
|
|
192
170
|
-------
|
|
193
171
|
|
data/lib/tilt.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
require 'digest/md5'
|
|
2
|
-
|
|
3
1
|
module Tilt
|
|
4
|
-
|
|
2
|
+
TOPOBJECT = defined?(BasicObject) ? BasicObject : Object
|
|
3
|
+
VERSION = '1.2.2'
|
|
5
4
|
|
|
6
5
|
@template_mappings = {}
|
|
7
6
|
|
|
@@ -42,24 +41,12 @@ module Tilt
|
|
|
42
41
|
@template_mappings[pattern]
|
|
43
42
|
end
|
|
44
43
|
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
# Including this module in scope objects passed to Template#render
|
|
48
|
-
# causes template source to be compiled to methods the first time they're
|
|
49
|
-
# used. This can yield significant (5x-10x) performance increases for
|
|
50
|
-
# templates that support it (ERB, Erubis, Builder).
|
|
51
|
-
#
|
|
52
|
-
# It's also possible (though not recommended) to include this module in
|
|
53
|
-
# Object to enable template compilation globally. The downside is that
|
|
54
|
-
# the template methods will polute the global namespace and could lead to
|
|
55
|
-
# unexpected behavior.
|
|
44
|
+
# Deprecated module.
|
|
56
45
|
module CompileSite
|
|
57
|
-
def __tilt__
|
|
58
|
-
end
|
|
59
46
|
end
|
|
60
47
|
|
|
61
48
|
# Base class for template implementations. Subclasses must implement
|
|
62
|
-
# the #prepare method and one of the #evaluate or #
|
|
49
|
+
# the #prepare method and one of the #evaluate or #precompiled_template
|
|
63
50
|
# methods.
|
|
64
51
|
class Template
|
|
65
52
|
# Template source; loaded from a file or given directly.
|
|
@@ -111,12 +98,15 @@ module Tilt
|
|
|
111
98
|
self.class.engine_initialized = true
|
|
112
99
|
end
|
|
113
100
|
|
|
114
|
-
# used to
|
|
115
|
-
@
|
|
116
|
-
@compiled_method_names = {}
|
|
101
|
+
# used to hold compiled template methods
|
|
102
|
+
@compiled_method = {}
|
|
117
103
|
|
|
118
|
-
#
|
|
119
|
-
|
|
104
|
+
# used on 1.9 to set the encoding if it is not set elsewhere (like a magic comment)
|
|
105
|
+
# currently only used if template compiles to ruby
|
|
106
|
+
@default_encoding = @options.delete :default_encoding
|
|
107
|
+
|
|
108
|
+
# load template data and prepare (uses binread to avoid encoding issues)
|
|
109
|
+
@reader = block || lambda { |t| File.respond_to?(:binread) ? File.binread(@file) : File.read(@file) }
|
|
120
110
|
@data = @reader.call(self)
|
|
121
111
|
prepare
|
|
122
112
|
end
|
|
@@ -174,26 +164,26 @@ module Tilt
|
|
|
174
164
|
raise NotImplementedError
|
|
175
165
|
end
|
|
176
166
|
end
|
|
177
|
-
|
|
178
|
-
# Process the template and return the result. When the scope mixes in
|
|
179
|
-
# the Tilt::CompileSite module, the template is compiled to a method and
|
|
180
|
-
# reused given identical locals keys. When the scope object
|
|
181
|
-
# does not mix in the CompileSite module, the template source is
|
|
182
|
-
# evaluated with instance_eval. In any case, template executation
|
|
183
|
-
# is guaranteed to be performed in the scope object with the locals
|
|
184
|
-
# specified and with support for yielding to the block.
|
|
167
|
+
|
|
185
168
|
def evaluate(scope, locals, &block)
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
169
|
+
cached_evaluate(scope, locals, &block)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Process the template and return the result. The first time this
|
|
173
|
+
# method is called, the template source is evaluated with instance_eval.
|
|
174
|
+
# On the sequential method calls it will compile the template to an
|
|
175
|
+
# unbound method which will lead to better performance. In any case,
|
|
176
|
+
# template executation is guaranteed to be performed in the scope object
|
|
177
|
+
# with the locals specified and with support for yielding to the block.
|
|
178
|
+
def cached_evaluate(scope, locals, &block)
|
|
179
|
+
# Redefine itself to use method compilation the next time:
|
|
180
|
+
def self.cached_evaluate(scope, locals, &block)
|
|
181
|
+
method = compiled_method(locals.keys)
|
|
182
|
+
method.bind(scope).call(locals, &block)
|
|
196
183
|
end
|
|
184
|
+
|
|
185
|
+
# Use instance_eval the first time:
|
|
186
|
+
evaluate_source(scope, locals, &block)
|
|
197
187
|
end
|
|
198
188
|
|
|
199
189
|
# Generates all template source by combining the preamble, template, and
|
|
@@ -207,9 +197,16 @@ module Tilt
|
|
|
207
197
|
# easier and more appropriate.
|
|
208
198
|
def precompiled(locals)
|
|
209
199
|
preamble = precompiled_preamble(locals)
|
|
200
|
+
template = precompiled_template(locals)
|
|
201
|
+
magic_comment = extract_magic_comment(template)
|
|
202
|
+
if magic_comment
|
|
203
|
+
# Magic comment e.g. "# coding: utf-8" has to be in the first line.
|
|
204
|
+
# So we copy the magic comment to the first line.
|
|
205
|
+
preamble = magic_comment + "\n" + preamble
|
|
206
|
+
end
|
|
210
207
|
parts = [
|
|
211
208
|
preamble,
|
|
212
|
-
|
|
209
|
+
template,
|
|
213
210
|
precompiled_postamble(locals)
|
|
214
211
|
]
|
|
215
212
|
[parts.join("\n"), preamble.count("\n") + 1]
|
|
@@ -241,10 +238,10 @@ module Tilt
|
|
|
241
238
|
''
|
|
242
239
|
end
|
|
243
240
|
|
|
244
|
-
# The
|
|
245
|
-
def
|
|
246
|
-
@
|
|
247
|
-
|
|
241
|
+
# The compiled method for the locals keys provided.
|
|
242
|
+
def compiled_method(locals_keys)
|
|
243
|
+
@compiled_method[locals_keys] ||=
|
|
244
|
+
compile_template_method(locals_keys)
|
|
248
245
|
end
|
|
249
246
|
|
|
250
247
|
private
|
|
@@ -274,36 +271,59 @@ module Tilt
|
|
|
274
271
|
end
|
|
275
272
|
end
|
|
276
273
|
|
|
277
|
-
def
|
|
278
|
-
parts = [object_id, @stamp] + locals_keys.map { |k| k.to_s }.sort
|
|
279
|
-
digest = Digest::MD5.hexdigest(parts.join(':'))
|
|
280
|
-
"__tilt_#{digest}"
|
|
281
|
-
end
|
|
282
|
-
|
|
283
|
-
def compile_template_method(method_name, locals)
|
|
274
|
+
def compile_template_method(locals)
|
|
284
275
|
source, offset = precompiled(locals)
|
|
285
|
-
offset +=
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
276
|
+
offset += 5
|
|
277
|
+
method_name = "__tilt_#{Thread.current.object_id.abs}"
|
|
278
|
+
Object.class_eval <<-RUBY, eval_file, line - offset
|
|
279
|
+
#{extract_magic_comment source}
|
|
280
|
+
TOPOBJECT.class_eval do
|
|
281
|
+
def #{method_name}(locals)
|
|
282
|
+
Thread.current[:tilt_vars] = [self, locals]
|
|
283
|
+
class << self
|
|
284
|
+
this, locals = Thread.current[:tilt_vars]
|
|
285
|
+
this.instance_eval do
|
|
286
|
+
#{source}
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
end
|
|
289
290
|
end
|
|
290
291
|
RUBY
|
|
292
|
+
unbind_compiled_method(method_name)
|
|
293
|
+
end
|
|
291
294
|
|
|
292
|
-
|
|
293
|
-
|
|
295
|
+
def unbind_compiled_method(method_name)
|
|
296
|
+
method = TOPOBJECT.instance_method(method_name)
|
|
297
|
+
TOPOBJECT.class_eval { remove_method(method_name) }
|
|
298
|
+
method
|
|
294
299
|
end
|
|
295
300
|
|
|
296
|
-
def
|
|
297
|
-
|
|
301
|
+
def extract_magic_comment(script)
|
|
302
|
+
comment = script.slice(/\A[ \t]*\#.*coding\s*[=:]\s*([[:alnum:]\-_]+).*$/)
|
|
303
|
+
return comment if comment and not %w[ascii-8bit binary].include?($1.downcase)
|
|
304
|
+
"# coding: #{@default_encoding}" if @default_encoding
|
|
298
305
|
end
|
|
299
306
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
+
# Special case Ruby 1.9.1's broken yield.
|
|
308
|
+
#
|
|
309
|
+
# http://github.com/rtomayko/tilt/commit/20c01a5
|
|
310
|
+
# http://redmine.ruby-lang.org/issues/show/3601
|
|
311
|
+
#
|
|
312
|
+
# Remove when 1.9.2 dominates 1.9.1 installs in the wild.
|
|
313
|
+
if RUBY_VERSION =~ /^1.9.1/
|
|
314
|
+
undef compile_template_method
|
|
315
|
+
def compile_template_method(locals)
|
|
316
|
+
source, offset = precompiled(locals)
|
|
317
|
+
offset += 1
|
|
318
|
+
method_name = "__tilt_#{Thread.current.object_id}"
|
|
319
|
+
Object.class_eval <<-RUBY, eval_file, line - offset
|
|
320
|
+
TOPOBJECT.class_eval do
|
|
321
|
+
def #{method_name}(locals)
|
|
322
|
+
#{source}
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
RUBY
|
|
326
|
+
unbind_compiled_method(method_name)
|
|
307
327
|
end
|
|
308
328
|
end
|
|
309
329
|
end
|
|
@@ -528,11 +548,19 @@ module Tilt
|
|
|
528
548
|
|
|
529
549
|
private
|
|
530
550
|
def sass_options
|
|
531
|
-
options.merge(:filename => eval_file, :line => line)
|
|
551
|
+
options.merge(:filename => eval_file, :line => line, :syntax => :sass)
|
|
532
552
|
end
|
|
533
553
|
end
|
|
534
554
|
register 'sass', SassTemplate
|
|
535
555
|
|
|
556
|
+
# Sass's new .scss type template implementation.
|
|
557
|
+
class ScssTemplate < SassTemplate
|
|
558
|
+
private
|
|
559
|
+
def sass_options
|
|
560
|
+
options.merge(:filename => eval_file, :line => line, :syntax => :scss)
|
|
561
|
+
end
|
|
562
|
+
end
|
|
563
|
+
register 'scss', ScssTemplate
|
|
536
564
|
|
|
537
565
|
# Lessscss template implementation. See:
|
|
538
566
|
# http://lesscss.org/
|
|
@@ -555,6 +583,73 @@ module Tilt
|
|
|
555
583
|
register 'less', LessTemplate
|
|
556
584
|
|
|
557
585
|
|
|
586
|
+
# CoffeeScript template implementation. See:
|
|
587
|
+
# http://coffeescript.org/
|
|
588
|
+
#
|
|
589
|
+
# CoffeeScript templates do not support object scopes, locals, or yield.
|
|
590
|
+
class CoffeeScriptTemplate < Template
|
|
591
|
+
@@default_no_wrap = false
|
|
592
|
+
|
|
593
|
+
def self.default_no_wrap
|
|
594
|
+
@@default_no_wrap
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
def self.default_no_wrap=(value)
|
|
598
|
+
@@default_no_wrap = value
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
def initialize_engine
|
|
602
|
+
return if defined? ::CoffeeScript
|
|
603
|
+
require_template_library 'coffee_script'
|
|
604
|
+
end
|
|
605
|
+
|
|
606
|
+
def prepare
|
|
607
|
+
@no_wrap = options.key?(:no_wrap) ? options[:no_wrap] :
|
|
608
|
+
self.class.default_no_wrap
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
def evaluate(scope, locals, &block)
|
|
612
|
+
@output ||= CoffeeScript.compile(data, :no_wrap => @no_wrap)
|
|
613
|
+
end
|
|
614
|
+
end
|
|
615
|
+
register 'coffee', CoffeeScriptTemplate
|
|
616
|
+
|
|
617
|
+
|
|
618
|
+
# Nokogiri template implementation. See:
|
|
619
|
+
# http://nokogiri.org/
|
|
620
|
+
class NokogiriTemplate < Template
|
|
621
|
+
def initialize_engine
|
|
622
|
+
return if defined?(::Nokogiri)
|
|
623
|
+
require_template_library 'nokogiri'
|
|
624
|
+
end
|
|
625
|
+
|
|
626
|
+
def prepare; end
|
|
627
|
+
|
|
628
|
+
def evaluate(scope, locals, &block)
|
|
629
|
+
block &&= proc { yield.gsub(/^<\?xml version=\"1\.0\"\?>\n?/, "") }
|
|
630
|
+
|
|
631
|
+
if data.respond_to?(:to_str)
|
|
632
|
+
super(scope, locals, &block)
|
|
633
|
+
else
|
|
634
|
+
::Nokogiri::XML::Builder.new.tap(&data).to_xml
|
|
635
|
+
end
|
|
636
|
+
end
|
|
637
|
+
|
|
638
|
+
def precompiled_preamble(locals)
|
|
639
|
+
return super if locals.include? :xml
|
|
640
|
+
"xml = ::Nokogiri::XML::Builder.new\n#{super}"
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
def precompiled_postamble(locals)
|
|
644
|
+
"xml.to_xml"
|
|
645
|
+
end
|
|
646
|
+
|
|
647
|
+
def precompiled_template(locals)
|
|
648
|
+
data.to_str
|
|
649
|
+
end
|
|
650
|
+
end
|
|
651
|
+
register 'nokogiri', NokogiriTemplate
|
|
652
|
+
|
|
558
653
|
# Builder template implementation. See:
|
|
559
654
|
# http://builder.rubyforge.org/
|
|
560
655
|
class BuilderTemplate < Template
|
|
@@ -563,20 +658,24 @@ module Tilt
|
|
|
563
658
|
require_template_library 'builder'
|
|
564
659
|
end
|
|
565
660
|
|
|
566
|
-
def prepare
|
|
567
|
-
end
|
|
661
|
+
def prepare; end
|
|
568
662
|
|
|
569
663
|
def evaluate(scope, locals, &block)
|
|
664
|
+
return super(scope, locals, &block) if data.respond_to?(:to_str)
|
|
570
665
|
xml = ::Builder::XmlMarkup.new(:indent => 2)
|
|
571
|
-
|
|
572
|
-
locals[:xml] = xml
|
|
573
|
-
super(scope, locals, &block)
|
|
574
|
-
elsif data.kind_of?(Proc)
|
|
575
|
-
data.call(xml)
|
|
576
|
-
end
|
|
666
|
+
data.call(xml)
|
|
577
667
|
xml.target!
|
|
578
668
|
end
|
|
579
669
|
|
|
670
|
+
def precompiled_preamble(locals)
|
|
671
|
+
return super if locals.include? :xml
|
|
672
|
+
"xml = ::Builder::XmlMarkup.new(:indent => 2)\n#{super}"
|
|
673
|
+
end
|
|
674
|
+
|
|
675
|
+
def precompiled_postamble(locals)
|
|
676
|
+
"xml.target!"
|
|
677
|
+
end
|
|
678
|
+
|
|
580
679
|
def precompiled_template(locals)
|
|
581
680
|
data.to_str
|
|
582
681
|
end
|
|
@@ -651,6 +750,29 @@ module Tilt
|
|
|
651
750
|
register 'md', RDiscountTemplate
|
|
652
751
|
|
|
653
752
|
|
|
753
|
+
# BlueCloth Markdown implementation. See:
|
|
754
|
+
# http://deveiate.org/projects/BlueCloth/
|
|
755
|
+
#
|
|
756
|
+
# RDiscount is a simple text filter. It does not support +scope+ or
|
|
757
|
+
# +locals+. The +:smartypants+ and +:escape_html+ options may be set true
|
|
758
|
+
# to enable those flags on the underlying BlueCloth object.
|
|
759
|
+
class BlueClothTemplate < Template
|
|
760
|
+
def initialize_engine
|
|
761
|
+
return if defined? ::BlueCloth
|
|
762
|
+
require_template_library 'bluecloth'
|
|
763
|
+
end
|
|
764
|
+
|
|
765
|
+
def prepare
|
|
766
|
+
@engine = BlueCloth.new(data, options)
|
|
767
|
+
@output = nil
|
|
768
|
+
end
|
|
769
|
+
|
|
770
|
+
def evaluate(scope, locals, &block)
|
|
771
|
+
@output ||= @engine.to_html
|
|
772
|
+
end
|
|
773
|
+
end
|
|
774
|
+
|
|
775
|
+
|
|
654
776
|
# RedCloth implementation. See:
|
|
655
777
|
# http://redcloth.org/
|
|
656
778
|
class RedClothTemplate < Template
|
|
@@ -729,4 +851,51 @@ module Tilt
|
|
|
729
851
|
end
|
|
730
852
|
end
|
|
731
853
|
register 'radius', RadiusTemplate
|
|
854
|
+
|
|
855
|
+
|
|
856
|
+
# Markaby
|
|
857
|
+
# http://github.com/markaby/markaby
|
|
858
|
+
class MarkabyTemplate < Template
|
|
859
|
+
def self.builder_class
|
|
860
|
+
@builder_class ||= Class.new(Markaby::Builder) do
|
|
861
|
+
def __capture_markaby_tilt__(&block)
|
|
862
|
+
__run_markaby_tilt__ do
|
|
863
|
+
text capture(&block)
|
|
864
|
+
end
|
|
865
|
+
end
|
|
866
|
+
end
|
|
867
|
+
end
|
|
868
|
+
|
|
869
|
+
def initialize_engine
|
|
870
|
+
return if defined? ::Markaby
|
|
871
|
+
require_template_library 'markaby'
|
|
872
|
+
end
|
|
873
|
+
|
|
874
|
+
def prepare
|
|
875
|
+
end
|
|
876
|
+
|
|
877
|
+
def evaluate(scope, locals, &block)
|
|
878
|
+
builder = self.class.builder_class.new({}, scope)
|
|
879
|
+
builder.locals = locals
|
|
880
|
+
|
|
881
|
+
if data.kind_of? Proc
|
|
882
|
+
(class << builder; self end).send(:define_method, :__run_markaby_tilt__, &data)
|
|
883
|
+
else
|
|
884
|
+
builder.instance_eval <<-CODE, __FILE__, __LINE__
|
|
885
|
+
def __run_markaby_tilt__
|
|
886
|
+
#{data}
|
|
887
|
+
end
|
|
888
|
+
CODE
|
|
889
|
+
end
|
|
890
|
+
|
|
891
|
+
if block
|
|
892
|
+
builder.__capture_markaby_tilt__(&block)
|
|
893
|
+
else
|
|
894
|
+
builder.__run_markaby_tilt__
|
|
895
|
+
end
|
|
896
|
+
|
|
897
|
+
builder.to_s
|
|
898
|
+
end
|
|
899
|
+
end
|
|
900
|
+
register 'mab', MarkabyTemplate
|
|
732
901
|
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
li foo
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
text "hello from markaby!"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
text "_why?"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
text "foo"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
li foo
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
require 'contest'
|
|
2
|
+
require 'tilt'
|
|
3
|
+
|
|
4
|
+
begin
|
|
5
|
+
require 'bluecloth'
|
|
6
|
+
|
|
7
|
+
class BlueClothTemplateTest < Test::Unit::TestCase
|
|
8
|
+
setup do
|
|
9
|
+
Tilt.register('markdown', Tilt::BlueClothTemplate)
|
|
10
|
+
Tilt.register('md', Tilt::BlueClothTemplate)
|
|
11
|
+
Tilt.register('mkd', Tilt::BlueClothTemplate)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
teardown do
|
|
15
|
+
# Need to revert to RDiscount, otherwise the RDiscount test will fail
|
|
16
|
+
Tilt.register('markdown', Tilt::RDiscountTemplate)
|
|
17
|
+
Tilt.register('md', Tilt::RDiscountTemplate)
|
|
18
|
+
Tilt.register('mkd', Tilt::RDiscountTemplate)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
test "registered for '.markdown' files unless RDiscount is loaded" do
|
|
22
|
+
unless defined?(RDiscount)
|
|
23
|
+
assert_equal Tilt::BlueClothTemplate, Tilt['test.markdown']
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
test "registered for '.md' files unless RDiscount is loaded" do
|
|
28
|
+
unless defined?(RDiscount)
|
|
29
|
+
assert_equal Tilt::BlueClothTemplate, Tilt['test.md']
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
test "registered for '.mkd' files unless RDiscount is loaded" do
|
|
34
|
+
unless defined?(RDiscount)
|
|
35
|
+
assert_equal Tilt::BlueClothTemplate, Tilt['test.mkd']
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
test "preparing and evaluating templates on #render" do
|
|
40
|
+
template = Tilt::BlueClothTemplate.new { |t| "# Hello World!" }
|
|
41
|
+
assert_equal "<h1>Hello World!</h1>", template.render
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
test "can be rendered more than once" do
|
|
45
|
+
template = Tilt::BlueClothTemplate.new { |t| "# Hello World!" }
|
|
46
|
+
3.times { assert_equal "<h1>Hello World!</h1>", template.render }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
test "smartypants when :smart is set" do
|
|
50
|
+
template = Tilt::BlueClothTemplate.new(:smartypants => true) { |t|
|
|
51
|
+
"OKAY -- 'Smarty Pants'" }
|
|
52
|
+
assert_equal "<p>OKAY — ‘Smarty Pants’</p>",
|
|
53
|
+
template.render
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
test "stripping HTML when :filter_html is set" do
|
|
57
|
+
template = Tilt::BlueClothTemplate.new(:escape_html => true) { |t|
|
|
58
|
+
"HELLO <blink>WORLD</blink>" }
|
|
59
|
+
assert_equal "<p>HELLO <blink>WORLD</blink></p>", template.render
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
rescue LoadError => boom
|
|
63
|
+
warn "Tilt::BlueClothTemplate (disabled)\n"
|
|
64
|
+
end
|
|
@@ -14,6 +14,11 @@ begin
|
|
|
14
14
|
assert_equal "<em>Hello World!</em>\n", template.render
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
+
test "can be rendered more than once" do
|
|
18
|
+
template = Tilt::BuilderTemplate.new { |t| "xml.em 'Hello World!'" }
|
|
19
|
+
3.times { assert_equal "<em>Hello World!</em>\n", template.render }
|
|
20
|
+
end
|
|
21
|
+
|
|
17
22
|
test "passing locals" do
|
|
18
23
|
template = Tilt::BuilderTemplate.new { "xml.em('Hey ' + name + '!')" }
|
|
19
24
|
assert_equal "<em>Hey Joe!</em>\n", template.render(Object.new, :name => 'Joe')
|
|
@@ -28,7 +33,7 @@ begin
|
|
|
28
33
|
|
|
29
34
|
test "passing a block for yield" do
|
|
30
35
|
template = Tilt::BuilderTemplate.new { "xml.em('Hey ' + yield + '!')" }
|
|
31
|
-
assert_equal "<em>Hey Joe!</em>\n", template.render { 'Joe' }
|
|
36
|
+
3.times { assert_equal "<em>Hey Joe!</em>\n", template.render { 'Joe' }}
|
|
32
37
|
end
|
|
33
38
|
|
|
34
39
|
test "block style templates" do
|
|
@@ -38,6 +43,16 @@ begin
|
|
|
38
43
|
end
|
|
39
44
|
assert_equal "<em>Hey Joe!</em>\n", template.render
|
|
40
45
|
end
|
|
46
|
+
|
|
47
|
+
test "allows nesting raw XML" do
|
|
48
|
+
subtemplate = Tilt::BuilderTemplate.new { "xml.em 'Hello World!'" }
|
|
49
|
+
template = Tilt::BuilderTemplate.new { "xml.strong { xml << yield }" }
|
|
50
|
+
3.times do
|
|
51
|
+
options = { :xml => Builder::XmlMarkup.new }
|
|
52
|
+
assert_equal "<strong>\n<em>Hello World!</em>\n</strong>\n",
|
|
53
|
+
template.render(options) { subtemplate.render(options) }
|
|
54
|
+
end
|
|
55
|
+
end
|
|
41
56
|
end
|
|
42
57
|
rescue LoadError
|
|
43
58
|
warn "Tilt::BuilderTemplate (disabled)"
|
data/test/tilt_cache_test.rb
CHANGED
|
@@ -14,10 +14,9 @@ class TiltCacheTest < Test::Unit::TestCase
|
|
|
14
14
|
|
|
15
15
|
test "caching with multiple complex arguments to #fetch" do
|
|
16
16
|
template = nil
|
|
17
|
-
|
|
18
|
-
result = @cache.fetch(*args) { template = Tilt::StringTemplate.new {''} }
|
|
17
|
+
result = @cache.fetch('hello', {:foo => 'bar', :baz => 'bizzle'}) { template = Tilt::StringTemplate.new {''} }
|
|
19
18
|
assert_same template, result
|
|
20
|
-
result = @cache.fetch(
|
|
19
|
+
result = @cache.fetch('hello', {:foo => 'bar', :baz => 'bizzle'}) { fail 'should be cached' }
|
|
21
20
|
assert_same template, result
|
|
22
21
|
end
|
|
23
22
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require 'contest'
|
|
2
|
+
require 'tilt'
|
|
3
|
+
|
|
4
|
+
begin
|
|
5
|
+
require 'coffee_script'
|
|
6
|
+
|
|
7
|
+
class CoffeeScriptTemplateTest < Test::Unit::TestCase
|
|
8
|
+
test "is registered for '.coffee' files" do
|
|
9
|
+
assert_equal Tilt::CoffeeScriptTemplate, Tilt['test.coffee']
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
test "compiles and evaluates the template on #render" do
|
|
13
|
+
template = Tilt::CoffeeScriptTemplate.new { |t| "puts 'Hello, World!'\n" }
|
|
14
|
+
assert_match "puts('Hello, World!');", template.render
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
test "can be rendered more than once" do
|
|
18
|
+
template = Tilt::CoffeeScriptTemplate.new { |t| "puts 'Hello, World!'\n" }
|
|
19
|
+
3.times { assert_match "puts('Hello, World!');", template.render }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
test "disabling coffee-script wrapper" do
|
|
23
|
+
template = Tilt::CoffeeScriptTemplate.new(:no_wrap => true) { |t| "puts 'Hello, World!'\n" }
|
|
24
|
+
assert_equal "puts('Hello, World!');", template.render
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
rescue LoadError => boom
|
|
29
|
+
warn "Tilt::CoffeeScriptTemplate (disabled)\n"
|
|
30
|
+
end
|