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