I two Haml templates that share the same chunk of code.
published.haml
--
%h1 Published
- links.each do |link|
.link
%h3
%a{ :href => bare ? link.permalink : link.url }= link.title
.actions
%a{ :href => link.permalink+'/'+_('edit') } Edit
%a{ :href => link.permalink+'/'+_('delete'), :onclick =>
"javascript:return confirm('"+_('Are you sure you want to delete this
item?')+"');" } Delete
%p= link.description
link.haml
--
.link
%h3
%a{ :href => bare ? link.permalink : link.url }= link.title
.actions
%a{ :href => link.permalink+'/'+_('edit') } Edit
%a{ :href => link.permalink+'/'+_('delete'), :onclick =>
"javascript:return confirm('"+_('Are you sure you want to delete this
item?')+"');" } Delete
%p= link.description
- comments.each do |comment|
.comment= comment.content
Is there any way to turn them into?? :
published.haml
--
%h1 Published
- links.each do |link|
+ link_body
link.haml
--
+ link_body
- comments.each do |comment|
.comment= comment.content
link_body.haml
--
.link
%h3
%a{ :href => bare ? link.permalink : link.url }= link.title
.actions
%a{ :href => link.permalink+'/'+_('edit') } Edit
%a{ :href => link.permalink+'/'+_('delete'), :onclick =>
"javascript:return confirm('"+_('Are you sure you want to delete this
item?')+"');" } Delete
%p= link.description
With haml and keep DRY within templates?
I know I can call in Hamle template with Sinatra using:
- haml_concat haml :link_body, :layout => false, :locals => {:link =>
link}
But this is pretty, and slow too.
Would be cool then if Haml::Engine#new took some :lookup option
parameter that is a symbol of scope object method that returns Haml
source for specified name:
class MyScopeObject
def lookup(name)
# some code goes here ...
template_src
end
end
So + action call can be customized by framework embedding Haml such as
Sinatra that would be:
class Sinatra::Base
def lookup(name)
lookup_template(name, :haml)
end
end
Regards,
--
Adam
Maybe you can reconsider my proposition, where you specify symbol of
the method of scoped object that takes String name and returns Haml
source.
So:
+ included_template_name
or whatever in place "+" would use a callback defined outside of Haml
that returns the Haml source for specified name.
> In addition, the functionality simply isn't
> useful in light of the possibility of defining this in Ruby code.
Yes, it is possible to do some kind of inclusion now. We could have a
call in Sinatra:
- haml_concat haml :link_body, :layout => false, :locals => {:link =>
link}
This is "includes" link_body into the output. But there's a serious
problem of performance.
When this haml_concat haml call is rendered in loop of 20 iterations
rendering takes ~4x time than if I just have put the link_body content
in place of the - haml_concat ....
And of source it is far less readable and clear than:
+ link_body
Regards,
--
Adam
Good question ;) Well it works now fine with =..., hmm I don't
remember what was wrong there.
> Second, if the partial-rendering call
> takes too much time, that suggests that there's a performance problem
> in Sinatra's Haml rendering code, like that I mentioned at
> http://nex-3.com/posts/81-more-haml-benchmark-issues#comment_561 .
Seems this is Sinatra's issue then. I see one potential problem then
with Sinatra against running it through
render_proc, def_method -> scope object instance does change on every
request. So I cannot really cache
render_proc, def_method... but I'll try to cache it during one
request. So may subcalls to = haml :template should be faster.
Thanks for your suggestions,
--
Regards
index.haml
---
%h1 Published
- links.each do |link|
= haml :link, :layout => false, :locals => {:link => link}
---
which calls 10 items via "= haml :link" (links.each has 10 iterations)
link.haml
---
.link
%h3
%a{ :href => link.url }= link.title
.actions
%a{ :href => link.permalink+'/edit' } Edit
%a{ :href => link.permalink+'/delete', :onclick =>
"javascript:return confirm('Are you sure you want to delete this
item?');" } Delete
%p= link.description
---
Complete requests: 500
(1) With caching engine = Haml::Engine#new(src ...) across whole
application, caching proc = engine.to_proc ... during request, and
calling proc.call
Time per request: 7.314 [ms] (mean) (RB 1.9 = 7.498)
(2) With caching engine = Haml::Engine#new(src ...) across whole
application, and calling engine.render
Time per request: 8.575 [ms] (mean) (RB 1.9 = 9.622)
(3) Without caching at all (bare Sinatra)
Time per request: 24.377 [ms] (mean) (RB 1.9 = 31.148)
Having "= haml :link" replaced with actual content of link.haml
(4) Same caching (1) but this time we don't call "= haml :link"
Time per request: 6.228 [ms] (mean) (RB 1.9 = 6.560)
(5) No caching at all (bare Sinatra) ...
Time per request: 11.019 [ms] (mean) (RB 1.9 = 12.558)
My conclusions are:
1. Sinatra w/o caching precompiled templates is a riot in
production 2x-5x slower (1)-(3), (4)-(5)!
2. using engine.render or proc.call (cached from engine.to_proc)
brings us ~20% performance boost (1)-(2) (Ruby's parser, compiler is
really fast then too)
3. theoretically using template inclusion could give us another
~20% boost with caching (1)-(4), or 2x boost with bare sinatra (which
should use caching anyway) (3)-(5)
4. Ruby 1.9 is slower here! probably due slower compiler, string
manipulation and not really using virtues of YARV here
Final question is if this 20% (1 ms) is worth this template inclusion?
Regards,
--
Adam
After thinking twice I found out that the `Haml::Engine.def_method`
can be called with `self.class` rather than `self`.
So the we got application class instance method that stays for life-
time for every request.
Thanks to your suggestions I think I got perfect Sinatra Haml cache,
here's the original msg from Sinatra group:
http://groups.google.com/group/sinatrarb/msg/2c5896c9430f2439
The trick is just the method defined via `def_method` in our
Application class is the "cache", which is actual Ruby bytecode. No
extra class variables, hashes or whatever is needed (as in my old
code). And it works fast & great!
Reading your blog... I'm looking forward 2.2 release so all together
we gonna run faster than ERB with :ugly and caching on production :)
Regards,
--
Adam | nanoant.com