tilt 0.2 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  Tilt
2
2
  ====
3
3
 
4
- Tilt provides a thin interface over a bunch of different template engines to
5
- make their usage as generic possible. This is useful for web frameworks,
6
- static site generators, and other systems that support multiple template
7
- engines but don't want to code for each of them explicitly.
4
+ Tilt is a thin interface over a bunch of different Ruby template engines in
5
+ an attempt to make their usage as generic possible. This is useful for web
6
+ frameworks, static site generators, and other systems that support multiple
7
+ template engines but don't want to code for each of them individually.
8
8
 
9
9
  The following features are supported for all template engines (assuming the
10
10
  feature is relevant to the engine):
@@ -15,61 +15,130 @@ feature is relevant to the engine):
15
15
  * Backtraces with correct filenames and line numbers
16
16
  * Template compilation caching and reloading
17
17
 
18
- These template engines are currently supported with (many) more on the way:
18
+ The primary goal is to get all of the things listed above right for all
19
+ template engines included in the distribution.
19
20
 
20
- * ERB
21
- * Interpolated Ruby String
22
- * Haml (with the `haml` gem/library)
23
- * Sass (with the `haml` gem/library)
24
- * Builder (with the `builder` gem/library)
25
- * Liquid (with the `liquid` gem/library)
21
+ Support for these template engines is included with the package:
26
22
 
27
- Usage
28
- -----
23
+ ENGINE FILE EXTENSIONS REQUIRED LIBRARIES
24
+ -------------------------- ----------------- ----------------------------
25
+ ERB .erb none (included ruby stdlib)
26
+ Interpolated String .str none (included ruby core)
27
+ Haml .haml haml
28
+ Sass .sass haml
29
+ Builder .builder builder
30
+ Liquid .liquid liquid
31
+ Mustache .mustache mustache
32
+ RDiscount .markdown rdiscount
29
33
 
30
- All supported templates have an implementation class under the `Tilt` module.
31
- Each template implementation follows the exact same interface for creation
32
- and rendering:
34
+ Basic Usage
35
+ -----------
36
+
37
+ Instant gratification:
38
+
39
+ require 'erb'
40
+ require 'tilt'
41
+ template = Tilt.new('templates/foo.erb')
42
+ => #<Tilt::ERBTemplate @file="templates/foo.rb" ...>
43
+ output = template.render
44
+ => "Hello world!"
45
+
46
+ It's recommended that calling programs explicitly require template engine
47
+ libraries (like 'erb' above) at load time. Tilt attempts to lazy require the
48
+ template engine library the first time a template is created but this is
49
+ prone to error in threaded environments.
50
+
51
+ The `Tilt` module contains generic implementation classes for all supported
52
+ template engines. Each template class adheres to the same interface for
53
+ creation and rendering. In the instant gratification example, we let Tilt
54
+ determine the template implementation class based on the filename, but
55
+ `Tilt::Template` implementations can also be used directly:
33
56
 
34
57
  template = Tilt::HamlTemplate.new('templates/foo.haml')
35
58
  output = template.render
36
59
 
37
60
  The `render` method takes an optional evaluation scope and locals hash
38
- arguments. In the following example, the template is evaluated within the
39
- context of the person object and can access the locals `x` and `y`:
61
+ arguments. Here, the template is evaluated within the context of the
62
+ `Person` object with locals `x` and `y`:
40
63
 
41
64
  template = Tilt::ERBTemplate.new('templates/foo.erb')
42
65
  joe = Person.find('joe')
43
66
  output = template.render(joe, :x => 35, :y => 42)
44
67
 
45
- The `render` method may be called multiple times without creating a new
46
- template object. Continuing the previous example, we can render in Jane's
47
- scope with a different set of locals:
68
+ If no scope is provided, the template is evaluated within the context of an
69
+ object created with `Object.new`.
70
+
71
+ A single `Template` instance's `render` method may be called multiple times
72
+ with different scope and locals arguments. Continuing the previous example,
73
+ we render the same compiled template but this time in jane's scope:
48
74
 
49
75
  jane = Person.find('jane')
50
76
  output = template.render(jane, :x => 22, :y => nil)
51
77
 
52
- Blocks can be passed to the render method for templates that support running
53
- arbitrary ruby code and using `yield`. Assuming the following was in a file
54
- named `foo.erb`:
78
+ Blocks can be passed to `render` for templates that support running
79
+ arbitrary ruby code (usually with some form of `yield`). For instance,
80
+ assuming the following in `foo.erb`:
55
81
 
56
82
  Hey <%= yield %>!
57
83
 
58
- The block passed to the `render` method is invoked on `yield`:
84
+ The block passed to `render` is called on `yield`:
59
85
 
60
86
  template = Tilt::ERBTemplate.new('foo.erb')
61
87
  template.render { 'Joe' }
62
88
  # => "Hey Joe!"
63
89
 
