tilt 1.0.1 → 1.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 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 can compile generated Ruby source code produced by template engines and
154
- reuse on subsequent template invocations. Benchmarks show this yields a 5x-10x
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
- VERSION = '1.0.1'
2
+ TOPOBJECT = defined?(BasicObject) ? BasicObject : Object
3
+ VERSION = '1.2'
5
4
 
6
5
  @template_mappings = {}
7
6
 
@@ -42,24 +41,15 @@ module Tilt
42
41
  @template_mappings[pattern]
43
42
  end
44
43
 
45
- # Mixin allowing template compilation on scope objects.
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__
46
+ def self.append_features(*)
47
+ warn "WARNING: Tilt::CompileSite is deprecated and will be removed in Tilt 2.0 (#{caller.first})."
58
48
  end
59
49
  end
60
50
 
61
51
  # Base class for template implementations. Subclasses must implement
62
- # the #prepare method and one of the #evaluate or #evaluate_source
52
+ # the #prepare method and one of the #evaluate or #precompiled_template
63
53
  # methods.
64
54
  class Template
65
55
  # Template source; loaded from a file or given directly.
@@ -111,12 +101,15 @@ module Tilt
111
101
  self.class.engine_initialized = true
112
102
  end
113
103
 
114
- # used to generate unique method names for template compilation
115
- @stamp = (Time.now.to_f * 10000).to_i
116
- @compiled_method_names = {}
104
+ # used to hold compiled template methods
105
+ @compiled_method = {}
106
+
107
+ # used on 1.9 to set the encoding if it is not set elsewhere (like a magic comment)
108
+ # currently only used if template compiles to ruby
109
+ @default_encoding = @options.delete :default_encoding
117
110
 
118
- # load template data and prepare
119
- @reader = block || lambda { |t| File.read(@file) }
111
+ # load template data and prepare (uses binread to avoid encoding issues)
112
+ @reader = block || lambda { |t| File.respond_to?(:binread) ? File.binread(@file) : File.read(@file) }
120
113
  @data = @reader.call(self)
121
114
  prepare
122
115
  end
@@ -175,25 +168,21 @@ module Tilt
175
168
  end
176
169
  end
177
170
 
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.
171
+ # Process the template and return the result. The first time this
172
+ # method is called, the template source is evaluated with instance_eval.
173
+ # On the sequential method calls it will compile the template to an
174
+ # unbound method which will lead to better performance. In any case,
175
+ # template executation is guaranteed to be performed in the scope object
176
+ # with the locals specified and with support for yielding to the block.
185
177
  def evaluate(scope, locals, &block)
186
- if scope.respond_to?(:__tilt__)
187
- method_name = compiled_method_name(locals.keys)
188
- if scope.respond_to?(method_name)
189
- scope.send(method_name, locals, &block)
190
- else
191
- compile_template_method(method_name, locals)
192
- scope.send(method_name, locals, &block)
193
- end
194
- else
195
- evaluate_source(scope, locals, &block)
178
+ # Redefine itself to use method compilation the next time:
179
+ def self.evaluate(scope, locals, &block)
180
+ method = compiled_method(locals.keys)
181
+ method.bind(scope).call(locals, &block)
196
182
  end
183
+
184
+ # Use instance_eval the first time:
185
+ evaluate_source(scope, locals, &block)
197
186
  end
198
187
 
199
188
  # Generates all template source by combining the preamble, template, and
@@ -207,9 +196,16 @@ module Tilt
207
196
  # easier and more appropriate.
208
197
  def precompiled(locals)
209
198
  preamble = precompiled_preamble(locals)
199
+ template = precompiled_template(locals)
200
+ magic_comment = extract_magic_comment(template)
201
+ if magic_comment
202
+ # Magic comment e.g. "# coding: utf-8" has to be in the first line.
203
+ # So we copy the magic comment to the first line.
204
+ preamble = magic_comment + "\n" + preamble
205
+ end
210
206
  parts = [
211
207
  preamble,
212
- precompiled_template(locals),
208
+ template,
213
209
  precompiled_postamble(locals)
214
210
  ]
215
211
  [parts.join("\n"), preamble.count("\n") + 1]
@@ -241,10 +237,10 @@ module Tilt
241
237
  ''
242
238
  end
243
239
 
244
- # The unique compiled method name for the locals keys provided.
245
- def compiled_method_name(locals_keys)
246
- @compiled_method_names[locals_keys] ||=
247
- generate_compiled_method_name(locals_keys)
240
+ # The compiled method for the locals keys provided.
241
+ def compiled_method(locals_keys)
242
+ @compiled_method[locals_keys] ||=
243
+ compile_template_method(locals_keys)
248
244
  end
249
245
 
