PERF: use raw rendering for topic list

rename my handlebars helper to "raw",
create ember compat handlebars to handle rendering
patch ember rails to use the correct precompiler
This commit is contained in:
Sam 2014-10-29 15:24:51 +11:00
parent e3b88d3688
commit 5729e7df6f
11 changed files with 153 additions and 14 deletions

View file

@ -1,4 +1,4 @@
Handlebars.registerHelper('handlebars', function(property, options) {
Handlebars.registerHelper('raw', function(property, options) {
var template = Em.TEMPLATES[property + ".raw"];
var params = options.hash;
@ -13,7 +13,3 @@ Handlebars.registerHelper('handlebars', function(property, options) {
return new Handlebars.SafeString(template(params));
});
Handlebars.registerHelper('get', function(property) {
return Em.get(this, property);
});

View file

@ -0,0 +1,105 @@
// keep IIF for simpler testing
// EmberCompatHandlebars is a mechanism for quickly rendering templates which is Ember aware
// templates are highly compatible with Ember so you don't need to worry about calling "get"
// and computed properties function, additionally it uses stringParams like Ember does
(function(){
// compat with ie8 in case this gets picked up elsewhere
var objectCreate = Object.create || function(parent) {
function F() {}
F.prototype = parent;
return new F();
};
var RawHandlebars = objectCreate(Handlebars);
RawHandlebars.helper = function() {};
RawHandlebars.helpers = objectCreate(Handlebars.helpers);
RawHandlebars.helpers.get = function(context, options){
var firstContext = options.contexts[0];
var val = firstContext[context];
val = val === undefined ? Em.get(firstContext, context): val;
return val;
};
// adds compatability so this works with stringParams
var stringCompatHelper = function(fn){
var old = RawHandlebars.helpers[fn];
RawHandlebars.helpers[fn] = function(context,options){
return old.apply(this, [
RawHandlebars.helpers.get(context,options),
options
]);
};
};
stringCompatHelper("each");
stringCompatHelper("if");
stringCompatHelper("unless");
stringCompatHelper("with");
RawHandlebars.Compiler = function() {};
RawHandlebars.Compiler.prototype = objectCreate(Handlebars.Compiler.prototype);
RawHandlebars.Compiler.prototype.compiler = RawHandlebars.Compiler;
RawHandlebars.JavaScriptCompiler = function() {};
RawHandlebars.JavaScriptCompiler.prototype = objectCreate(Handlebars.JavaScriptCompiler.prototype);
RawHandlebars.JavaScriptCompiler.prototype.compiler = RawHandlebars.JavaScriptCompiler;
RawHandlebars.JavaScriptCompiler.prototype.namespace = "Discourse.EmberCompatHandlebars";
RawHandlebars.Compiler.prototype.mustache = function(mustache) {
if ( !(mustache.params.length || mustache.hash)) {
var id = new Handlebars.AST.IdNode([{ part: 'get' }]);
if (!mustache.escaped) {
mustache.hash = mustache.hash || new Handlebars.AST.HashNode([]);
mustache.hash.pairs.push(["unescaped", new Handlebars.AST.StringNode("true")]);
}
mustache = new Handlebars.AST.MustacheNode([id].concat([mustache.id]), mustache.hash, !mustache.escaped);
}
return Handlebars.Compiler.prototype.mustache.call(this, mustache);
};
RawHandlebars.precompile = function(value, asObject) {
var ast = Handlebars.parse(value);
var options = {
knownHelpers: {
get: true
},
data: true,
stringParams: true
};
asObject = asObject === undefined ? true : asObject;
var environment = new RawHandlebars.Compiler().compile(ast, options);
return new RawHandlebars.JavaScriptCompiler().compile(environment, options, undefined, asObject);
};
RawHandlebars.compile = function(string) {
var ast = Handlebars.parse(string);
// this forces us to rewrite helpers
var options = { data: true, stringParams: true };
var environment = new RawHandlebars.Compiler().compile(ast, options);
var templateSpec = new RawHandlebars.JavaScriptCompiler().compile(environment, options, undefined, true);
var template = RawHandlebars.template(templateSpec);
return template;
};
Discourse.EmberCompatHandlebars = RawHandlebars;
})();

View file

@ -48,7 +48,7 @@
{{number topic.views numberKey="views_long"}}
</td>
{{handlebars "list/activity_column" topic=topic class="num" tagName="td"}}
{{raw "list/activity_column" topic=topic class="num" tagName="td"}}
</tr>
{{/grouped-each}}
</tbody>

View file

@ -1 +1 @@
<{{this.tagName}} class="{{cold-age-class "topic.createdAt" startDate="topic.bumpedAt" class=this.class}} activity" title="{{get "topic.bumpedAtTitle"}}"><a href="{{get "topic.lastPostUrl"}}">{{format-date "topic.bumpedAt" format="tiny" noTitle=true}}</a></{{this.tagName}}>
<{{this.tagName}} class="{{class}} {{cold-age-class topic.createdAt startDate=topic.bumpedAt class=""}} activity" title="{{topic.bumpedAtTitle}}"><a href="{{topic.lastPostUrl}}">{{format-date topic.bumpedAt format="tiny" noTitle=true}}</a></{{this.tagName}}>

View file

@ -1,3 +1,3 @@
{{#unless hideCategory}}
<td class='category'>{{category-link "category" showParent=true}}</td>
<td class='category'>{{category-link category showParent=true}}</td>
{{/unless}}

View file

@ -1,5 +1,5 @@
<td class='posters'>
{{#each posters}}
<a href="{{get "user.path"}}" data-user-card="{{this.user.username}}" class="{{get "extras"}}">{{avatar this usernamePath="user.username" imageSize="small"}}</a>
<a href="{{user.path}}" data-user-card="{{user.username}}" class="{{extras}}">{{avatar this usernamePath="user.username" imageSize="small"}}</a>
{{/each}}
</td>

View file

@ -27,10 +27,10 @@
{{/if}}
</td>
{{handlebars "list/category_column" hideCategory=hideCategory category=category}}
{{handlebars "list/posters_column" posters=posters}}
{{raw "list/category_column" hideCategory=hideCategory category=category}}
{{raw "list/posters_column" posters=posters}}
{{posts-count-column topic=model class="num" action="showTopicEntrance"}}
<td class="num views {{unbound viewsHeat}}">{{number views numberKey="views_long"}}</td>
{{handlebars "list/activity_column" topic=model class="num" tagName="td"}}
{{raw "list/activity_column" topic=model class="num" tagName="td"}}

View file

@ -27,7 +27,7 @@
<div class="topic-item-stats clearfix">
<div class="pull-right">
{{posts-count-column topic=topic tagName="div" class="num posts" action="clickedPosts"}}
{{handlebars "list/activity_column" topic=topic tagName="div" class="num activity last"}}
{{raw "list/activity_column" topic=topic tagName="div" class="num activity last"}}
</div>
{{#unless controller.hideCategory}}
<div class='category'>

View file

@ -31,7 +31,7 @@
{{posts-count-column topic=this tagName="div" class="num posts" action="showTopicEntrance"}}
<div class='num activity last'>
<a href="{{lastPostUrl}}" title='{{i18n last_post}}: {{{raw-date bumped_at}}}'>{{last_poster_username}}</a>
{{handlebars "list/activity_column" topic=this tagName="span" class="age"}}
{{raw "list/activity_column" topic=this tagName="span" class="age"}}
</div>
</div>
<div class="clearfix"></div>

View file

@ -8,8 +8,10 @@
// This is a BUG we should fix
// it is only required here cause preview is not loading it using LAB
//= require highlight.pack.js
//
// Stuff we need to load first
//= require ./discourse/lib/ember_compat_handlebars
//= require ./discourse/lib/computed
//= require ./discourse/mixins/scrolling
//= require_tree ./discourse/mixins

View file

@ -0,0 +1,36 @@
# barber patches to re-route raw compilation via ember compat handlebars
#
module Barber
class EmberCompatPrecompiler < Barber::Precompiler
def self.call(template)
"Handlebars.template(#{compile(template)})"
end
def sources
[handlebars, precompiler]
end
def precompiler
@precompiler ||= StringIO.new <<END
var Discourse = {};
#{File.read(Rails.root + "app/assets/javascripts/discourse/lib/ember_compat_handlebars.js")}
var Barber = {
precompile: function(string) {
return Discourse.EmberCompatHandlebars.precompile(string).toString();
}
};
END
end
end
end
class Ember::Handlebars::Template
def precompile_handlebars(string)
"Discourse.EmberCompatHandlebars.template(#{Barber::EmberCompatPrecompiler.compile(string)});"
end
def compile_handlebars(string)
"Discourse.EmberCompatHandlebars.compile(#{indent(string).inspect});"
end
end