64
- There's also a lightweight file extension to template engine mapping layer.
65
- You can pass a filename or extension to `Tilt::[]` to retrieve the
66
- corresponding implementation class:
90
+ Template Mappings
91
+ -----------------
67
92
 
68
- Tilt['hello.erb']
69
- # => Tilt::ERBTemplate
93
+ The `Tilt` module includes methods for associating template implementation
94
+ classes with filename patterns and for locating/instantiating template
95
+ classes based on those associations.
70
96
 
71
- The `Tilt.new` works similarly but returns a new instance of the underlying
72
- implementation class:
97
+ The `Tilt::register` method associates a filename pattern with a specific
98
+ template implementation. To use ERB for files ending in a `.bar` extension:
73
99
 
74
- template = Tilt.new('templates/foo.erb')
75
- output = template.render
100
+ >> Tilt.register 'bar', Tilt::ERBTemplate
101
+ >> Tilt.new('views/foo.bar')
102
+ => #<Tilt::ERBTemplate @file="views/foo.bar" ...>
103
+
104
+ Retrieving the template class for a file or file extension:
105
+
106
+ >> Tilt['foo.bar']
107
+ => Tilt::ERBTemplate
108
+ >> Tilt['haml']
109
+ => Tilt::HamlTemplate
110
+
111
+ It's also possible to register template file mappings that are more specific
112
+ than a file extension. To use Erubis for `bar.erb` but ERB for all other `.erb`
113
+ files:
114
+
115
+ >> Tilt.register 'bar.erb', Tilt::ErubisTemplate
116
+ >> Tilt.new('views/foo.erb')
117
+ => Tilt::ERBTemplate
118
+ >> Tilt.new('views/bar.erb')
119
+ => Tilt::ErubisTemplate
120
+
121
+ The template class is determined by searching for a series of decreasingly
122
+ specific name patterns. When creating a new template with
123
+ `Tilt.new('views/foo.html.erb')`, we check for the following template
124
+ mappings:
125
+
126
+ 1. `views/foo.html.erb`
127
+ 2. `foo.html.erb`
128
+ 3. `html.erb`
129
+ 4. `erb`
130
+
131
+ `Tilt::register` can also be used to select between alternative template
132
+ engines. To use Erubis instead of ERB for `.erb` files:
133
+
134
+ Tilt.register 'erb', Tilt::ErubisTemplate
135
+
136
+ Or, use BlueCloth for markdown instead of RDiscount:
137
+
138
+ Tilt.register 'markdown', Tilt::BlueClothTemplate
139
+
140
+ LICENSE
141
+ -------
142
+
143
+ Tilt is Copyright (c) 2009 [Ryan Tomayko](http://tomayko.com/about) and
144
+ distributed under the MIT license. See the COPYING file for more info.
data/Rakefile CHANGED
@@ -6,6 +6,7 @@ desc 'Generate test coverage report'
6
6
  task :rcov do
7
7
  sh "rcov -Ilib:test test/*_test.rb"
8
8
  end
9
+
9
10
  desc 'Run specs with unit test style output'
10
11
  task :test do |t|
11
12
  sh 'bacon -qa'
@@ -18,11 +19,8 @@ end
18
19
 
19
20
  # PACKAGING =================================================================
20
21
 
21
- # load gemspec like github's gem builder to surface any SAFE issues.
22
- Thread.new do
23
- require 'rubygems/specification'
24
- $spec = eval("$SAFE=3\n#{File.read('tilt.gemspec')}")
25
- end.join
22
+ require 'rubygems/specification'
23
+ $spec ||= eval(File.read('tilt.gemspec'))
26
24
 
27
25
  def package(ext='')
28
26
  "dist/tilt-#{$spec.version}" + ext
@@ -58,8 +56,12 @@ end
58
56
  # GEMSPEC ===================================================================
59
57
 
60
58
  file 'tilt.gemspec' => FileList['{lib,test}/**','Rakefile'] do |f|
59
+ # read version from tilt.rb
60
+ version = File.read('lib/tilt.rb')[/VERSION = '(.*)'/] && $1
61
61
  # read spec file and split out manifest section
62
- spec = File.read(f.name)
62
+ spec = File.
63
+ read(f.name).
64
+ sub(/s\.version\s*=\s*'.*'/, "s.version = '#{version}'")
63
65
  parts = spec.split(" # = MANIFEST =\n")
64
66
  # determine file list from git ls-files
65
67
  files = `git ls-files`.
@@ -0,0 +1,49 @@
1
+ Templates
2
+ =========
3
+
4
+ ERB (`erb`, `rhtml`)
5
+ --------------------
6
+
7
+ [Docs](http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/classes/ERB.html) |
8
+ [Syntax](http://vision-media.ca/resources/ruby/ruby-rdoc-documentation-syntax)
9
+
10
+ ### Example
11
+
12
+ Hello <%= world %>!
13
+
14
+ ### Usage
15
+
16
+ The `Tilt::ERBTemplate` class is registered for all files ending in `.erb` or
17
+ `.rhtml` by default. ERB templates support custom evaluation scopes and locals:
18
+
19
+ >> require 'erb'
20
+ >> template = Tilt.new('hello.html.erb', :trim => '<>')
21
+ => #<Tilt::ERBTemplate @file='hello.html.erb'>
22
+ >> template.render(self, :world => 'World!')
23
+ => "Hello World!"
24
+
25
+ Or, use the `Tilt::ERBTemplate` class directly to process strings:
26
+
27
+ require 'erb'
28
+ template = Tilt::ERBTemplate.new(nil, :trim => '<>') { "Hello <%= world %>!" }
29
+ template.render(self, :world => 'World!')
30
+
31
+ ### Options
32
+
33
+ `:trim => '-'`
34
+
35
+ The ERB trim mode flags. This is a string consisting
36
+ of any combination of the following characters:
37
+
38
+ * `'>'` omits newlines for lines ending in `>`
39
+ * `'<>'` omits newlines for lines starting with `<%` and ending in `%>`
40
+ * `'-'` omits newlines for lines ending in `-%>`.
41
+ * `'%'` enables processing of lines beginning with `%`
42
+
43
+ `:safe => nil`
44
+
45
+ The `$SAFE` level; when set, ERB code will be run in a
46
+ separate thread with `$SAFE` set to the provided level.
47
+
48
+ It's suggested that your program require 'erb' at load time when using this
49
+ template engine.
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env ruby
2
+ require 'ostruct'
3
+ require 'optparse'
4
+ require 'tilt'
5
+
6
+ usage = <<EOF
7
+ Usage: tilt [OPTIONS] [FILE]
8
+ Process template FILE and generate output. With no FILE or when FILE
9
+ is '-', read template from stdin and use the --type option to determine
10
+ the template's type.
11
+
12
+ Options
13
+ -l, --list List supported template engines + file patterns and exit.
14
+ -t, --type=PATTERN Use this template engine; required when FILE is stdin.
15
+
16
+ -h, --help Show this help message.
17
+
18
+ Convert markdown to HTML:
19
+ $ tilt foo.markdown > foo.html
20
+
21
+ Process ERB template:
22
+ $ echo "Answer: <%= 2 + 2 %>" | tilt -t erb
23
+ Answer: 4
24
+ EOF
25
+
26
+ script_name = File.basename($0)
27
+ pattern = nil
28
+ layout = nil
29
+
30
+ ARGV.options do |o|
31
+ o.program_name = script_name
32
+
33
+ # list all available template engines
34
+ o.on("-l", "--list") do
35
+ groups = {}
36
+ Tilt.mappings.each do |pattern,engine|
37
+ key = engine.name.split('::').last.sub(/Template$/, '')
38
+ (groups[key] ||= []) << pattern
39
+ end
40
+ groups.sort { |(k1,v1),(k2,v2)| k1 <=> k2 }.each do |engine,files|
41
+ printf "%-15s %s\n", engine, files.sort.join(', ')
42
+ end
43
+ exit
44
+ end
45
+
46
+ # the template type / pattern
47
+ o.on("-t", "--type=PATTERN") do |val|
48
+ fail "unknown template type: #{val}" if Tilt[val].nil?
49
+ pattern = val
50
+ end
51
+
52
+ # pass template output into the specified layout template
53
+ o.on("-y", "--layout=FILE") do |file|
54
+ paths = [file, "~/.tilt/#{file}", "/etc/tilt/#{file}"]
55
+ layout = paths.
56
+ map { |p| File.expand_path(p) }.
57
+ find { |p| File.exist?(p) }
58
+ fail "no such layout: #{file}" if layout.nil?
59
+ end
60
+
61
+ o.on_tail("-h", "--help") { puts usage; exit }
62
+
63
+ o.parse!
64
+ end
65
+
66
+ file = ARGV.first || '-'
67
+ pattern = file if pattern.nil?
68
+ template =
69
+ Tilt[pattern].new(file) {
70
+ if file == '-'
71
+ $stdin.read
72
+ else
73
+ File.read(file)
74
+ end
75
+ }
76
+ output = template.render
77
+
78
+ # process layout
79
+ output = Tilt.new(layout).render { output } if layout
80
+
81
+ $stdout.write(output)
@@ -1,16 +1,24 @@
1
1
  module Tilt
2
+ VERSION = '0.3'
3
+
2
4
  @template_mappings = {}
3
5
 
6
+ # Hash of template path pattern => template implementation
7
+ # class mappings.
8
+ def self.mappings
9
+ @template_mappings
10
+ end
11
+
4
12
  # Register a template implementation by file extension.
5
13
  def self.register(ext, template_class)
6
- ext = ext.sub(/^\./, '')
7
- @template_mappings[ext.downcase] = template_class
14
+ ext = ext.to_s.sub(/^\./, '')
15
+ mappings[ext.downcase] = template_class
8
16
  end
9
17
 
10
18
  # Create a new template for the given file using the file's extension
11
19
  # to determine the the template mapping.
12
20
  def self.new(file, line=nil, options={}, &block)
13
- if template_class = self[File.basename(file)]
21
+ if template_class = self[file]
14
22
  template_class.new(file, line, options, &block)
15
23
  else
16
24
  fail "No template engine registered for #{File.basename(file)}"
@@ -19,13 +27,21 @@ module Tilt
19
27
 
20
28
  # Lookup a template class given for the given filename or file
21
29
  # extension. Return nil when no implementation is found.
22
- def self.[](filename)
23
- ext = filename.to_s.downcase
24
- until ext.empty?
25
- return @template_mappings[ext] if @template_mappings.key?(ext)
26
- ext = ext.sub(/^[^.]*\.?/, '')
30
+ def self.[](file)
31
+ if @template_mappings.key?(pattern = file.to_s.downcase)
32
+ @template_mappings[pattern]
33
+ elsif @template_mappings.key?(pattern = File.basename(pattern))
34
+ @template_mappings[pattern]
35
+ else
36
+ while !pattern.empty?
37
+ if @template_mappings.key?(pattern)
38
+ return @template_mappings[pattern]
39
+ else
40
+ pattern = pattern.sub(/^[^.]*\.?/, '')
41
+ end
42
+ end
43
+ nil
27
44
  end
28
- nil
29
45
  end
30
46
 
31
47
  # Base class for template implementations. Subclasses must implement
@@ -52,26 +68,44 @@ module Tilt
52
68
  # file is nil, a block is required.
53
69
  def initialize(file=nil, line=1, options={}, &block)
54
70
  raise ArgumentError, "file or block required" if file.nil? && block.nil?
71
+ options, line = line, 1 if line.is_a?(Hash)
55
72
  @file = file
56
73
  @line = line || 1
57
74
  @options = options || {}
58
75
  @reader = block || lambda { |t| File.read(file) }
59
76
  end
60
77
 
61
- # Render the template in the given scope with the locals specified. If a
62
- # block is given, it is typically available within the template via
63
- # +yield+.
64
- def render(scope=Object.new, locals={}, &block)
78
+ # Load template source and compile the template. The template is
79
+ # loaded and compiled the first time this method is called; subsequent
80
+ # calls are no-ops.
81
+ def compile
65
82
  if @data.nil?
66
83
  @data = @reader.call(self)
67
84
  compile!
68
85
  end
86
+ end
87
+
88
+ # Render the template in the given scope with the locals specified. If a
89
+ # block is given, it is typically available within the template via
90
+ # +yield+.
91
+ def render(scope=Object.new, locals={}, &block)
92
+ compile
69
93
  evaluate scope, locals || {}, &block
70
94
  end
71
95
 
96
+ # The basename of the template file.
97
+ def basename(suffix='')
98
+ File.basename(file, suffix) if file
99
+ end
100
+
101
+ # The template file's basename with all extensions chomped off.
102
+ def name
103
+ basename.split('.', 2).first if basename
104
+ end
105
+
72
106
  # The filename used in backtraces to describe the template.
73
107
  def eval_file
74
- @file || '(__TEMPLATE__)'
108
+ file || '(__TEMPLATE__)'
75
109
  end
76
110
 
77
111
  protected
@@ -107,8 +141,10 @@ module Tilt
107
141
  end
108
142
 
109
143
  def require_template_library(name)
110
- warn "WARN: loading '#{name}' library in a non thread-safe way; " +
111
- "explicit require '#{name}' suggested."
144
+ if Thread.list.size > 1
145
+ warn "WARN: tilt autoloading '#{name}' in a non thread-safe way; " +
146
+ "explicit require '#{name}' suggested."
147
+ end
112
148
  require name
113
149
  end
114
150
  end
@@ -152,7 +188,7 @@ module Tilt
152
188
  class ERBTemplate < Template
153
189
  def compile!
154
190
  require_template_library 'erb' unless defined?(::ERB)
155
- @engine = ::ERB.new(data, nil, nil, '@_out_buf')
191
+ @engine = ::ERB.new(data, options[:safe], options[:trim], '@_out_buf')
156
192
  end
157
193
 
158
194
  def template_source
@@ -188,6 +224,20 @@ module Tilt
188
224
  end
189
225
  %w[erb rhtml].each { |ext| register ext, ERBTemplate }
190
226
 
227
+ # Erubis template implementation. See:
228
+ # http://www.kuwata-lab.com/erubis/
229
+ #
230
+ # It's suggested that your program require 'erubis' at load
231
+ # time when using this template engine.
232
+ class ErubisTemplate < ERBTemplate
233
+ def compile!
234
+ require_template_library 'erubis' unless defined?(::Erubis)
235
+ Erubis::Eruby.class_eval(%Q{def add_preamble(src) src << "@_out_buf = _buf = '';" end})
236
+ @engine = ::Erubis::Eruby.new(data)
237
+ end
238
+ end
239
+ register 'erubis', ErubisTemplate
240
+
191
241
  # Haml template implementation. See:
192
242
  # http://haml.hamptoncatlin.com/
193
243
  #
@@ -293,5 +343,107 @@ module Tilt
293
343
  end
294
344
  end
295
345
  register 'markdown', RDiscountTemplate
346
+ register 'md', RDiscountTemplate
296
347
 
348
+ # Mustache is written and maintained by Chris Wanstrath. See:
349
+ # http://github.com/defunkt/mustache
350
+ #
351
+ # It's suggested that your program require 'mustache' at load
352
+ # time when using this template engine.
353
+ #
354
+ # Mustache templates support the following options:
355
+ #
356
+ # * :view - The Mustache subclass that should be used a the view. When
357
+ # this option is specified, the template file will be determined from
358
+ # the view class, and the :namespace and :mustaches options are
359
+ # irrelevant.
360
+ #
361
+ # * :namespace - The class or module where View classes are located.
362
+ # If you have Hurl::App::Views, namespace should be Hurl:App. This
363
+ # defaults to Object, causing ::Views to be searched for classes.
364
+ #
365
+ # * :mustaches - Where mustache views (.rb files) are located, or nil
366
+ # disable auto-requiring of views based on template names. By default,
367
+ # the view file is assumed to be in the same directory as the template
368
+ # file.
369
+ #
370
+ # All other options are assumed to be attribute writer's on the Mustache
371
+ # class and are set when a template is compiled. They are:
372
+ #
373
+ # * :path - The base path where mustache templates (.html files) are
374
+ # located. This defaults to the current working directory.
375
+ #
376
+ # * :template_extension - The file extension used on mustache templates.
377
+ # The default is 'html'.
378
+ #
379
+ class MustacheTemplate < Template
380
+ attr_reader :engine
381
+
382
+ # Locates and compiles the Mustache object used to create new views. The
383
+ def compile!
384
+ require_template_library 'mustache' unless defined?(::Mustache)
385
+
386
+ @view_name = Mustache.classify(name.to_s)
387
+ @namespace = options[:namespace] || Object
388
+
389
+ # Figure out which Mustache class to use.
390
+ @engine =
391
+ if options[:view]
392
+ @view_name = options[:view].name
393
+ options[:view]
394
+ elsif @namespace.const_defined?(:Views) &&
395
+ @namespace::Views.const_defined?(@view_name)
396
+ @namespace::Views.const_get(@view_name)
397
+ elsif load_mustache_view
398
+ engine = @namespace::Views.const_get(@view_name)
399
+ engine.template = data
400
+ engine
401
+ else
402
+ Mustache
403
+ end
404
+
405
+ # set options on the view class
406
+ options.each do |key, value|
407
+ next if %w[view namespace mustaches].include?(key.to_s)
408
+ @engine.send("#{key}=", value) if @engine.respond_to? "#{key}="
409
+ end
410
+ end
411
+
412
+ def evaluate(scope=nil, locals={}, &block)
413
+ # Create a new instance for playing with
414
+ instance = @engine.new
415
+
416
+ # Copy instance variables from scope to the view
417
+ scope.instance_variables.each do |name|
418
+ instance.instance_variable_set(name, scope.instance_variable_get(name))
419
+ end
420
+
421
+ # Locals get added to the view's context
422
+ locals.each do |local, value|
423
+ instance[local] = value
424
+ end
425
+
426
+ # If we're passed a block it's a subview. Sticking it in yield
427
+ # lets us use {{yield}} in layout.html to render the actual page.
428
+ instance[:yield] = block.call if block
429
+
430
+ instance.template = data unless instance.compiled?
431
+
432
+ instance.to_html
433
+ end
434
+
435
+ # Require the mustache view lib if it exists.
436
+ def load_mustache_view
437
+ return if name.nil?
438
+ path = "#{options[:mustaches]}/#{name}.rb"
439
+ if options[:mustaches] && File.exist?(path)
440
+ require path.chomp('.rb')
441
+ path
442
+ elsif File.exist?(path = file.sub(/\.[^\/]+$/, '.rb'))
443
+ require path.chomp('.rb')
444
+ path
445
+ end
446
+ end
447
+ end
448
+ register 'mustache', MustacheTemplate
297
449
  end
@@ -1,7 +1,7 @@
1
1
  require 'bacon'
2
2
  require 'tilt'
3
3
 
4
- describe "Tilt" do
4
+ describe Tilt do
5
5
  class MockTemplate
6
6
  attr_reader :args, :block
7
7
  def initialize(*args, &block)
@@ -14,6 +14,10 @@ describe "Tilt" do
14
14
  lambda { Tilt.register('mock', MockTemplate) }.should.not.raise
15
15
  end
16
16
 
17
+ it "registers template implementation classes by a file extension given as a symbol" do
18
+ lambda { Tilt.register(:mock, MockTemplate) }.should.not.raise
19
+ end
20
+
17
21
  it "looks up template implementation classes by file extension" do
18
22
  impl = Tilt['mock']
19
23
  impl.should.equal MockTemplate
@@ -36,6 +40,11 @@ describe "Tilt" do
36
40
  Tilt['none'].should.be.nil
37
41
  end
38
42
 
43
+ it "exposes the file pattern -> template class Hash at Tilt::mappings" do
44
+ Tilt.should.respond_to :mappings
45
+ Tilt.mappings.should.respond_to :[]
46
+ end
47
+
39
48
  it "creates a new template instance given a filename" do
40
49
  template = Tilt.new('foo.mock', 1, :key => 'val') { 'Hello World!' }
41
50
  template.args.should.equal ['foo.mock', 1, {:key => 'val'}]
@@ -1,8 +1,9 @@
1
1
  require 'bacon'
2
2
  require 'tilt'
3
3
  require 'erb'
4
+ require 'builder'
4
5
 
5
- describe "Tilt::BuilderTemplate" do
6
+ describe Tilt::BuilderTemplate do
6
7
  it "is registered for '.builder' files" do
7
8
  Tilt['test.builder'].should.equal Tilt::BuilderTemplate
8
9
  Tilt['test.xml.builder'].should.equal Tilt::BuilderTemplate
@@ -2,7 +2,7 @@ require 'bacon'
2
2
  require 'tilt'
3
3
  require 'erb'
4
4
 
5
- describe "Tilt::ERBTemplate" do
5
+ describe Tilt::ERBTemplate do
6
6
  it "is registered for '.erb' files" do
7
7
  Tilt['test.erb'].should.equal Tilt::ERBTemplate
8
8
  Tilt['test.html.erb'].should.equal Tilt::ERBTemplate
@@ -65,6 +65,21 @@ describe "Tilt::ERBTemplate" do
65
65
  line.should.equal '6'
66
66
  end
67
67
  end
68
+
69
+ it "renders erb templates with new lines by default" do
70
+ template = Tilt.new('test.erb', 1) { "\n<%= 1 + 1 %>\n" }
71
+ template.render.should.equal "\n2\n"
72
+ end
73
+
74
+ it "strips new lines explicitly when :trim option is set to '-'" do
75
+ template = Tilt.new('test.erb', 1, :trim => '-') { "\n<%= 1 + 1 -%>\n" }
76
+ template.render.should.equal "\n2"
77
+ end
78
+
79
+ it "processes lines start with % when :trim option is set to '%'" do
80
+ template = Tilt.new('test.erb', 1, :trim => '%') { "\n% if true\nhello\n%end\n" }
81
+ template.render.should.equal "\nhello\n"
82
+ end
68
83
  end
69
84
 
70
85
  __END__
@@ -0,0 +1,78 @@
1
+ require 'bacon'
2
+ require 'tilt'
3
+
4
+ begin
5
+ require 'erubis'
6
+ describe "Tilt::ErubisTemplate" do
7
+ it "is registered for '.erubis' files" do
8
+ Tilt['test.erubis'].should.equal Tilt::ErubisTemplate
9
+ Tilt['test.html.erubis'].should.equal Tilt::ErubisTemplate
10
+ end
11
+
12
+ it "compiles and evaluates the template on #render" do
13
+ template = Tilt::ErubisTemplate.new { |t| "Hello World!" }
14
+ template.render.should.equal "Hello World!"
15
+ end
16
+
17
+ it "supports locals" do
18
+ template = Tilt::ErubisTemplate.new { 'Hey <%= name %>!' }
19
+ template.render(Object.new, :name => 'Joe').should.equal "Hey Joe!"
20
+ end
21
+
22
+ it "is evaluated in the object scope provided" do
23
+ template = Tilt::ErubisTemplate.new { 'Hey <%= @name %>!' }
24
+ scope = Object.new
25
+ scope.instance_variable_set :@name, 'Joe'
26
+ template.render(scope).should.equal "Hey Joe!"
27
+ end
28
+
29
+ it "evaluates template_source with yield support" do
30
+ template = Tilt::ErubisTemplate.new { 'Hey <%= yield %>!' }
31
+ template.render { 'Joe' }.should.equal "Hey Joe!"
32
+ end
33
+
34
+ it "reports the file and line properly in backtraces without locals" do
35
+ data = File.read(__FILE__).split("\n__END__\n").last
36
+ fail unless data[0] == ?<
37
+ template = Tilt::ErubisTemplate.new('test.erubis', 11) { data }
38
+ begin
39
+ template.render
40
+ flunk 'should have raised an exception'
41
+ rescue => boom
42
+ boom.should.be.kind_of NameError
43
+ line = boom.backtrace.first
44
+ file, line, meth = line.split(":")
45
+ file.should.equal 'test.erubis'
46
+ line.should.equal '13'
47
+ end
48
+ end
49
+
50
+ it "reports the file and line properly in backtraces with locals" do
51
+ data = File.read(__FILE__).split("\n__END__\n").last
52
+ fail unless data[0] == ?<
53
+ template = Tilt::ErubisTemplate.new('test.erubis', 1) { data }
54
+ begin
55
+ template.render(nil, :name => 'Joe', :foo => 'bar')
56
+ flunk 'should have raised an exception'
57
+ rescue => boom
58
+ boom.should.be.kind_of RuntimeError
59
+ line = boom.backtrace.first
60
+ file, line, meth = line.split(":")
61
+ file.should.equal 'test.erubis'
62
+ line.should.equal '6'
63
+ end
64
+ end
65
+ end
66
+ rescue LoadError => boom
67
+ warn "Tilt::ErubisTemplate (disabled)\n"
68
+ end
69
+
70
+ __END__
71
+ <html>
72
+ <body>
73
+ <h1>Hey <%= name %>!</h1>
74
+
75
+
76
+ <p><% fail %></p>
77
+ </body>
78
+ </html>
@@ -6,7 +6,8 @@ begin
6
6
  end
7
7
 
8
8
  require 'haml'
9
- describe "Tilt::HamlTemplate" do
9
+
10
+ describe Tilt::HamlTemplate do
10
11
  it "is registered for '.haml' files" do
11
12
  Tilt['test.haml'].should.equal Tilt::HamlTemplate
12
13
  end
@@ -3,7 +3,8 @@ require 'tilt'
3
3
 
4
4
  begin
5
5
  require 'liquid'
6
- describe "Tilt::LiquidTemplate" do
6
+
7
+ describe Tilt::LiquidTemplate do
7
8
  it "is registered for '.liquid' files" do
8
9
  Tilt['test.liquid'].should.equal Tilt::LiquidTemplate
9
10
  end
@@ -0,0 +1,64 @@
1
+ require 'bacon'
2
+ require 'tilt'
3
+
4
+ begin
5
+ require 'mustache'
6
+ raise LoadError, "mustache version must be > 0.2.2" if !Mustache.respond_to?(:compiled?)
7
+
8
+ describe Tilt::MustacheTemplate do
9
+ it "is registered for '.mustache' files" do
10
+ Tilt['test.mustache'].should.equal Tilt::MustacheTemplate
11
+ end
12
+
13
+ it "compiles and evaluates the template on #render" do
14
+ template = Tilt::MustacheTemplate.new { |t| "Hello World!" }
15
+ template.render.should.equal "Hello World!"
16
+ end
17
+
18
+ it "supports locals" do
19
+ template = Tilt::MustacheTemplate.new { "<p>Hey {{name}}!</p>" }
20
+ template.render(nil, :name => 'Joe').should.equal "<p>Hey Joe!</p>"
21
+ end
22
+
23
+ it "evaluates template_source with yield support" do
24
+ template = Tilt::MustacheTemplate.new { "<p>Hey {{yield}}!</p>" }
25
+ template.render { 'Joe' }.should.equal "<p>Hey Joe!</p>"
26
+ end
27
+
28
+ module Views
29
+ class Foo < Mustache
30
+ attr_reader :foo
31
+ end
32
+ end
33
+
34
+ it "locates views defined at the top-level by default" do
35
+ template = Tilt::MustacheTemplate.new('foo.mustache') { "<p>Hey {{foo}}!</p>" }
36
+ template.compile
37
+ template.engine.should.equal Views::Foo
38
+ end
39
+
40
+ module Bar
41
+ module Views
42
+ class Bizzle < Mustache
43
+ end
44
+ end
45
+ end
46
+
47
+ it "locates views defined in a custom namespace" do
48
+ template = Tilt::MustacheTemplate.new('bizzle.mustache', :namespace => Bar) { "<p>Hello World!</p>" }
49
+ template.compile
50
+ template.engine.should.equal Bar::Views::Bizzle
51
+ template.render.should.equal "<p>Hello World!</p>"
52
+ end
53
+
54
+ it "copies instance variables from scope object" do
55
+ template = Tilt::MustacheTemplate.new('foo.mustache') { "<p>Hey {{foo}}!</p>" }
56
+ scope = Object.new
57
+ scope.instance_variable_set(:@foo, 'Jane!')
58
+ template.render(scope).should.equal "<p>Hey Jane!!</p>"
59
+ end
60
+ end
61
+
62
+ rescue LoadError => boom
63
+ warn "Tilt::MustacheTemplate (disabled)\n"
64
+ end
@@ -3,7 +3,8 @@ require 'tilt'
3
3
 
4
4
  begin
5
5
  require 'rdiscount'
6
- describe "Tilt::RDiscountTemplate" do
6
+
7
+ describe Tilt::RDiscountTemplate do
7
8
  it "is registered for '.markdown' files" do
8
9
  Tilt['test.markdown'].should.equal Tilt::RDiscountTemplate
9
10
  end
@@ -3,7 +3,9 @@ require 'tilt'
3
3
 
4
4
  begin
5
5
  require 'haml'
6
- describe "Tilt::SassTemplate" do
6
+ require 'sass'
7
+
8
+ describe Tilt::SassTemplate do
7
9
  it "is registered for '.sass' files" do
8
10
  Tilt['test.sass'].should.equal Tilt::SassTemplate
9
11
  end
@@ -1,7 +1,7 @@
1
1
  require 'bacon'
2
2
  require 'tilt'
3
3
 
4
- describe "Tilt::StringTemplate" do
4
+ describe Tilt::StringTemplate do
5
5
  it "is registered for '.str' files" do
6
6
  Tilt['test.str'].should.equal Tilt::StringTemplate
7
7
  end
@@ -1,7 +1,7 @@
1
1
  require 'bacon'
2
2
  require 'tilt'
3
3
 
4
- describe "Tilt::Template" do
4
+ describe Tilt::Template do
5
5
  it "raises ArgumentError when a file or block not given" do
6
6
  lambda { Tilt::Template.new }.should.raise ArgumentError
7
7
  end
@@ -28,6 +28,16 @@ describe "Tilt::Template" do
28
28
  inst.eval_file.should.not.include "\n"
29
29
  end
30
30
 
31
+ it "responds to #basename with the file's basename" do
32
+ inst = Tilt::Template.new('/tmp/templates/foo.html.erb')
33
+ inst.basename.should.equal 'foo.html.erb'
34
+ end
35
+
36
+ it "responds to #name with the file's basename minus file extensions" do
37
+ inst = Tilt::Template.new('/tmp/templates/foo.html.erb')
38
+ inst.name.should.equal 'foo'
39
+ end
40
+
31
41
  it "can be constructed with a data loading block" do
32
42
  lambda {
33
43
  Tilt::Template.new { |template| "Hello World!" }
@@ -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 = '0.2'
7
- s.date = '2009-06-07'
6
+ s.version = '0.3'
7
+ s.date = '2009-10-16'
8
8
 
9
9
  s.description = "Generic interface to multiple Ruby template engines"
10
10
  s.summary = s.description
@@ -17,13 +17,17 @@ Gem::Specification.new do |s|
17
17
  COPYING
18
18
  README.md
19
19
  Rakefile
20
+ TEMPLATES.md
21
+ bin/tilt
20
22
  lib/tilt.rb
21
23
  test/.bacon
22
24
  test/spec_tilt.rb
23
25
  test/spec_tilt_buildertemplate.rb
24
26
  test/spec_tilt_erbtemplate.rb
27
+ test/spec_tilt_erubistemplate.rb
25
28
  test/spec_tilt_hamltemplate.rb
26
29
  test/spec_tilt_liquid_template.rb
30
+ test/spec_tilt_mustachetemplate.rb
27
31
  test/spec_tilt_rdiscount.rb
28
32
  test/spec_tilt_sasstemplate.rb
29
33
  test/spec_tilt_stringtemplate.rb
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tilt
3
3
  version: !ruby/object:Gem::Version
4
- version: "0.2"
4
+ version: "0.3"
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Tomayko
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-06-07 00:00:00 -07:00
12
+ date: 2009-10-16 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -25,13 +25,17 @@ files:
25
25
  - COPYING
26
26
  - README.md
27
27
  - Rakefile
28
+ - TEMPLATES.md
29
+ - bin/tilt
28
30
  - lib/tilt.rb
29
31
  - test/.bacon
30
32
  - test/spec_tilt.rb
31
33
  - test/spec_tilt_buildertemplate.rb
32
34
  - test/spec_tilt_erbtemplate.rb
35
+ - test/spec_tilt_erubistemplate.rb
33
36
  - test/spec_tilt_hamltemplate.rb
34
37
  - test/spec_tilt_liquid_template.rb
38
+ - test/spec_tilt_mustachetemplate.rb
35
39
  - test/spec_tilt_rdiscount.rb
36
40
  - test/spec_tilt_sasstemplate.rb
37
41
  - test/spec_tilt_stringtemplate.rb
@@ -39,6 +43,8 @@ files:
39
43
  - tilt.gemspec
40
44
  has_rdoc: true
41
45
  homepage: http://github.com/rtomayko/tilt/
46
+ licenses: []
47
+
42
48
  post_install_message:
43
49
  rdoc_options:
44
50
  - --line-numbers
@@ -64,7 +70,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
64
70
  requirements: []
65
71
 
66
72
  rubyforge_project: wink
67
- rubygems_version: 1.3.1
73
+ rubygems_version: 1.3.4
68
74
  signing_key:
69
75
  specification_version: 2
70
76
  summary: Generic interface to multiple Ruby template engines
@@ -72,8 +78,10 @@ test_files:
72
78
  - test/spec_tilt.rb
73
79
  - test/spec_tilt_buildertemplate.rb
74
80
  - test/spec_tilt_erbtemplate.rb
81
+ - test/spec_tilt_erubistemplate.rb
75
82
  - test/spec_tilt_hamltemplate.rb
76
83
  - test/spec_tilt_liquid_template.rb
84
+ - test/spec_tilt_mustachetemplate.rb
77
85
  - test/spec_tilt_rdiscount.rb
78
86
  - test/spec_tilt_sasstemplate.rb
79
87
  - test/spec_tilt_stringtemplate.rb