Tried. Doesn't really work, since it seems Sinatra::Base instance, so
@template_cache, is re-created upon each request (at least at my
machine Rack+Thin).
> - hook deeper into the render logic so that you can do a File.stat()
> on the template file, and check for changes. This check is cheap (the
> inode will remain in RAM for a frequently-accessed template) but
> avoids the problem of stale templates in both development and
> production mode.
This make sense, however Sinatra doesn't reload its files when
modified on production, so I don't see why it should reload the
templates.
While on development it does reload all classes on each request.
> Perhaps render_foo should become compile_foo, which returns a closure
> that performs the actual work.
I think the performance problem also related to fast that haml =
Haml.new(template_src) after it parses Haml template does produce
internally haml.@precompiled which is not-yet evaluated Ruby src code.
And then on every haml.render(bindings) it does call
eval(@precompiled) which does Ruby compilation all over again.
It would be better if Haml.new(template_src) produced Proc instance,
but I don't have a clue how to run Proc with specified bindings, same
as it is done with eval.
Regards,
--
Adam
Okay, I got you point, here's fixed source:
# Sinatra 0.9.x Haml template cache module
#
# Caches Haml templates in self.class.template_cache class variable,
avoiding recompilation of the
# template on every page reload.
class Sinatra::Base
class <<self
attr_accessor :template_cache
end
# Overriden function responsible for calling internal Haml
rendering routines
def haml(template, options={})
self.class.template_cache ||= {}
return super(template, options) unless
self.class.template_cache.has_key?(template)
output = self.class.template_cache[template].render(self,
options[:locals] || {})
if self.class.template_cache.has_key?(:layout) &&
options[:layout] != false
self.class.template_cache[:layout].render(self,
options[:locals] || {}) { output }
else
output
end
end
# Overriden internal function responsible for template
precompilation and rendering
def render_haml(template, data, options, &block)
self.class.template_cache[template] ||= ::Haml::Engine.new(data,
options[:options] || {})
self.class.template_cache[template].render(self, options[:locals]
|| {}, &block)
end
# Clears the cache
def empty_cache!
self.class.template_cache = {}
end
end
Regards,
--
Adam
You may read about my benchmarks with this caching at: http://groups.google.com/group/haml/msg/92af7904b69042cd
(Actually I've replaced render_proc into def_method below, since it is
even slightly faster)
# Sinatra 0.9.x Haml template cache module
#
# Caches Haml templates in self.class.template_cache class variable,
avoiding reparsing of the
# template on every page reload.
# Caches Haml templates in "render_haml_#{template}" for running
instance, avoiding Ruby's code
# recompilation in case of loops, and nested "= haml :template" calls.
class Sinatra::Base
class <<self
attr_accessor :template_cache
end
# Overriden function responsible for calling internal Haml
rendering routines
def haml(template, options={})
self.class.template_cache ||= {}
return super(template, options) unless
self.class.template_cache.has_key?(template)
# define local method that renders the template, this improves
performance since the code is compiled once
method = "render_haml_#{template}".to_sym
self.class.template_cache[template].def_method(self, method,
*((options[:locals] || {}).keys)) unless self.respond_to?(method)
output = self.send(method, options[:locals] || {})
if self.class.template_cache.has_key?(:layout) &&
options[:layout] != false
# :layout is usually called once per instance so we won't have
much performnace improvement
# caching method here
self.class.template_cache[:layout].render(self,
options[:locals] || {}) { output }
else
output
end
end
# Overriden internal function responsible for template
precompilation and rendering
def render_haml(template, data, options, &block)
self.class.template_cache[template] ||= ::Haml::Engine.new(data,
options[:options] || {})
# define local method that renders the template, this improves
performance since the code is compiled once
method = "render_haml_#{template}".to_sym
self.class.template_cache[template].def_method(self, method,
*((options[:locals] || {}).keys))
self.send(method, options[:locals] || {}, &block)
end
# Clears the cache
def empty_cache!
Since we got brand new 0.9.2 I decided to rework my Haml cache to be
even faster & simpler.
Actually I found out we can use Haml's def_method for our Application
class rather than just the current instance, so parsed and compiled
methods via `Haml::Engine::def_method` stay for Application's life-time.
This brings even more speed boost comparing to the code I've posted
last month, as each template is now touched by Haml engine just once,
and adds `render_haml_templatename` method that is Ruby compiled code,
that's called later on upon each template call.
Here's the code:
-------------------- CUT --------------------
# Sinatra >= 0.9.2 Haml template cache module
# Caches Haml templates in class instance "render_haml_#{template}"
methods,
# avoiding reparsing and recompiling of the template on every page
reload.
class Sinatra::Base
# Override function responsible for calling internal Haml rendering
routines
def haml(template, options={}, locals={})
method = "render_haml_#{template}".to_sym
return super(template, options, locals) unless self.respond_to?
method
locals = options.delete(:locals) || locals || {}
if options[:layout] != false
__send__(:render_haml_layout, locals) { __send__(method,
locals) }
else
__send__(method, locals)
end
end
def render_haml(template, data, options, locals, &block)
method = "render_haml_#{template}".to_sym
::Haml::Engine.new(data, options).def_method(self.class, method,
*(locals.keys))
__send__(method, locals, &block)
end
def self.clear_cache!
instance_methods.grep(/^render_haml_/).each{|m| remove_method m}
end
def clear_cache!; self.class.clear_cache! end
end
-------------------- CUT --------------------
You may add cache clear or reload based on Rack::Reloader.
I will try to post my code regarding this reloaded as soon its finished.
Have fun with my cache in your production environment :)
Waiting for your comments and suggestions...
Cheers,
--
Adam