250
246
  private
@@ -274,36 +270,59 @@ module Tilt
274
270
  end
275
271
  end
276
272
 
277
- def generate_compiled_method_name(locals_keys)
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)
273
+ def compile_template_method(locals)
284
274
  source, offset = precompiled(locals)
285
- offset += 1
286
- CompileSite.module_eval <<-RUBY, eval_file, line - offset
287
- def #{method_name}(locals)
288
- #{source}
275
+ offset += 5
276
+ method_name = "__tilt_#{Thread.current.object_id.abs}"
277
+ Object.class_eval <<-RUBY, eval_file, line - offset
278
+ #{extract_magic_comment source}
279
+ TOPOBJECT.class_eval do
280
+ def #{method_name}(locals)
281
+ Thread.current[:tilt_vars] = [self, locals]
282
+ class << self
283
+ this, locals = Thread.current[:tilt_vars]
284
+ this.instance_eval do
285
+ #{source}
286
+ end
287
+ end
288
+ end
289
289
  end
290
290
  RUBY
291
+ unbind_compiled_method(method_name)
292
+ end
291
293
 
292
- ObjectSpace.define_finalizer self,
293
- Template.compiled_template_method_remover(CompileSite, method_name)
294
+ def unbind_compiled_method(method_name)
295
+ method = TOPOBJECT.instance_method(method_name)
296
+ TOPOBJECT.class_eval { remove_method(method_name) }
297
+ method
294
298
  end
295
299
 
