sinatra 1.0.a → 1.0.b
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sinatra might be problematic. Click here for more details.
- data/CHANGES +59 -21
- data/README.rdoc +21 -7
- data/Rakefile +11 -8
- data/lib/sinatra/base.rb +51 -24
- data/lib/sinatra/main.rb +1 -1
- data/lib/sinatra/tilt.rb +368 -131
- data/sinatra.gemspec +5 -2
- data/test/helpers_test.rb +9 -0
- data/test/less_test.rb +37 -0
- data/test/mapped_error_test.rb +13 -2
- data/test/server_test.rb +1 -1
- data/test/settings_test.rb +47 -16
- data/test/templates_test.rb +14 -10
- data/test/views/hello.less +5 -0
- metadata +71 -28
data/CHANGES
CHANGED
@@ -1,58 +1,88 @@
|
|
1
|
-
= 1.0 /
|
1
|
+
= 1.0 / 2010-01-28 (prerelease)
|
2
2
|
|
3
3
|
* It's now possible to register blocks to run after each request using
|
4
4
|
after filters. After filters run at the end of each request, after
|
5
|
-
routes and error handlers.
|
5
|
+
routes and error handlers. (Jimmy Schementi)
|
6
6
|
|
7
7
|
* Sinatra now uses Tilt <http://github.com/rtomayko/tilt> for rendering
|
8
8
|
templates. This adds support for template caching, consistent
|
9
9
|
template backtraces, and support for new template engines, like
|
10
|
-
mustache and liquid.
|
10
|
+
mustache and liquid. (Ryan Tomayko)
|
11
|
+
|
12
|
+
* ERB, Erubis, and Haml templates are now compiled the first time
|
13
|
+
they're rendered instead of being string eval'd on each invocation.
|
14
|
+
Benchmarks show a 5x-10x improvement in render time. This also
|
15
|
+
reduces the number of objects created, decreasing pressure on Ruby's
|
16
|
+
GC. (Ryan Tomayko)
|
17
|
+
|
18
|
+
* New 'settings' method gives access to options in both class and request
|
19
|
+
scopes. This replaces the 'options' method. (Chris Wanstrath)
|
11
20
|
|
12
21
|
* New boolean 'reload_templates' setting controls whether template files
|
13
22
|
are reread from disk and recompiled on each request. Template read/compile
|
14
|
-
is cached by default in all environments except development.
|
23
|
+
is cached by default in all environments except development. (Ryan Tomayko)
|
15
24
|
|
16
|
-
* New '
|
17
|
-
|
18
|
-
* New 'erubis' helper method for rendering Erubis templates.
|
25
|
+
* New 'erubis' helper method for rendering ERB template with Erubis. The
|
26
|
+
erubis gem is required. (Dylan Egan)
|
19
27
|
|
20
28
|
* New 'cache_control' helper method provides a convenient way of
|
21
29
|
setting the Cache-Control response header. Takes a variable number
|
22
30
|
of boolean directives followed by a hash of value directives, like
|
23
|
-
this:
|
24
|
-
|
31
|
+
this: cache_control :public, :must_revalidate, :max_age => 60
|
32
|
+
(Ryan Tomayko)
|
25
33
|
|
26
34
|
* New 'expires' helper method is like cache_control but takes an
|
27
35
|
integer number of seconds or Time object:
|
28
|
-
|
36
|
+
expires 300, :public, :must_revalidate
|
37
|
+
(Ryan Tomayko)
|
38
|
+
|
39
|
+
* New request.secure? method for checking for an SSL connection.
|
40
|
+
(Adam Wiggins)
|
29
41
|
|
30
|
-
* Sinatra apps can now be run with a `-
|
31
|
-
the address to bind to.
|
42
|
+
* Sinatra apps can now be run with a `-o <addr>` argument to specify
|
43
|
+
the address to bind to. (Ryan Tomayko)
|
32
44
|
|
33
45
|
* Rack::Session::Cookie is now added to the middleware pipeline when
|
34
46
|
running in test environments if the :sessions option is set.
|
47
|
+
(Simon Rozet)
|
35
48
|
|
36
49
|
* Route handlers, before filters, templates, error mappings, and
|
37
50
|
middleware are now resolved dynamically up the inheritance hierarchy
|
38
51
|
when needed instead of duplicating the superclass's version when
|
39
52
|
a new Sinatra::Base subclass is created. This should fix a variety
|
40
53
|
of issues with extensions that need to add any of these things
|
41
|
-
to the base class.
|
54
|
+
to the base class. (Ryan Tomayko)
|
55
|
+
|
56
|
+
* Exception error handlers always override the raise_errors option now.
|
57
|
+
Previously, all exceptions would be raised outside of the application
|
58
|
+
when the raise_errors option was enabled, even if an error handler was
|
59
|
+
defined for that exception. The raise_errors option now controls
|
60
|
+
whether unhandled exceptions are raised (enabled) or if a generic 500
|
61
|
+
error is returned (disabled). (Ryan Tomayko)
|
42
62
|
|
43
|
-
The
|
63
|
+
* The X-Cascade response header is set to 'pass' when no matching route
|
64
|
+
is found or all routes pass. (Josh Peek)
|
44
65
|
|
45
|
-
*
|
46
|
-
|
47
|
-
|
48
|
-
|
66
|
+
* Filters do not run when serving static files anymore. (Ryan Tomayko)
|
67
|
+
|
68
|
+
The following Sinatra features have been obsoleted (removed entirely) in
|
69
|
+
the 1.0 release:
|
70
|
+
|
71
|
+
* The `sinatra/test` library is obsolete. This includes the `Sinatra::Test`
|
72
|
+
module, the `Sinatra::TestHarness` class, and the `get_it`, `post_it`,
|
73
|
+
`put_it`, `delete_it`, and `head_it` helper methods. The
|
49
74
|
[`Rack::Test` library](http://gitrdoc.com/brynary/rack-test) should
|
50
75
|
be used instead.
|
51
76
|
|
52
77
|
* Test framework specific libraries (`sinatra/test/spec`,
|
53
|
-
`sinatra/test/bacon`,`sinatra/test/rspec`, etc.) are obsolete.
|
54
|
-
|
55
|
-
|
78
|
+
`sinatra/test/bacon`,`sinatra/test/rspec`, etc.) are obsolete. See
|
79
|
+
http://www.sinatrarb.com/testing.html for instructions on setting up a
|
80
|
+
testing environment under each of these frameworks.
|
81
|
+
|
82
|
+
* `Sinatra::Default` is obsolete; use `Sinatra::Base` instead.
|
83
|
+
`Sinatra::Base` acts more like `Sinatra::Default` in development mode.
|
84
|
+
For example, static file serving and sexy development error pages are
|
85
|
+
enabled by default.
|
56
86
|
|
57
87
|
* Auto-requiring template libraries in the `erb`, `builder`, `haml`,
|
58
88
|
and `sass` methods is obsolete due to thread-safety issues. You must
|
@@ -65,8 +95,13 @@ The following Sinatra features have been obsoleted in the 1.0 release:
|
|
65
95
|
Template engine options should be passed in the second Hash argument
|
66
96
|
instead.
|
67
97
|
|
98
|
+
* The `use_in_file_templates` method is obsolete. Use
|
99
|
+
`enable :inline_templates` or `set :inline_templates, 'path/to/file'`
|
100
|
+
|
68
101
|
* The 'media_type' helper method is obsolete. Use 'mime_type' instead.
|
69
102
|
|
103
|
+
* The 'mime' main and class method is obsolete. Use 'mime_type' instead.
|
104
|
+
|
70
105
|
* The request-level `send_data` method is no longer supported.
|
71
106
|
|
72
107
|
* The `Sinatra::Event` and `Sinatra::EventContext` classes are no longer
|
@@ -100,6 +135,9 @@ The following Sinatra features have been obsoleted in the 1.0 release:
|
|
100
135
|
within a request are now treated as internal server errors and result in
|
101
136
|
a 500 response status.
|
102
137
|
|
138
|
+
* The `:methodoverride' option to enable/disable the POST _method hack is
|
139
|
+
obsolete; use `:method_override` instead.
|
140
|
+
|
103
141
|
= 0.9.2 / 2009-05-18
|
104
142
|
|
105
143
|
* This version is compatible with Rack 1.0. [Rein Henrichs]
|
data/README.rdoc
CHANGED
@@ -203,6 +203,20 @@ and overridden on an individual basis.
|
|
203
203
|
sass :stylesheet, :style => :expanded # overridden
|
204
204
|
end
|
205
205
|
|
206
|
+
=== Less Templates
|
207
|
+
|
208
|
+
The less gem/library is required to render Less templates:
|
209
|
+
|
210
|
+
## You'll need to require less in your app
|
211
|
+
require 'less'
|
212
|
+
|
213
|
+
get '/stylesheet.css' do
|
214
|
+
content_type 'text/css', :charset => 'utf-8'
|
215
|
+
less :stylesheet
|
216
|
+
end
|
217
|
+
|
218
|
+
Renders <tt>./views/stylesheet.less</tt>.
|
219
|
+
|
206
220
|
=== Inline Templates
|
207
221
|
|
208
222
|
get '/' do
|
@@ -565,20 +579,20 @@ system. The +Sinatra::Application+ class -- a special subclass of
|
|
565
579
|
Sinatra::Base -- receives all :get, :put, :post, :delete, :before,
|
566
580
|
:error, :not_found, :configure, and :set messages sent to the
|
567
581
|
top-level. Have a look at the code for yourself: here's the
|
568
|
-
{Sinatra::Delegator mixin}[http://github.com/sinatra/sinatra/blob/
|
569
|
-
being {included into the main namespace}[http://github.com/sinatra/sinatra/blob/
|
582
|
+
{Sinatra::Delegator mixin}[http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/base.rb#L1128]
|
583
|
+
being {included into the main namespace}[http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/main.rb#L28]
|
570
584
|
|
571
585
|
== Command line
|
572
586
|
|
573
587
|
Sinatra applications can be run directly:
|
574
588
|
|
575
|
-
ruby myapp.rb [-h] [-x] [-e ENVIRONMENT] [-p PORT] [-
|
589
|
+
ruby myapp.rb [-h] [-x] [-e ENVIRONMENT] [-p PORT] [-o HOST] [-s HANDLER]
|
576
590
|
|
577
591
|
Options are:
|
578
592
|
|
579
593
|
-h # help
|
580
594
|
-p # set the port (default is 4567)
|
581
|
-
-
|
595
|
+
-o # set the host (default is 0.0.0.0)
|
582
596
|
-e # set the environment (default is development)
|
583
597
|
-s # specify rack server/handler (default is thin)
|
584
598
|
-x # turn on the mutex lock (default is off)
|
@@ -611,12 +625,12 @@ To update the Sinatra sources in the future:
|
|
611
625
|
|
612
626
|
== More
|
613
627
|
|
614
|
-
* {Project Website}[http://
|
628
|
+
* {Project Website}[http://www.sinatrarb.com/] - Additional documentation,
|
615
629
|
news, and links to other resources.
|
616
|
-
* {Contributing}[http://
|
630
|
+
* {Contributing}[http://www.sinatrarb.com/contributing] - Find a bug? Need
|
617
631
|
help? Have a patch?
|
618
632
|
* {Lighthouse}[http://sinatra.lighthouseapp.com] - Issue tracking and release
|
619
633
|
planning.
|
620
634
|
* {Twitter}[http://twitter.com/sinatra]
|
621
|
-
* {Mailing List}[http://groups.google.com/group/sinatrarb]
|
635
|
+
* {Mailing List}[http://groups.google.com/group/sinatrarb/topics]
|
622
636
|
* {IRC: #sinatra}[irc://chat.freenode.net/#sinatra] on http://freenode.net
|
data/Rakefile
CHANGED
@@ -37,16 +37,19 @@ task 'doc' => ['doc:api']
|
|
37
37
|
task 'doc:api' => ['doc/api/index.html']
|
38
38
|
|
39
39
|
file 'doc/api/index.html' => FileList['lib/**/*.rb','README.rdoc'] do |f|
|
40
|
+
require 'rbconfig'
|
41
|
+
hanna = RbConfig::CONFIG['ruby_install_name'].sub('ruby', 'hanna')
|
40
42
|
rb_files = f.prerequisites
|
41
43
|
sh((<<-end).gsub(/\s+/, ' '))
|
42
|
-
hanna
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
44
|
+
#{hanna}
|
45
|
+
--charset utf8
|
46
|
+
--fmt html
|
47
|
+
--inline-source
|
48
|
+
--line-numbers
|
49
|
+
--main README.rdoc
|
50
|
+
--op doc/api
|
51
|
+
--title 'Sinatra API Documentation'
|
52
|
+
#{rb_files.join(' ')}
|
50
53
|
end
|
51
54
|
end
|
52
55
|
CLEAN.include 'doc/api'
|
data/lib/sinatra/base.rb
CHANGED
@@ -8,12 +8,18 @@ require 'sinatra/showexceptions'
|
|
8
8
|
# require tilt if available; fall back on bundled version.
|
9
9
|
begin
|
10
10
|
require 'tilt'
|
11
|
+
if Tilt::VERSION < '0.8'
|
12
|
+
warn "WARN: sinatra requires tilt >= 0.8; you have #{Tilt::VERSION}. " +
|
13
|
+
"loading bundled version..."
|
14
|
+
Object.send :remove_const, :Tilt
|
15
|
+
raise LoadError
|
16
|
+
end
|
11
17
|
rescue LoadError
|
12
18
|
require 'sinatra/tilt'
|
13
19
|
end
|
14
20
|
|
15
21
|
module Sinatra
|
16
|
-
VERSION = '1.0.
|
22
|
+
VERSION = '1.0.b'
|
17
23
|
|
18
24
|
# The request object. See Rack::Request for more info:
|
19
25
|
# http://rack.rubyforge.org/doc/classes/Rack/Request.html
|
@@ -240,6 +246,7 @@ module Sinatra
|
|
240
246
|
# matches the time specified, execution is immediately halted with a
|
241
247
|
# '304 Not Modified' response.
|
242
248
|
def last_modified(time)
|
249
|
+
return unless time
|
243
250
|
time = time.to_time if time.respond_to?(:to_time)
|
244
251
|
time = time.httpdate if time.respond_to?(:httpdate)
|
245
252
|
response['Last-Modified'] = time
|
@@ -284,15 +291,19 @@ module Sinatra
|
|
284
291
|
#
|
285
292
|
# Possible options are:
|
286
293
|
# :layout If set to false, no layout is rendered, otherwise
|
287
|
-
# the specified layout is used (Ignored for `sass`)
|
294
|
+
# the specified layout is used (Ignored for `sass` and `less`)
|
288
295
|
# :locals A hash with local variables that should be available
|
289
296
|
# in the template
|
290
297
|
module Templates
|
298
|
+
include Tilt::CompileSite
|
299
|
+
|
291
300
|
def erb(template, options={}, locals={})
|
301
|
+
options[:outvar] = '@_out_buf'
|
292
302
|
render :erb, template, options, locals
|
293
303
|
end
|
294
304
|
|
295
305
|
def erubis(template, options={}, locals={})
|
306
|
+
options[:outvar] = '@_out_buf'
|
296
307
|
render :erubis, template, options, locals
|
297
308
|
end
|
298
309
|
|
@@ -305,6 +316,11 @@ module Sinatra
|
|
305
316
|
render :sass, template, options, locals
|
306
317
|
end
|
307
318
|
|
319
|
+
def less(template, options={}, locals={})
|
320
|
+
options[:layout] = false
|
321
|
+
render :less, template, options, locals
|
322
|
+
end
|
323
|
+
|
308
324
|
def builder(template=nil, options={}, locals={}, &block)
|
309
325
|
options, template = template, nil if template.is_a?(Hash)
|
310
326
|
template = Proc.new { block } if template.nil?
|
@@ -340,20 +356,23 @@ module Sinatra
|
|
340
356
|
|
341
357
|
def compile_template(engine, data, options, views)
|
342
358
|
@template_cache.fetch engine, data, options do
|
359
|
+
template = Tilt[engine]
|
360
|
+
raise "Template engine not found: #{engine}" if template.nil?
|
361
|
+
|
343
362
|
case
|
344
363
|
when data.is_a?(Symbol)
|
345
364
|
body, path, line = self.class.templates[data]
|
346
365
|
if body
|
347
366
|
body = body.call if body.respond_to?(:call)
|
348
|
-
|
367
|
+
template.new(path, line.to_i, options) { body }
|
349
368
|
else
|
350
369
|
path = ::File.join(views, "#{data}.#{engine}")
|
351
|
-
|
370
|
+
template.new(path, 1, options)
|
352
371
|
end
|
353
372
|
when data.is_a?(Proc) || data.is_a?(String)
|
354
373
|
body = data.is_a?(String) ? Proc.new { data } : data
|
355
374
|
path, line = self.class.caller_locations.first
|
356
|
-
|
375
|
+
template.new(path, line.to_i, options, &body)
|
357
376
|
else
|
358
377
|
raise ArgumentError
|
359
378
|
end
|
@@ -600,10 +619,16 @@ module Sinatra
|
|
600
619
|
@env['sinatra.error'] = boom
|
601
620
|
|
602
621
|
dump_errors!(boom) if settings.dump_errors?
|
603
|
-
raise boom if settings.
|
622
|
+
raise boom if settings.show_exceptions?
|
604
623
|
|
605
624
|
@response.status = 500
|
606
|
-
error_block!
|
625
|
+
if res = error_block!(boom.class)
|
626
|
+
res
|
627
|
+
elsif settings.raise_errors?
|
628
|
+
raise boom
|
629
|
+
else
|
630
|
+
error_block!(Exception)
|
631
|
+
end
|
607
632
|
end
|
608
633
|
|
609
634
|
# Find an custom error block for the key(s) specified.
|
@@ -613,8 +638,7 @@ module Sinatra
|
|
613
638
|
while base.respond_to?(:errors)
|
614
639
|
if block = base.errors[key]
|
615
640
|
# found a handler, eval and return result
|
616
|
-
|
617
|
-
return res
|
641
|
+
return instance_eval(&block)
|
618
642
|
else
|
619
643
|
base = base.superclass
|
620
644
|
end
|
@@ -679,11 +703,13 @@ module Sinatra
|
|
679
703
|
|
680
704
|
# Sets an option to the given value. If the value is a proc,
|
681
705
|
# the proc will be called every time the option is accessed.
|
682
|
-
def set(option, value=self)
|
706
|
+
def set(option, value=self, &block)
|
707
|
+
raise ArgumentError if block && value != self
|
708
|
+
value = block if block
|
683
709
|
if value.kind_of?(Proc)
|
684
710
|
metadef(option, &value)
|
685
711
|
metadef("#{option}?") { !!__send__(option) }
|
686
|
-
metadef("#{option}=") { |val|
|
712
|
+
metadef("#{option}=") { |val| metadef(option, &Proc.new{val}) }
|
687
713
|
elsif value == self && option.respond_to?(:to_hash)
|
688
714
|
option.to_hash.each { |k,v| set(k, v) }
|
689
715
|
elsif respond_to?("#{option}=")
|
@@ -833,7 +859,7 @@ module Sinatra
|
|
833
859
|
private
|
834
860
|
def route(verb, path, options={}, &block)
|
835
861
|
# Because of self.options.host
|
836
|
-
host_name(options.delete(:
|
862
|
+
host_name(options.delete(:bind)) if options.key?(:host)
|
837
863
|
|
838
864
|
options.each {|option, args| send(option, *args)}
|
839
865
|
|
@@ -927,7 +953,7 @@ module Sinatra
|
|
927
953
|
handler_name = handler.name.gsub(/.*::/, '')
|
928
954
|
puts "== Sinatra/#{Sinatra::VERSION} has taken the stage " +
|
929
955
|
"on #{port} for #{environment} with backup from #{handler_name}" unless handler_name =~/cgi/i
|
930
|
-
handler.run self, :Host =>
|
956
|
+
handler.run self, :Host => bind, :Port => port do |server|
|
931
957
|
trap(:INT) do
|
932
958
|
## Use thins' hard #stop! if available, otherwise just #stop
|
933
959
|
server.respond_to?(:stop!) ? server.stop! : server.stop
|
@@ -951,7 +977,7 @@ module Sinatra
|
|
951
977
|
builder = Rack::Builder.new
|
952
978
|
builder.use Rack::Session::Cookie if sessions?
|
953
979
|
builder.use Rack::CommonLogger if logging?
|
954
|
-
builder.use Rack::MethodOverride if
|
980
|
+
builder.use Rack::MethodOverride if method_override?
|
955
981
|
builder.use ShowExceptions if show_exceptions?
|
956
982
|
middleware.each { |c,a,b| builder.use(c, *a, &b) }
|
957
983
|
|
@@ -1024,24 +1050,29 @@ module Sinatra
|
|
1024
1050
|
reset!
|
1025
1051
|
|
1026
1052
|
set :environment, (ENV['RACK_ENV'] || :development).to_sym
|
1027
|
-
set :raise_errors, Proc.new {
|
1028
|
-
set :dump_errors, Proc.new {
|
1053
|
+
set :raise_errors, Proc.new { test? }
|
1054
|
+
set :dump_errors, Proc.new { !test? }
|
1029
1055
|
set :show_exceptions, Proc.new { development? }
|
1030
1056
|
set :clean_trace, true
|
1031
1057
|
set :sessions, false
|
1032
1058
|
set :logging, false
|
1033
|
-
set :
|
1059
|
+
set :method_override, false
|
1060
|
+
|
1061
|
+
class << self
|
1062
|
+
alias_method :methodoverride?, :method_override?
|
1063
|
+
alias_method :methodoverride=, :method_override=
|
1064
|
+
end
|
1034
1065
|
|
1035
1066
|
set :run, false # start server via at-exit hook?
|
1036
1067
|
set :running, false # is the built-in server running now?
|
1037
1068
|
set :server, %w[thin mongrel webrick]
|
1038
|
-
set :
|
1069
|
+
set :bind, '0.0.0.0'
|
1039
1070
|
set :port, 4567
|
1040
1071
|
|
1041
1072
|
set :app_file, nil
|
1042
1073
|
set :root, Proc.new { app_file && File.expand_path(File.dirname(app_file)) }
|
1043
1074
|
set :views, Proc.new { root && File.join(root, 'views') }
|
1044
|
-
set :reload_templates, Proc.new {
|
1075
|
+
set :reload_templates, Proc.new { development? }
|
1045
1076
|
set :lock, false
|
1046
1077
|
|
1047
1078
|
set :public, Proc.new { root && File.join(root, 'public') }
|
@@ -1095,13 +1126,9 @@ module Sinatra
|
|
1095
1126
|
# top-level. Subclassing Sinatra::Base is heavily recommended for
|
1096
1127
|
# modular applications.
|
1097
1128
|
class Application < Base
|
1098
|
-
set :raise_errors, Proc.new { test? }
|
1099
|
-
set :dump_errors, true
|
1100
|
-
set :sessions, false
|
1101
1129
|
set :logging, Proc.new { ! test? }
|
1102
|
-
set :
|
1130
|
+
set :method_override, true
|
1103
1131
|
set :run, Proc.new { ! test? }
|
1104
|
-
set :static, true
|
1105
1132
|
|
1106
1133
|
def self.register(*extensions, &block) #:nodoc:
|
1107
1134
|
added_methods = extensions.map {|m| m.public_instance_methods }.flatten
|
data/lib/sinatra/main.rb
CHANGED
@@ -17,7 +17,7 @@ module Sinatra
|
|
17
17
|
op.on('-e env') { |val| set :environment, val.to_sym }
|
18
18
|
op.on('-s server') { |val| set :server, val }
|
19
19
|
op.on('-p port') { |val| set :port, val.to_i }
|
20
|
-
op.on('-
|
20
|
+
op.on('-o addr') { |val| set :bind, val }
|
21
21
|
}.parse!(ARGV.dup)
|
22
22
|
end
|
23
23
|
end
|
data/lib/sinatra/tilt.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
|
1
3
|
module Tilt
|
2
|
-
VERSION = '0.
|
4
|
+
VERSION = '0.8'
|
3
5
|
|
4
6
|
@template_mappings = {}
|
5
7
|
|
6
|
-
# Hash of template path pattern => template implementation
|
7
|
-
# class mappings.
|
8
|
+
# Hash of template path pattern => template implementation class mappings.
|
8
9
|
def self.mappings
|
9
10
|
@template_mappings
|
10
11
|
end
|
@@ -25,7 +26,7 @@ module Tilt
|
|
25
26
|
end
|
26
27
|
end
|
27
28
|
|
28
|
-
# Lookup a template class
|
29
|
+
# Lookup a template class for the given filename or file
|
29
30
|
# extension. Return nil when no implementation is found.
|
30
31
|
def self.[](file)
|
31
32
|
if @template_mappings.key?(pattern = file.to_s.downcase)
|
@@ -44,9 +45,24 @@ module Tilt
|
|
44
45
|
end
|
45
46
|
end
|
46
47
|
|
48
|
+
# Mixin allowing template compilation on scope objects.
|
49
|
+
#
|
50
|
+
# Including this module in scope objects passed to Template#render
|
51
|
+
# causes template source to be compiled to methods the first time they're
|
52
|
+
# used. This can yield significant (5x-10x) performance increases for
|
53
|
+
# templates that support it (ERB, Erubis, Builder).
|
54
|
+
#
|
55
|
+
# It's also possible (though not recommended) to include this module in
|
56
|
+
# Object to enable template compilation globally. The downside is that
|
57
|
+
# the template methods will polute the global namespace and could lead to
|
58
|
+
# unexpected behavior.
|
59
|
+
module CompileSite
|
60
|
+
def __tilt__
|
61
|
+
end
|
62
|
+
end
|
47
63
|
|
48
64
|
# Base class for template implementations. Subclasses must implement
|
49
|
-
# the #
|
65
|
+
# the #prepare method and one of the #evaluate or #template_source
|
50
66
|
# methods.
|
51
67
|
class Template
|
52
68
|
# Template source; loaded from a file or given directly.
|
@@ -63,51 +79,55 @@ module Tilt
|
|
63
79
|
# interface.
|
64
80
|
attr_reader :options
|
65
81
|
|
82
|
+
# Used to determine if this class's initialize_engine method has
|
83
|
+
# been called yet.
|
84
|
+
@engine_initialized = false
|
85
|
+
class << self
|
86
|
+
attr_accessor :engine_initialized
|
87
|
+
alias engine_initialized? engine_initialized
|
88
|
+
end
|
89
|
+
|
66
90
|
# Create a new template with the file, line, and options specified. By
|
67
|
-
# default, template data is read from the file
|
68
|
-
#
|
69
|
-
#
|
91
|
+
# default, template data is read from the file. When a block is given,
|
92
|
+
# it should read template data and return as a String. When file is nil,
|
93
|
+
# a block is required.
|
70
94
|
#
|
71
|
-
#
|
72
|
-
# time this template subclass has been initialized.
|
95
|
+
# All arguments are optional.
|
73
96
|
def initialize(file=nil, line=1, options={}, &block)
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
97
|
+
@file, @line, @options = nil, 1, {}
|
98
|
+
|
99
|
+
[options, line, file].compact.each do |arg|
|
100
|
+
case
|
101
|
+
when arg.respond_to?(:to_str) ; @file = arg.to_str
|
102
|
+
when arg.respond_to?(:to_int) ; @line = arg.to_int
|
103
|
+
when arg.respond_to?(:to_hash) ; @options = arg.to_hash
|
104
|
+
else raise TypeError
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
raise ArgumentError, "file or block required" if (@file || block).nil?
|
109
|
+
|
110
|
+
# call the initialize_engine method if this is the very first time
|
111
|
+
# an instance of this class has been created.
|
112
|
+
if !self.class.engine_initialized?
|
82
113
|
initialize_engine
|
83
114
|
self.class.engine_initialized = true
|
84
115
|
end
|
85
|
-
end
|
86
|
-
|
87
|
-
# Called once and only once for each template subclass the first time
|
88
|
-
# the template class is initialized. This should be used to require the
|
89
|
-
# underlying template library and perform any initial setup.
|
90
|
-
def initialize_engine
|
91
|
-
end
|
92
|
-
@engine_initialized = false
|
93
|
-
class << self ; attr_accessor :engine_initialized ; end
|
94
116
|
|
117
|
+
# used to generate unique method names for template compilation
|
118
|
+
@stamp = (Time.now.to_f * 10000).to_i
|
119
|
+
@compiled_method_names = {}
|
95
120
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
if @data.nil?
|
101
|
-
@data = @reader.call(self)
|
102
|
-
compile!
|
103
|
-
end
|
121
|
+
# load template data and prepare
|
122
|
+
@reader = block || lambda { |t| File.read(@file) }
|
123
|
+
@data = @reader.call(self)
|
124
|
+
prepare
|
104
125
|
end
|
105
126
|
|
106
127
|
# Render the template in the given scope with the locals specified. If a
|
107
128
|
# block is given, it is typically available within the template via
|
108
129
|
# +yield+.
|
109
130
|
def render(scope=Object.new, locals={}, &block)
|
110
|
-
compile
|
111
131
|
evaluate scope, locals || {}, &block
|
112
132
|
end
|
113
133
|
|
@@ -127,43 +147,167 @@ module Tilt
|
|
127
147
|
end
|
128
148
|
|
129
149
|
protected
|
130
|
-
#
|
131
|
-
#
|
132
|
-
#
|
150
|
+
# Called once and only once for each template subclass the first time
|
151
|
+
# the template class is initialized. This should be used to require the
|
152
|
+
# underlying template library and perform any initial setup.
|
153
|
+
def initialize_engine
|
154
|
+
end
|
155
|
+
|
156
|
+
# Like Kernel::require but issues a warning urging a manual require when
|
157
|
+
# running under a threaded environment.
|
158
|
+
def require_template_library(name)
|
159
|
+
if Thread.list.size > 1
|
160
|
+
warn "WARN: tilt autoloading '#{name}' in a non thread-safe way; " +
|
161
|
+
"explicit require '#{name}' suggested."
|
162
|
+
end
|
163
|
+
require name
|
164
|
+
end
|
165
|
+
|
166
|
+
# Do whatever preparation is necessary to setup the underlying template
|
167
|
+
# engine. Called immediately after template data is loaded. Instance
|
168
|
+
# variables set in this method are available when #evaluate is called.
|
133
169
|
#
|
134
170
|
# Subclasses must provide an implementation of this method.
|
135
|
-
def
|
136
|
-
|
171
|
+
def prepare
|
172
|
+
if respond_to?(:compile!)
|
173
|
+
# backward compat with tilt < 0.6; just in case
|
174
|
+
warn 'Tilt::Template#compile! is deprecated; implement #prepare instead.'
|
175
|
+
compile!
|
176
|
+
else
|
177
|
+
raise NotImplementedError
|
178
|
+
end
|
137
179
|
end
|
138
180
|
|
139
|
-
# Process the template and return the result.
|
140
|
-
#
|
181
|
+
# Process the template and return the result. When the scope mixes in
|
182
|
+
# the Tilt::CompileSite module, the template is compiled to a method and
|
183
|
+
# reused given identical locals keys. When the scope object
|
184
|
+
# does not mix in the CompileSite module, the template source is
|
185
|
+
# evaluated with instance_eval. In any case, template executation
|
186
|
+
# is guaranteed to be performed in the scope object with the locals
|
187
|
+
# specified and with support for yielding to the block.
|
141
188
|
def evaluate(scope, locals, &block)
|
142
|
-
|
143
|
-
|
144
|
-
|
189
|
+
if scope.respond_to?(:__tilt__)
|
190
|
+
method_name = compiled_method_name(locals.keys)
|
191
|
+
if scope.respond_to?(method_name)
|
192
|
+
scope.send(method_name, locals, &block)
|
193
|
+
else
|
194
|
+
compile_template_method(method_name, locals)
|
195
|
+
scope.send(method_name, locals, &block)
|
196
|
+
end
|
197
|
+
else
|
198
|
+
evaluate_source(scope, locals, &block)
|
199
|
+
end
|
145
200
|
end
|
146
201
|
|
147
|
-
#
|
148
|
-
#
|
149
|
-
#
|
150
|
-
|
202
|
+
# Generates all template source by combining the preamble, template, and
|
203
|
+
# postamble and returns a two-tuple of the form: [source, offset], where
|
204
|
+
# source is the string containing (Ruby) source code for the template and
|
205
|
+
# offset is the integer line offset where line reporting should begin.
|
206
|
+
#
|
207
|
+
# Template subclasses may override this method when they need complete
|
208
|
+
# control over source generation or want to adjust the default line
|
209
|
+
# offset. In most cases, overriding the #precompiled_template method is
|
210
|
+
# easier and more appropriate.
|
211
|
+
def precompiled(locals)
|
212
|
+
preamble = precompiled_preamble(locals)
|
213
|
+
parts = [
|
214
|
+
preamble,
|
215
|
+
precompiled_template(locals),
|
216
|
+
precompiled_postamble(locals)
|
217
|
+
]
|
218
|
+
[parts.join("\n"), preamble.count("\n") + 1]
|
219
|
+
end
|
220
|
+
|
221
|
+
# A string containing the (Ruby) source code for the template. The
|
222
|
+
# default Template#evaluate implementation requires either this method
|
223
|
+
# or the #precompiled method be overridden. When defined, the base
|
224
|
+
# Template guarantees correct file/line handling, locals support, custom
|
225
|
+
# scopes, and support for template compilation when the scope object
|
226
|
+
# allows it.
|
227
|
+
def precompiled_template(locals)
|
151
228
|
raise NotImplementedError
|
152
229
|
end
|
153
230
|
|
231
|
+
# Generates preamble code for initializing template state, and performing
|
232
|
+
# locals assignment. The default implementation performs locals
|
233
|
+
# assignment only. Lines included in the preamble are subtracted from the
|
234
|
+
# source line offset, so adding code to the preamble does not effect line
|
235
|
+
# reporting in Kernel::caller and backtraces.
|
236
|
+
def precompiled_preamble(locals)
|
237
|
+
locals.map { |k,v| "#{k} = locals[:#{k}]" }.join("\n")
|
238
|
+
end
|
239
|
+
|
240
|
+
# Generates postamble code for the precompiled template source. The
|
241
|
+
# string returned from this method is appended to the precompiled
|
242
|
+
# template source.
|
243
|
+
def precompiled_postamble(locals)
|
244
|
+
''
|
245
|
+
end
|
246
|
+
|
247
|
+
# The unique compiled method name for the locals keys provided.
|
248
|
+
def compiled_method_name(locals_keys)
|
249
|
+
@compiled_method_names[locals_keys] ||=
|
250
|
+
generate_compiled_method_name(locals_keys)
|
251
|
+
end
|
252
|
+
|
154
253
|
private
|
155
|
-
|
156
|
-
|
157
|
-
source
|
158
|
-
|
254
|
+
# Evaluate the template source in the context of the scope object.
|
255
|
+
def evaluate_source(scope, locals, &block)
|
256
|
+
source, offset = precompiled(locals)
|
257
|
+
scope.instance_eval(source, eval_file, line - offset)
|
159
258
|
end
|
160
259
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
260
|
+
# JRuby doesn't allow Object#instance_eval to yield to the block it's
|
261
|
+
# closed over. This is by design and (ostensibly) something that will
|
262
|
+
# change in MRI, though no current MRI version tested (1.8.6 - 1.9.2)
|
263
|
+
# exhibits the behavior. More info here:
|
264
|
+
#
|
265
|
+
# http://jira.codehaus.org/browse/JRUBY-2599
|
266
|
+
#
|
267
|
+
# Additionally, JRuby's eval line reporting is off by one compared to
|
268
|
+
# all MRI versions tested.
|
269
|
+
#
|
270
|
+
# We redefine evaluate_source to work around both issues.
|
271
|
+
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
|
272
|
+
undef evaluate_source
|
273
|
+
def evaluate_source(scope, locals, &block)
|
274
|
+
source, offset = precompiled(locals)
|
275
|
+
file, lineno = eval_file, (line - offset) - 1
|
276
|
+
scope.instance_eval { Kernel::eval(source, binding, file, lineno) }
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
def generate_compiled_method_name(locals_keys)
|
281
|
+
parts = [object_id, @stamp] + locals_keys.map { |k| k.to_s }.sort
|
282
|
+
digest = Digest::MD5.hexdigest(parts.join(':'))
|
283
|
+
"__tilt_#{digest}"
|
284
|
+
end
|
285
|
+
|
286
|
+
def compile_template_method(method_name, locals)
|
287
|
+
source, offset = precompiled(locals)
|
288
|
+
offset += 1
|
289
|
+
CompileSite.module_eval <<-RUBY, eval_file, line - offset
|
290
|
+
def #{method_name}(locals)
|
291
|
+
#{source}
|
292
|
+
end
|
293
|
+
RUBY
|
294
|
+
|
295
|
+
ObjectSpace.define_finalizer self,
|
296
|
+
Template.compiled_template_method_remover(CompileSite, method_name)
|
297
|
+
end
|
298
|
+
|
299
|
+
def self.compiled_template_method_remover(site, method_name)
|
300
|
+
proc { |oid| garbage_collect_compiled_template_method(site, method_name) }
|
301
|
+
end
|
302
|
+
|
303
|
+
def self.garbage_collect_compiled_template_method(site, method_name)
|
304
|
+
site.module_eval do
|
305
|
+
begin
|
306
|
+
remove_method(method_name)
|
307
|
+
rescue NameError
|
308
|
+
# method was already removed (ruby >= 1.9)
|
309
|
+
end
|
165
310
|
end
|
166
|
-
require name
|
167
311
|
end
|
168
312
|
end
|
169
313
|
|
@@ -173,7 +317,7 @@ module Tilt
|
|
173
317
|
# cache = Tilt::Cache.new
|
174
318
|
# cache.fetch(path, line, options) { Tilt.new(path, line, options) }
|
175
319
|
#
|
176
|
-
# Subsequent invocations return the already
|
320
|
+
# Subsequent invocations return the already loaded template object.
|
177
321
|
class Cache
|
178
322
|
def initialize
|
179
323
|
@cache = {}
|
@@ -195,11 +339,11 @@ module Tilt
|
|
195
339
|
# The template source is evaluated as a Ruby string. The #{} interpolation
|
196
340
|
# syntax can be used to generated dynamic output.
|
197
341
|
class StringTemplate < Template
|
198
|
-
def
|
342
|
+
def prepare
|
199
343
|
@code = "%Q{#{data}}"
|
200
344
|
end
|
201
345
|
|
202
|
-
def
|
346
|
+
def precompiled_template(locals)
|
203
347
|
@code
|
204
348
|
end
|
205
349
|
end
|
@@ -210,44 +354,46 @@ module Tilt
|
|
210
354
|
# http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/classes/ERB.html
|
211
355
|
class ERBTemplate < Template
|
212
356
|
def initialize_engine
|
213
|
-
|
357
|
+
return if defined? ::ERB
|
358
|
+
require_template_library 'erb'
|
214
359
|
end
|
215
360
|
|
216
|
-
def
|
217
|
-
@
|
361
|
+
def prepare
|
362
|
+
@outvar = (options[:outvar] || '_erbout').to_s
|
363
|
+
@engine = ::ERB.new(data, options[:safe], options[:trim], @outvar)
|
218
364
|
end
|
219
365
|
|
220
|
-
def
|
366
|
+
def precompiled_template(locals)
|
221
367
|
@engine.src
|
222
368
|
end
|
223
369
|
|
224
|
-
def
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
scope.instance_variable_get(:@_out_buf)
|
231
|
-
|
232
|
-
scope.instance_eval source, eval_file, line - offset
|
233
|
-
|
234
|
-
output = scope.instance_variable_get(:@_out_buf)
|
235
|
-
scope.instance_variable_set(:@_out_buf, original_out_buf)
|
236
|
-
|
237
|
-
output
|
370
|
+
def precompiled_preamble(locals)
|
371
|
+
<<-RUBY
|
372
|
+
begin
|
373
|
+
__original_outvar = #{@outvar} if defined?(#{@outvar})
|
374
|
+
#{super}
|
375
|
+
RUBY
|
238
376
|
end
|
239
377
|
|
240
|
-
|
378
|
+
def precompiled_postamble(locals)
|
379
|
+
<<-RUBY
|
380
|
+
#{super}
|
381
|
+
ensure
|
382
|
+
#{@outvar} = __original_outvar
|
383
|
+
end
|
384
|
+
RUBY
|
385
|
+
end
|
241
386
|
|
242
387
|
# ERB generates a line to specify the character coding of the generated
|
243
388
|
# source in 1.9. Account for this in the line offset.
|
244
389
|
if RUBY_VERSION >= '1.9.0'
|
245
|
-
def
|
390
|
+
def precompiled(locals)
|
246
391
|
source, offset = super
|
247
392
|
[source, offset + 1]
|
248
393
|
end
|
249
394
|
end
|
250
395
|
end
|
396
|
+
|
251
397
|
%w[erb rhtml].each { |ext| register ext, ERBTemplate }
|
252
398
|
|
253
399
|
|
@@ -255,20 +401,28 @@ module Tilt
|
|
255
401
|
# http://www.kuwata-lab.com/erubis/
|
256
402
|
class ErubisTemplate < ERBTemplate
|
257
403
|
def initialize_engine
|
258
|
-
|
404
|
+
return if defined? ::Erubis
|
405
|
+
require_template_library 'erubis'
|
259
406
|
end
|
260
407
|
|
261
|
-
def
|
262
|
-
|
408
|
+
def prepare
|
409
|
+
@options.merge!(:preamble => false, :postamble => false)
|
410
|
+
@outvar = (options.delete(:outvar) || '_erbout').to_s
|
263
411
|
@engine = ::Erubis::Eruby.new(data, options)
|
264
412
|
end
|
265
413
|
|
266
|
-
|
414
|
+
def precompiled_preamble(locals)
|
415
|
+
[super, "#{@outvar} = _buf = ''"].join("\n")
|
416
|
+
end
|
417
|
+
|
418
|
+
def precompiled_postamble(locals)
|
419
|
+
["_buf", super].join("\n")
|
420
|
+
end
|
267
421
|
|
268
|
-
# Erubis doesn't have ERB's line-off-by-one under 1.9 problem.
|
269
|
-
# and adjust back.
|
422
|
+
# Erubis doesn't have ERB's line-off-by-one under 1.9 problem.
|
423
|
+
# Override and adjust back.
|
270
424
|
if RUBY_VERSION >= '1.9.0'
|
271
|
-
def
|
425
|
+
def precompiled(locals)
|
272
426
|
source, offset = super
|
273
427
|
[source, offset - 1]
|
274
428
|
end
|
@@ -281,20 +435,54 @@ module Tilt
|
|
281
435
|
# http://haml.hamptoncatlin.com/
|
282
436
|
class HamlTemplate < Template
|
283
437
|
def initialize_engine
|
284
|
-
|
438
|
+
return if defined? ::Haml::Engine
|
439
|
+
require_template_library 'haml'
|
285
440
|
end
|
286
441
|
|
287
|
-
def
|
288
|
-
|
442
|
+
def prepare
|
443
|
+
options = @options.merge(:filename => eval_file, :line => line)
|
444
|
+
@engine = ::Haml::Engine.new(data, options)
|
289
445
|
end
|
290
446
|
|
291
447
|
def evaluate(scope, locals, &block)
|
292
|
-
@engine.
|
448
|
+
if @engine.respond_to?(:precompiled_method_return_value, true)
|
449
|
+
super
|
450
|
+
else
|
451
|
+
@engine.render(scope, locals, &block)
|
452
|
+
end
|
293
453
|
end
|
294
454
|
|
295
|
-
|
296
|
-
|
297
|
-
|
455
|
+
# Precompiled Haml source. Taken from the precompiled_with_ambles
|
456
|
+
# method in Haml::Precompiler:
|
457
|
+
# http://github.com/nex3/haml/blob/master/lib/haml/precompiler.rb#L111-126
|
458
|
+
def precompiled_template(locals)
|
459
|
+
@engine.precompiled
|
460
|
+
end
|
461
|
+
|
462
|
+
def precompiled_preamble(locals)
|
463
|
+
local_assigns = super
|
464
|
+
@engine.instance_eval do
|
465
|
+
<<-RUBY
|
466
|
+
begin
|
467
|
+
extend Haml::Helpers
|
468
|
+
_hamlout = @haml_buffer = Haml::Buffer.new(@haml_buffer, #{options_for_buffer.inspect})
|
469
|
+
_erbout = _hamlout.buffer
|
470
|
+
__in_erb_template = true
|
471
|
+
_haml_locals = locals
|
472
|
+
#{local_assigns}
|
473
|
+
RUBY
|
474
|
+
end
|
475
|
+
end
|
476
|
+
|
477
|
+
def precompiled_postamble(locals)
|
478
|
+
@engine.instance_eval do
|
479
|
+
<<-RUBY
|
480
|
+
#{precompiled_method_return_value}
|
481
|
+
ensure
|
482
|
+
@haml_buffer = @haml_buffer.upper
|
483
|
+
end
|
484
|
+
RUBY
|
485
|
+
end
|
298
486
|
end
|
299
487
|
end
|
300
488
|
register 'haml', HamlTemplate
|
@@ -306,15 +494,16 @@ module Tilt
|
|
306
494
|
# Sass templates do not support object scopes, locals, or yield.
|
307
495
|
class SassTemplate < Template
|
308
496
|
def initialize_engine
|
309
|
-
|
497
|
+
return if defined? ::Sass::Engine
|
498
|
+
require_template_library 'sass'
|
310
499
|
end
|
311
500
|
|
312
|
-
def
|
501
|
+
def prepare
|
313
502
|
@engine = ::Sass::Engine.new(data, sass_options)
|
314
503
|
end
|
315
504
|
|
316
505
|
def evaluate(scope, locals, &block)
|
317
|
-
@engine.render
|
506
|
+
@output ||= @engine.render
|
318
507
|
end
|
319
508
|
|
320
509
|
private
|
@@ -325,14 +514,36 @@ module Tilt
|
|
325
514
|
register 'sass', SassTemplate
|
326
515
|
|
327
516
|
|
517
|
+
# Lessscss template implementation. See:
|
518
|
+
# http://lesscss.org/
|
519
|
+
#
|
520
|
+
# Less templates do not support object scopes, locals, or yield.
|
521
|
+
class LessTemplate < Template
|
522
|
+
def initialize_engine
|
523
|
+
return if defined? ::Less::Engine
|
524
|
+
require_template_library 'less'
|
525
|
+
end
|
526
|
+
|
527
|
+
def prepare
|
528
|
+
@engine = ::Less::Engine.new(data)
|
529
|
+
end
|
530
|
+
|
531
|
+
def evaluate(scope, locals, &block)
|
532
|
+
@engine.to_css
|
533
|
+
end
|
534
|
+
end
|
535
|
+
register 'less', LessTemplate
|
536
|
+
|
537
|
+
|
328
538
|
# Builder template implementation. See:
|
329
539
|
# http://builder.rubyforge.org/
|
330
540
|
class BuilderTemplate < Template
|
331
541
|
def initialize_engine
|
332
|
-
|
542
|
+
return if defined?(::Builder)
|
543
|
+
require_template_library 'builder'
|
333
544
|
end
|
334
545
|
|
335
|
-
def
|
546
|
+
def prepare
|
336
547
|
end
|
337
548
|
|
338
549
|
def evaluate(scope, locals, &block)
|
@@ -346,7 +557,7 @@ module Tilt
|
|
346
557
|
xml.target!
|
347
558
|
end
|
348
559
|
|
349
|
-
def
|
560
|
+
def precompiled_template(locals)
|
350
561
|
data.to_str
|
351
562
|
end
|
352
563
|
end
|
@@ -368,10 +579,11 @@ module Tilt
|
|
368
579
|
# time when using this template engine.
|
369
580
|
class LiquidTemplate < Template
|
370
581
|
def initialize_engine
|
371
|
-
|
582
|
+
return if defined? ::Liquid::Template
|
583
|
+
require_template_library 'liquid'
|
372
584
|
end
|
373
585
|
|
374
|
-
def
|
586
|
+
def prepare
|
375
587
|
@engine = ::Liquid::Template.parse(data)
|
376
588
|
end
|
377
589
|
|
@@ -381,9 +593,8 @@ module Tilt
|
|
381
593
|
scope = scope.to_h.inject({}){ |h,(k,v)| h[k.to_s] = v ; h }
|
382
594
|
locals = scope.merge(locals)
|
383
595
|
end
|
384
|
-
# TODO: Is it possible to lazy yield ?
|
385
596
|
locals['yield'] = block.nil? ? '' : yield
|
386
|
-
locals['content'] =
|
597
|
+
locals['content'] = locals['yield']
|
387
598
|
@engine.render(locals)
|
388
599
|
end
|
389
600
|
end
|
@@ -402,15 +613,17 @@ module Tilt
|
|
402
613
|
end
|
403
614
|
|
404
615
|
def initialize_engine
|
405
|
-
|
616
|
+
return if defined? ::RDiscount
|
617
|
+
require_template_library 'rdiscount'
|
406
618
|
end
|
407
619
|
|
408
|
-
def
|
620
|
+
def prepare
|
409
621
|
@engine = RDiscount.new(data, *flags)
|
622
|
+
@output = nil
|
410
623
|
end
|
411
624
|
|
412
625
|
def evaluate(scope, locals, &block)
|
413
|
-
@engine.to_html
|
626
|
+
@output ||= @engine.to_html
|
414
627
|
end
|
415
628
|
end
|
416
629
|
register 'markdown', RDiscountTemplate
|
@@ -418,22 +631,24 @@ module Tilt
|
|
418
631
|
register 'md', RDiscountTemplate
|
419
632
|
|
420
633
|
|
421
|
-
# RedCloth implementation. See:
|
422
|
-
# http://redcloth.org/
|
423
|
-
class RedClothTemplate < Template
|
424
|
-
|
425
|
-
|
426
|
-
|
634
|
+
# RedCloth implementation. See:
|
635
|
+
# http://redcloth.org/
|
636
|
+
class RedClothTemplate < Template
|
637
|
+
def initialize_engine
|
638
|
+
return if defined? ::RedCloth
|
639
|
+
require_template_library 'redcloth'
|
640
|
+
end
|
427
641
|
|
428
|
-
|
429
|
-
|
430
|
-
|
642
|
+
def prepare
|
643
|
+
@engine = RedCloth.new(data)
|
644
|
+
@output = nil
|
645
|
+
end
|
431
646
|
|
432
|
-
|
433
|
-
|
647
|
+
def evaluate(scope, locals, &block)
|
648
|
+
@output ||= @engine.to_html
|
649
|
+
end
|
434
650
|
end
|
435
|
-
|
436
|
-
register 'textile', RedClothTemplate
|
651
|
+
register 'textile', RedClothTemplate
|
437
652
|
|
438
653
|
|
439
654
|
# Mustache is written and maintained by Chris Wanstrath. See:
|
@@ -446,14 +661,16 @@ register 'textile', RedClothTemplate
|
|
446
661
|
attr_reader :engine
|
447
662
|
|
448
663
|
def initialize_engine
|
449
|
-
|
664
|
+
return if defined? ::Mustache
|
665
|
+
require_template_library 'mustache'
|
450
666
|
end
|
451
667
|
|
452
|
-
def
|
668
|
+
def prepare
|
453
669
|
Mustache.view_namespace = options[:namespace]
|
670
|
+
Mustache.view_path = options[:view_path] || options[:mustaches]
|
454
671
|
@engine = options[:view] || Mustache.view_class(name)
|
455
672
|
options.each do |key, value|
|
456
|
-
next if %w[view namespace mustaches].include?(key.to_s)
|
673
|
+
next if %w[view view_path namespace mustaches].include?(key.to_s)
|
457
674
|
@engine.send("#{key}=", value) if @engine.respond_to? "#{key}="
|
458
675
|
end
|
459
676
|
end
|
@@ -482,6 +699,7 @@ register 'textile', RedClothTemplate
|
|
482
699
|
end
|
483
700
|
register 'mustache', MustacheTemplate
|
484
701
|
|
702
|
+
|
485
703
|
# RDoc template. See:
|
486
704
|
# http://rdoc.rubyforge.org/
|
487
705
|
#
|
@@ -490,20 +708,39 @@ register 'textile', RedClothTemplate
|
|
490
708
|
# engine.
|
491
709
|
class RDocTemplate < Template
|
492
710
|
def initialize_engine
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
end
|
711
|
+
return if defined?(::RDoc::Markup)
|
712
|
+
require_template_library 'rdoc/markup'
|
713
|
+
require_template_library 'rdoc/markup/to_html'
|
497
714
|
end
|
498
715
|
|
499
|
-
def
|
716
|
+
def prepare
|
500
717
|
markup = RDoc::Markup::ToHtml.new
|
501
718
|
@engine = markup.convert(data)
|
719
|
+
@output = nil
|
502
720
|
end
|
503
721
|
|
504
722
|
def evaluate(scope, locals, &block)
|
505
|
-
@engine.to_s
|
723
|
+
@output ||= @engine.to_s
|
506
724
|
end
|
507
725
|
end
|
508
726
|
register 'rdoc', RDocTemplate
|
727
|
+
|
728
|
+
|
729
|
+
# CoffeeScript info:
|
730
|
+
# http://jashkenas.github.com/coffee-script/
|
731
|
+
class CoffeeTemplate < Template
|
732
|
+
def initialize_engine
|
733
|
+
return if defined? ::CoffeeScript
|
734
|
+
require_template_library 'coffee-script'
|
735
|
+
end
|
736
|
+
|
737
|
+
def prepare
|
738
|
+
@output = nil
|
739
|
+
end
|
740
|
+
|
741
|
+
def evaluate(scope, locals, &block)
|
742
|
+
@output ||= ::CoffeeScript::compile(data, options)
|
743
|
+
end
|
744
|
+
end
|
745
|
+
register 'coffee', CoffeeTemplate
|
509
746
|
end
|