tilt 0.2 → 0.3
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 +103 -34
- data/Rakefile +8 -6
- data/TEMPLATES.md +49 -0
- data/bin/tilt +81 -0
- data/lib/tilt.rb +169 -17
- data/test/spec_tilt.rb +10 -1
- data/test/spec_tilt_buildertemplate.rb +2 -1
- data/test/spec_tilt_erbtemplate.rb +16 -1
- data/test/spec_tilt_erubistemplate.rb +78 -0
- data/test/spec_tilt_hamltemplate.rb +2 -1
- data/test/spec_tilt_liquid_template.rb +2 -1
- data/test/spec_tilt_mustachetemplate.rb +64 -0
- data/test/spec_tilt_rdiscount.rb +2 -1
- data/test/spec_tilt_sasstemplate.rb +3 -1
- data/test/spec_tilt_stringtemplate.rb +1 -1
- data/test/spec_tilt_template.rb +11 -1
- data/tilt.gemspec +6 -2
- metadata +11 -3
data/README.md
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
Tilt
|
2
2
|
====
|
3
3
|
|
4
|
-
Tilt
|
5
|
-
make their usage as generic possible. This is useful for web
|
6
|
-
static site generators, and other systems that support multiple
|
7
|
-
engines but don't want to code for each of them
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
31
|
-
|
32
|
-
|
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.
|
39
|
-
|
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
|
-
|
46
|
-
|
47
|
-
|
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
|
53
|
-
arbitrary ruby code
|
54
|
-
|
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
|
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
|
-
|
65
|
-
|
66
|
-
corresponding implementation class:
|
90
|
+
Template Mappings
|
91
|
+
-----------------
|
67
92
|
|
68
|
-
|
69
|
-
|
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
|
72
|
-
implementation
|
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
|
-
|
75
|
-
|
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
|
-
|
22
|
-
|
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.
|
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`.
|
data/TEMPLATES.md
ADDED
@@ -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.
|
data/bin/tilt
ADDED
@@ -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)
|
data/lib/tilt.rb
CHANGED
@@ -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
|
-
|
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[
|
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.[](
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
-
#
|
62
|
-
#
|
63
|
-
#
|
64
|
-
def
|
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
|
-
|
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
|
-
|
111
|
-
|
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,
|
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
|
data/test/spec_tilt.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'bacon'
|
2
2
|
require 'tilt'
|
3
3
|
|
4
|
-
describe
|
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
|
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
|
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>
|
@@ -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
|
data/test/spec_tilt_rdiscount.rb
CHANGED
data/test/spec_tilt_template.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'bacon'
|
2
2
|
require 'tilt'
|
3
3
|
|
4
|
-
describe
|
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!" }
|
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 = '0.
|
7
|
-
s.date = '2009-
|
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.
|
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-
|
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.
|
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
|