296
- def self.compiled_template_method_remover(site, method_name)
297
- proc { |oid| garbage_collect_compiled_template_method(site, method_name) }
300
+ def extract_magic_comment(script)
301
+ comment = script.slice(/\A[ \t]*\#.*coding\s*[=:]\s*([[:alnum:]\-_]+).*$/)
302
+ return comment if comment and not %w[ascii-8bit binary].include?($1.downcase)
303
+ "# coding: #{@default_encoding}" if @default_encoding
298
304
  end
299
305
 
300
- def self.garbage_collect_compiled_template_method(site, method_name)
301
- site.module_eval do
302
- begin
303
- remove_method(method_name)
304
- rescue NameError
305
- # method was already removed (ruby >= 1.9)
306
- end
306
+ # Special case Ruby 1.9.1's broken yield.
307
+ #
308
+ # http://github.com/rtomayko/tilt/commit/20c01a5
309
+ # http://redmine.ruby-lang.org/issues/show/3601
310
+ #
311
+ # Remove when 1.9.2 dominates 1.9.1 installs in the wild.
312
+ if RUBY_VERSION =~ /^1.9.1/
313
+ undef compile_template_method
314
+ def compile_template_method(locals)
315
+ source, offset = precompiled(locals)
316
+ offset += 1
317
+ method_name = "__tilt_#{Thread.current.object_id}"
318
+ Object.class_eval <<-RUBY, eval_file, line - offset
319
+ TOPOBJECT.class_eval do
320
+ def #{method_name}(locals)
321
+ #{source}
322
+ end
323
+ end
324
+ RUBY
325
+ unbind_compiled_method(method_name)
307
326
  end
308
327
  end
309
328
  end
@@ -528,11 +547,19 @@ module Tilt
528
547
 
529
548
  private
530
549
  def sass_options
531
- options.merge(:filename => eval_file, :line => line)
550
+ options.merge(:filename => eval_file, :line => line, :syntax => :sass)
532
551
  end
533
552
  end
534
553
  register 'sass', SassTemplate
535
554
 
555
+ # Sass's new .scss type template implementation.
556
+ class ScssTemplate < SassTemplate
557
+ private
558
+ def sass_options
559
+ options.merge(:filename => eval_file, :line => line, :syntax => :scss)
560
+ end
561
+ end
562
+ register 'scss', ScssTemplate
536
563
 
537
564
  # Lessscss template implementation. See:
538
565
  # http://lesscss.org/
@@ -555,6 +582,66 @@ module Tilt
555
582
  register 'less', LessTemplate
556
583
 
557
584
 
585
+ # CoffeeScript template implementation. See:
586
+ # http://coffeescript.org/
587
+ #
588
+ # CoffeeScript templates do not support object scopes, locals, or yield.
589
+ class CoffeeScriptTemplate < Template
590
+ @@default_no_wrap = false
591
+
592
+ def self.default_no_wrap
593
+ @@default_no_wrap
594
+ end
595
+
596
+ def self.default_no_wrap=(value)
597
+ @@default_no_wrap = value
598
+ end
599
+
600
+ def initialize_engine
601
+ return if defined? ::CoffeeScript
602
+ require_template_library 'coffee_script'
603
+ end
604
+
605
+ def prepare
606
+ @no_wrap = options.key?(:no_wrap) ? options[:no_wrap] :
607
+ self.class.default_no_wrap
608
+ end
609
+
610
+ def evaluate(scope, locals, &block)
611
+ @output ||= CoffeeScript.compile(data, :no_wrap => @no_wrap)
612
+ end
613
+ end
614
+ register 'coffee', CoffeeScriptTemplate
615
+
616
+
617
+ # Nokogiri template implementation. See:
618
+ # http://nokogiri.org/
619
+ class NokogiriTemplate < Template
620
+ def initialize_engine
621
+ return if defined?(::Nokogiri)
622
+ require_template_library 'nokogiri'
623
+ end
624
+
625
+ def prepare; end
626
+
627
+ def evaluate(scope, locals, &block)
628
+ xml = ::Nokogiri::XML::Builder.new
629
+ if data.respond_to?(:to_str)
630
+ locals[:xml] = xml
631
+ block &&= proc { yield.gsub(/^<\?xml version=\"1\.0\"\?>\n?/, "") }
632
+ super(scope, locals, &block)
633
+ elsif data.kind_of?(Proc)
634
+ data.call(xml)
635
+ end
636
+ xml.to_xml
637
+ end
638
+
639
+ def precompiled_template(locals)
640
+ data.to_str
641
+ end
642
+ end
643
+ register 'nokogiri', NokogiriTemplate
644
+
558
645
  # Builder template implementation. See:
559
646
  # http://builder.rubyforge.org/
560
647
  class BuilderTemplate < Template
@@ -651,6 +738,29 @@ module Tilt
651
738
  register 'md', RDiscountTemplate
652
739
 
653
740
 
741
+ # BlueCloth Markdown implementation. See:
742
+ # http://deveiate.org/projects/BlueCloth/
743
+ #
744
+ # RDiscount is a simple text filter. It does not support +scope+ or
745
+ # +locals+. The +:smartypants+ and +:escape_html+ options may be set true
746
+ # to enable those flags on the underlying BlueCloth object.
747
+ class BlueClothTemplate < Template
748
+ def initialize_engine
749
+ return if defined? ::BlueCloth
750
+ require_template_library 'bluecloth'
751
+ end
752
+
753
+ def prepare
754
+ @engine = BlueCloth.new(data, options)
755
+ @output = nil
756
+ end
757
+
758
+ def evaluate(scope, locals, &block)
759
+ @output ||= @engine.to_html
760
+ end
761
+ end
762
+
763
+
654
764
  # RedCloth implementation. See:
655
765
  # http://redcloth.org/
656
766
  class RedClothTemplate < Template
@@ -729,4 +839,51 @@ module Tilt
729
839
  end
730
840
  end
731
841
  register 'radius', RadiusTemplate
842
+
843
+
844
+ # Markaby
845
+ # http://github.com/markaby/markaby
846
+ class MarkabyTemplate < Template
847
+ def self.builder_class
848
+ @builder_class ||= Class.new(Markaby::Builder) do
849
+ def __capture_markaby_tilt__(&block)
850
+ __run_markaby_tilt__ do
851
+ text capture(&block)
852
+ end
853
+ end
854
+ end
855
+ end
856
+
857
+ def initialize_engine
858
+ return if defined? ::Markaby
859
+ require_template_library 'markaby'
860
+ end
861
+
862
+ def prepare
863
+ end
864
+
865
+ def evaluate(scope, locals, &block)
866
+ builder = self.class.builder_class.new({}, scope)
867
+ builder.locals = locals
868
+
869
+ if data.kind_of? Proc
870
+ (class << builder; self end).send(:define_method, :__run_markaby_tilt__, &data)
871
+ else
872
+ builder.instance_eval <<-CODE, __FILE__, __LINE__
873
+ def __run_markaby_tilt__
874
+ #{data}
875
+ end
876
+ CODE
877
+ end
878
+
879
+ if block
880
+ builder.__capture_markaby_tilt__(&block)
881
+ else
882
+ builder.__run_markaby_tilt__
883
+ end
884
+
885
+ builder.to_s
886
+ end
887
+ end
888
+ register 'mab', MarkabyTemplate
732
889
  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,2 @@
1
+ text("Hey ")
2
+ yield
@@ -0,0 +1,59 @@
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 "smartypants when :smart is set" do
45
+ template = Tilt::BlueClothTemplate.new(:smartypants => true) { |t|
46
+ "OKAY -- 'Smarty Pants'" }
47
+ assert_equal "<p>OKAY &mdash; &lsquo;Smarty Pants&rsquo;</p>",
48
+ template.render
49
+ end
50
+
51
+ test "stripping HTML when :filter_html is set" do
52
+ template = Tilt::BlueClothTemplate.new(:escape_html => true) { |t|
53
+ "HELLO <blink>WORLD</blink>" }
54
+ assert_equal "<p>HELLO &lt;blink>WORLD&lt;/blink></p>", template.render
55
+ end
56
+ end
57
+ rescue LoadError => boom
58
+ warn "Tilt::BlueClothTemplate (disabled)\n"
59
+ end
@@ -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
- args = ['hello', {:foo => 'bar', :baz => 'bizzle'}]
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(*args) { fail 'should be cached' }
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,25 @@
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 "disabling coffee-script wrapper" do
18
+ template = Tilt::CoffeeScriptTemplate.new(:no_wrap => true) { |t| "puts 'Hello, World!'\n" }
19
+ assert_equal "puts('Hello, World!');", template.render
20
+ end
21
+ end
22
+
23
+ rescue LoadError => boom
24
+ warn "Tilt::CoffeeScriptTemplate (disabled)\n"
25
+ end
@@ -17,48 +17,13 @@ class CompileSiteTest < Test::Unit::TestCase
17
17
  end
18
18
 
19
19
  class Scope
20
- include Tilt::CompileSite
21
20
  end
22
21
 
23
22
  test "compiling template source to a method" do
24
23
  template = CompilingTemplate.new { |t| "Hello World!" }
25
24
  template.render(Scope.new)
26
- method_name = template.send(:compiled_method_name, [])
27
- method_name = method_name.to_sym if Symbol === Kernel.methods.first
28
- assert Tilt::CompileSite.instance_methods.include?(method_name),
29
- "CompileSite.instance_methods.include?(#{method_name.inspect})"
30
- assert Scope.new.respond_to?(method_name),
31
- "scope.respond_to?(#{method_name.inspect})"
32
- end
33
-
34
- test 'garbage collecting compiled methods' do
35
- template = CompilingTemplate.new { '' }
36
- method_name = template.send(:compiled_method_name, [])
37
- template.render(Scope.new)
38
- assert Scope.new.respond_to?(method_name)
39
- Tilt::Template.send(
40
- :garbage_collect_compiled_template_method,
41
- Tilt::CompileSite,
42
- method_name
43
- )
44
- assert !Scope.new.respond_to?(method_name), "compiled method not removed"
45
- end
46
-
47
- def self.create_and_destroy_template
48
- template = CompilingTemplate.new { 'Hello World' }
49
- template.render(Scope.new)
50
- method_name = template.send(:compiled_method_name, [])
51
- method_name = method_name.to_sym if Symbol === Kernel.methods.first
52
- [template.object_id, method_name]
53
- end
54
-
55
- finalized_object_id, finalized_method_name = create_and_destroy_template
56
-
57
- test "triggering compiled method gc finalizer" do
58
- assert !Tilt::CompileSite.instance_methods.include?(finalized_method_name),
59
- "CompileSite.instance_methods.include?(#{finalized_method_name.inspect})"
60
- assert !Scope.new.respond_to?(finalized_method_name),
61
- "Scope.new.respond_to?(#{finalized_method_name.inspect})"
25
+ method = template.send(:compiled_method, [])
26
+ assert_kind_of UnboundMethod, method
62
27
  end
63
28
 
64
29
  # This test attempts to surface issues with compiling templates from
@@ -1,6 +1,8 @@
1
+ # coding: utf-8
1
2
  require 'contest'
2
3
  require 'tilt'
3
4
  require 'erb'
5
+ require 'tempfile'
4
6
 
5
7
  class ERBTemplateTest < Test::Unit::TestCase
6
8
  test "registered for '.erb' files" do
@@ -113,18 +115,13 @@ class CompiledERBTemplateTest < Test::Unit::TestCase
113
115
  end
114
116
 
115
117
  class Scope
116
- include Tilt::CompileSite
117
118
  end
118
119
 
119
120
  test "compiling template source to a method" do
120
121
  template = Tilt::ERBTemplate.new { |t| "Hello World!" }
121
122
  template.render(Scope.new)
122
- method_name = template.send(:compiled_method_name, [])
123
- method_name = method_name.to_sym if Symbol === Kernel.methods.first
124
- assert Tilt::CompileSite.instance_methods.include?(method_name),
125
- "CompileSite.instance_methods.include?(#{method_name.inspect})"
126
- assert Scope.new.respond_to?(method_name),
127
- "scope.respond_to?(#{method_name.inspect})"
123
+ method = template.send(:compiled_method, [])
124
+ assert_kind_of UnboundMethod, method
128
125
  end
129
126
 
130
127
  test "loading and evaluating templates on #render" do
@@ -199,6 +196,27 @@ class CompiledERBTemplateTest < Test::Unit::TestCase
199
196
  template = Tilt.new('test.erb', :trim => '%') { "\n% if true\nhello\n%end\n" }
200
197
  assert_equal "\nhello\n", template.render(Scope.new)
201
198
  end
199
+
200
+ test "encoding with magic comment" do
201
+ f = Tempfile.open("template")
202
+ f.puts('<%# coding: UTF-8 %>')
203
+ f.puts('ふが <%= @hoge %>')
204
+ f.close()
205
+ @hoge = "ほげ"
206
+ erb = Tilt['erb'].new(f.path)
207
+ 3.times { erb.render(self) }
208
+ f.delete
209
+ end
210
+
211
+ test "encoding with :default_encoding" do
212
+ f = Tempfile.open("template")
213
+ f.puts('ふが <%= @hoge %>')
214
+ f.close()
215
+ @hoge = "ほげ"
216
+ erb = Tilt['erb'].new(f.path, :default_encoding => 'UTF-8')
217
+ 3.times { erb.render(self) }
218
+ f.delete
219
+ end
202
220
  end
203
221
 
204
222
  __END__
@@ -68,18 +68,13 @@ begin
68
68
 
69
69
  class CompiledHamlTemplateTest < Test::Unit::TestCase
70
70
  class Scope
71
- include Tilt::CompileSite
72
71
  end
73
72
 
74
73
  test "compiling template source to a method" do
75
74
  template = Tilt::HamlTemplate.new { |t| "Hello World!" }
76
75
  template.render(Scope.new)
77
- method_name = template.send(:compiled_method_name, [])
78
- method_name = method_name.to_sym if Symbol === Kernel.methods.first
79
- assert Tilt::CompileSite.instance_methods.include?(method_name),
80
- "CompileSite.instance_methods.include?(#{method_name.inspect})"
81
- assert Scope.new.respond_to?(method_name),
82
- "scope.respond_to?(#{method_name.inspect})"
76
+ method = template.send(:compiled_method, [])
77
+ assert_kind_of UnboundMethod, method
83
78
  end
84
79
 
85
80
  test "passing locals" do
@@ -0,0 +1,83 @@
1
+ require 'contest'
2
+ require 'tilt'
3
+
4
+ begin
5
+ require 'markaby'
6
+
7
+ class MarkabyTiltTest < Test::Unit::TestCase
8
+ def setup
9
+ @block = lambda do |t|
10
+ File.read(File.dirname(__FILE__) + "/#{t.file}")
11
+ end
12
+ end
13
+
14
+ test "should be able to render a markaby template with static html" do
15
+ tilt = Tilt::MarkabyTemplate.new("markaby/markaby.mab", &@block)
16
+ assert_equal "hello from markaby!", tilt.render
17
+ end
18
+
19
+ test "should use the contents of the template" do
20
+ tilt = ::Tilt::MarkabyTemplate.new("markaby/markaby_other_static.mab", &@block)
21
+ assert_equal "_why?", tilt.render
22
+ end
23
+
24
+ test "should render from a string (given as data)" do
25
+ tilt = ::Tilt::MarkabyTemplate.new { "html do; end" }
26
+ assert_equal "<html></html>", tilt.render
27
+ end
28
+
29
+ test "should evaluate a template file in the scope given" do
30
+ scope = Object.new
31
+ def scope.foo
32
+ "bar"
33
+ end
34
+
35
+ tilt = ::Tilt::MarkabyTemplate.new("markaby/scope.mab", &@block)
36
+ assert_equal "<li>bar</li>", tilt.render(scope)
37
+ end
38
+
39
+ test "should pass locals to the template" do
40
+ tilt = ::Tilt::MarkabyTemplate.new("markaby/locals.mab", &@block)
41
+ assert_equal "<li>bar</li>", tilt.render(Object.new, { :foo => "bar" })
42
+ end
43
+
44
+ test "should yield to the block given" do
45
+ tilt = ::Tilt::MarkabyTemplate.new("markaby/yielding.mab", &@block)
46
+ eval_scope = Markaby::Builder.new
47
+
48
+ output = tilt.render(Object.new, {}) do
49
+ text("Joe")
50
+ end
51
+
52
+ assert_equal "Hey Joe", output
53
+ end
54
+
55
+ test "should be able to render two templates in a row" do
56
+ tilt = ::Tilt::MarkabyTemplate.new("markaby/render_twice.mab", &@block)
57
+
58
+ assert_equal "foo", tilt.render
59
+ assert_equal "foo", tilt.render
60
+ end
61
+
62
+ test "should retrieve a Tilt::MarkabyTemplate when calling Tilt['hello.mab']" do
63
+ assert_equal Tilt::MarkabyTemplate, ::Tilt['./markaby/markaby.mab']
64
+ end
65
+
66
+ test "should return a new instance of the implementation class (when calling Tilt.new)" do
67
+ assert ::Tilt.new(File.dirname(__FILE__) + "/markaby/markaby.mab").kind_of?(Tilt::MarkabyTemplate)
68
+ end
69
+
70
+ test "should be able to evaluate block style templates" do
71
+ tilt = Tilt::MarkabyTemplate.new { |t| lambda { h1 "Hello World!" }}
72
+ assert_equal "<h1>Hello World!</h1>", tilt.render
73
+ end
74
+
75
+ test "should pass locals to block style templates" do
76
+ tilt = Tilt::MarkabyTemplate.new { |t| lambda { h1 "Hello #{name}!" }}
77
+ assert_equal "<h1>Hello _why!</h1>", tilt.render(nil, :name => "_why")
78
+ end
79
+ end
80
+
81
+ rescue LoadError => boom
82
+ warn "Tilt::MarkabyTemplate (disabled)\n"
83
+ end
@@ -0,0 +1,63 @@
1
+ require 'contest'
2
+ require 'tilt'
3
+
4
+ begin
5
+ require 'nokogiri'
6
+ class NokogiriTemplateTest < Test::Unit::TestCase
7
+ test "registered for '.nokogiri' files" do
8
+ assert_equal Tilt::NokogiriTemplate, Tilt['test.nokogiri']
9
+ assert_equal Tilt::NokogiriTemplate, Tilt['test.xml.nokogiri']
10
+ end
11
+
12
+ test "preparing and evaluating the template on #render" do
13
+ template = Tilt::NokogiriTemplate.new { |t| "xml.em 'Hello World!'" }
14
+ doc = Nokogiri.XML template.render
15
+ assert_equal 'Hello World!', doc.root.text
16
+ assert_equal 'em', doc.root.name
17
+ end
18
+
19
+ test "passing locals" do
20
+ template = Tilt::NokogiriTemplate.new { "xml.em('Hey ' + name + '!')" }
21
+ doc = Nokogiri.XML template.render(Object.new, :name => 'Joe')
22
+ assert_equal 'Hey Joe!', doc.root.text
23
+ assert_equal 'em', doc.root.name
24
+ end
25
+
26
+ test "evaluating in an object scope" do
27
+ template = Tilt::NokogiriTemplate.new { "xml.em('Hey ' + @name + '!')" }
28
+ scope = Object.new
29
+ scope.instance_variable_set :@name, 'Joe'
30
+ doc = Nokogiri.XML template.render(scope)
31
+ assert_equal 'Hey Joe!', doc.root.text
32
+ assert_equal 'em', doc.root.name
33
+ end
34
+
35
+ test "passing a block for yield" do
36
+ template = Tilt::NokogiriTemplate.new { "xml.em('Hey ' + yield + '!')" }
37
+ doc = Nokogiri.XML template.render { 'Joe' }
38
+ assert_equal 'Hey Joe!', doc.root.text
39
+ assert_equal 'em', doc.root.name
40
+ end
41
+
42
+ test "block style templates" do
43
+ template =
44
+ Tilt::NokogiriTemplate.new do |t|
45
+ lambda { |xml| xml.em('Hey Joe!') }
46
+ end
47
+ doc = Nokogiri.XML template.render
48
+ assert_equal 'Hey Joe!', doc.root.text
49
+ assert_equal 'em', doc.root.name
50
+ end
51
+
52
+ test "allows nesting raw XML, API-compatible to Builder" do
53
+ subtemplate = Tilt::NokogiriTemplate.new { "xml.em 'Hello World!'" }
54
+ template = Tilt::NokogiriTemplate.new { "xml.strong { xml << yield }" }
55
+ options = { :xml => Nokogiri::XML::Builder.new }
56
+ doc = Nokogiri.XML(template.render(options) { subtemplate.render(options) })
57
+ assert_equal 'Hello World!', doc.root.text.strip
58
+ assert_equal 'strong', doc.root.name
59
+ end
60
+ end
61
+ rescue LoadError
62
+ warn "Tilt::NokogiriTemplate (disabled)"
63
+ end
@@ -2,6 +2,10 @@ require 'contest'
2
2
  require 'tilt'
3
3
 
4
4
  begin
5
+ # Disable radius tests under Ruby versions >= 1.9.1 since it's still buggy.
6
+ # Remove when fixed upstream.
7
+ raise LoadError if RUBY_VERSION >= "1.9.1"
8
+
5
9
  require 'radius'
6
10
 
7
11
  class RadiusTemplateTest < Test::Unit::TestCase
@@ -16,6 +16,17 @@ begin
16
16
  end
17
17
  end
18
18
 
19
+ class ScssTemplateTest < Test::Unit::TestCase
20
+ test "is registered for '.scss' files" do
21
+ assert_equal Tilt::ScssTemplate, Tilt['test.scss']
22
+ end
23
+
24
+ test "compiles and evaluates the template on #render" do
25
+ template = Tilt::ScssTemplate.new { |t| "#main {\n background-color: #0000f1;\n}" }
26
+ assert_equal "#main {\n background-color: #0000f1; }\n", template.render
27
+ end
28
+ end
29
+
19
30
  rescue LoadError => boom
20
31
  warn "Tilt::SassTemplate (disabled)\n"
21
32
  end
@@ -74,18 +74,13 @@ class CompiledStringTemplateTest < Test::Unit::TestCase
74
74
  end
75
75
 
76
76
  class Scope
77
- include Tilt::CompileSite
78
77
  end
79
78
 
80
79
  test "compiling template source to a method" do
81
80
  template = Tilt::StringTemplate.new { |t| "Hello World!" }
82
81
  template.render(Scope.new)
83
- method_name = template.send(:compiled_method_name, [])
84
- method_name = method_name.to_sym if Symbol === Kernel.methods.first
85
- assert Tilt::CompileSite.instance_methods.include?(method_name),
86
- "CompileSite.instance_methods.include?(#{method_name.inspect})"
87
- assert Scope.new.respond_to?(method_name),
88
- "scope.respond_to?(#{method_name.inspect})"
82
+ method = template.send(:compiled_method, [])
83
+ assert_kind_of UnboundMethod, method
89
84
  end
90
85
 
91
86
  test "loading and evaluating templates on #render" do
@@ -145,15 +145,4 @@ class TiltTemplateTest < Test::Unit::TestCase
145
145
  inst = SourceGeneratingMockTemplate.new { |t| 'Hey #{CONSTANT}!' }
146
146
  assert_equal "Hey Bob!", inst.render(Person.new("Joe"))
147
147
  end
148
-
149
- class FastPerson < Person
150
- include Tilt::CompileSite
151
- end
152
-
153
- # FAILING CONSTANT TEST. DISABLED FOR 1.0.
154
-
155
- # test "template which accesses a constant with Tilt::CompileSite" do
156
- # inst = SourceGeneratingMockTemplate.new { |t| 'Hey #{CONSTANT}!' }
157
- # assert_equal "Hey Bob!", inst.render(FastPerson.new("Joe"))
158
- # end
159
148
  end
data/tilt.gemspec CHANGED
@@ -3,8 +3,8 @@ Gem::Specification.new do |s|
3
3
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
4
4
 
5
5
  s.name = 'tilt'
6
- s.version = '1.0.1'
7
- s.date = '2010-06-15'
6
+ s.version = '1.2'
7
+ s.date = '2010-12-24'
8
8
 
9
9
  s.description = "Generic interface to multiple Ruby template engines"
10
10
  s.summary = s.description
@@ -21,14 +21,24 @@ Gem::Specification.new do |s|
21
21
  bin/tilt
22
22
  lib/tilt.rb
23
23
  test/contest.rb
24
+ test/markaby/locals.mab
25
+ test/markaby/markaby.mab
26
+ test/markaby/markaby_other_static.mab
27
+ test/markaby/render_twice.mab
28
+ test/markaby/scope.mab
29
+ test/markaby/yielding.mab
30
+ test/tilt_blueclothtemplate_test.rb
24
31
  test/tilt_buildertemplate_test.rb
25
32
  test/tilt_cache_test.rb
33
+ test/tilt_coffeescripttemplate_test.rb
26
34
  test/tilt_compilesite_test.rb
27
35
  test/tilt_erbtemplate_test.rb
28
36
  test/tilt_erubistemplate_test.rb
29
37
  test/tilt_hamltemplate_test.rb
30
38
  test/tilt_lesstemplate_test.rb
31
39
  test/tilt_liquidtemplate_test.rb
40
+ test/tilt_markaby_test.rb
41
+ test/tilt_nokogiritemplate_test.rb
32
42
  test/tilt_radiustemplate_test.rb
33
43
  test/tilt_rdiscounttemplate_test.rb
34
44
  test/tilt_rdoctemplate_test.rb
@@ -53,6 +63,9 @@ Gem::Specification.new do |s|
53
63
  s.add_development_dependency 'liquid'
54
64
  s.add_development_dependency 'less'
55
65
  s.add_development_dependency 'radius'
66
+ s.add_development_dependency 'nokogiri'
67
+ s.add_development_dependency 'markaby'
68
+ s.add_development_dependency 'coffee-script'
56
69
 
57
70
  s.extra_rdoc_files = %w[COPYING]
58
71
 
metadata CHANGED
@@ -1,13 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tilt
3
3
  version: !ruby/object:Gem::Version
4
- hash: 21
4
+ hash: 11
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
- - 0
9
- - 1
10
- version: 1.0.1
8
+ - 2
9
+ version: "1.2"
11
10
  platform: ruby
12
11
  authors:
13
12
  - Ryan Tomayko
@@ -15,7 +14,7 @@ autorequire:
15
14
  bindir: bin
16
15
  cert_chain: []
17
16
 
18
- date: 2010-06-15 00:00:00 -07:00
17
+ date: 2010-12-24 00:00:00 -08:00
19
18
  default_executable: tilt
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency
@@ -132,6 +131,48 @@ dependencies:
132
131
  version: "0"
133
132
  type: :development
134
133
  version_requirements: *id008
134
+ - !ruby/object:Gem::Dependency
135
+ name: nokogiri
136
+ prerelease: false
137
+ requirement: &id009 !ruby/object:Gem::Requirement
138
+ none: false
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ hash: 3
143
+ segments:
144
+ - 0
145
+ version: "0"
146
+ type: :development
147
+ version_requirements: *id009
148
+ - !ruby/object:Gem::Dependency
149
+ name: markaby
150
+ prerelease: false
151
+ requirement: &id010 !ruby/object:Gem::Requirement
152
+ none: false
153
+ requirements:
154
+ - - ">="
155
+ - !ruby/object:Gem::Version
156
+ hash: 3
157
+ segments:
158
+ - 0
159
+ version: "0"
160
+ type: :development
161
+ version_requirements: *id010
162
+ - !ruby/object:Gem::Dependency
163
+ name: coffee-script
164
+ prerelease: false
165
+ requirement: &id011 !ruby/object:Gem::Requirement
166
+ none: false
167
+ requirements:
168
+ - - ">="
169
+ - !ruby/object:Gem::Version
170
+ hash: 3
171
+ segments:
172
+ - 0
173
+ version: "0"
174
+ type: :development
175
+ version_requirements: *id011
135
176
  description: Generic interface to multiple Ruby template engines
136
177
  email: r@tomayko.com
137
178
  executables:
@@ -148,14 +189,24 @@ files:
148
189
  - bin/tilt
149
190
  - lib/tilt.rb
150
191
  - test/contest.rb
192
+ - test/markaby/locals.mab
193
+ - test/markaby/markaby.mab
194
+ - test/markaby/markaby_other_static.mab
195
+ - test/markaby/render_twice.mab
196
+ - test/markaby/scope.mab
197
+ - test/markaby/yielding.mab
198
+ - test/tilt_blueclothtemplate_test.rb
151
199
  - test/tilt_buildertemplate_test.rb
152
200
  - test/tilt_cache_test.rb
201
+ - test/tilt_coffeescripttemplate_test.rb
153
202
  - test/tilt_compilesite_test.rb
154
203
  - test/tilt_erbtemplate_test.rb
155
204
  - test/tilt_erubistemplate_test.rb
156
205
  - test/tilt_hamltemplate_test.rb
157
206
  - test/tilt_lesstemplate_test.rb
158
207
  - test/tilt_liquidtemplate_test.rb
208
+ - test/tilt_markaby_test.rb
209
+ - test/tilt_nokogiritemplate_test.rb
159
210
  - test/tilt_radiustemplate_test.rb
160
211
  - test/tilt_rdiscounttemplate_test.rb
161
212
  - test/tilt_rdoctemplate_test.rb
@@ -205,14 +256,18 @@ signing_key:
205
256
  specification_version: 2
206
257
  summary: Generic interface to multiple Ruby template engines
207
258
  test_files:
259
+ - test/tilt_blueclothtemplate_test.rb
208
260
  - test/tilt_buildertemplate_test.rb
209
261
  - test/tilt_cache_test.rb
262
+ - test/tilt_coffeescripttemplate_test.rb
210
263
  - test/tilt_compilesite_test.rb
211
264
  - test/tilt_erbtemplate_test.rb
212
265
  - test/tilt_erubistemplate_test.rb
213
266
  - test/tilt_hamltemplate_test.rb
214
267
  - test/tilt_lesstemplate_test.rb
215
268
  - test/tilt_liquidtemplate_test.rb
269
+ - test/tilt_markaby_test.rb
270
+ - test/tilt_nokogiritemplate_test.rb
216
271
  - test/tilt_radiustemplate_test.rb
217
272
  - test/tilt_rdiscounttemplate_test.rb
218
273
  - test/tilt_rdoctemplate_test.rb