Class: Iodine::Mustache
- Inherits:
-
Data
- Object
- Data
- Iodine::Mustache
- Defined in:
- lib/iodine/mustache.rb,
ext/iodine/iodine_mustache.c
Overview
Iodine includes a strict and extra safe Mustache templating engine.
The Iodine Mustache templating engine provides increased XSS protection through agressive HTML escaping. It’s also faster than the original (Ruby based) Mustache templating engine.
Another difference is that the Iodine Mustache templating engine always loads the templates from the disk, allowing for patial template path resolution.
There’s no monkey-patch for ‘mustache` Ruby gem since the API is incompatible.
You can benchmark the Iodine Mustache performance and decide if you wish to switch from the Ruby implementation.
require 'benchmark/ips'
require 'mustache'
require 'iodine'
def benchmark_mustache
# benchmark code was copied, in part, from:
# https://github.com/mustache/mustache/blob/master/benchmarks/render_collection_benchmark.rb
template = """
{{#products}}
<div class='product_brick'>
<div class='container'>
<div class='element'>
<img src='images/{{image}}' class='product_miniature' />
</div>
<div class='element description'>
<a href={{url}} class='product_name block bold'>
{{external_index}}
</a>
</div>
</div>
</div>
{{/products}}
"""
IO.write "test_template.mustache", template
data_1 = {
products: [ {
:external_index=>"This <product> should've been \"properly\" escaped.",
:url=>"/products/7",
:image=>"products/product.jpg"
} ]
}
data_10 = {
products: []
}
10.times do
data_10[:products] << {
:external_index=>"product",
:url=>"/products/7",
:image=>"products/product.jpg"
}
end
data_100 = {
products: []
}
100.times do
data_100[:products] << {
:external_index=>"product",
:url=>"/products/7",
:image=>"products/product.jpg"
}
end
data_1000 = {
products: []
}
1000.times do
data_1000[:products] << {
:external_index=>"product",
:url=>"/products/7",
:image=>"products/product.jpg"
}
end
data_1000_escaped = {
products: []
}
1000.times do
data_1000_escaped[:products] << {
:external_index=>"This <product> should've been \"properly\" escaped.",
:url=>"/products/7",
:image=>"products/product.jpg"
}
end
view = Mustache.new
view.template = template
view.render # Call render once so the template will be compiled
iodine_view = Iodine::Mustache.new("test_template")
puts "Ruby Mustache rendering (and HTML escaping) results in:",
view.render(data_1), "",
"Notice that Iodine::Mustache rendering (and HTML escaping) results in agressive escaping:",
iodine_view.render(data_1), "", "----"
# return;
Benchmark.ips do |x|
x.report("Ruby Mustache render list of 10") do |times|
view.render(data_10)
end
x.report("Iodine::Mustache render list of 10") do |times|
iodine_view.render(data_10)
end
x.report("Ruby Mustache render list of 100") do |times|
view.render(data_100)
end
x.report("Iodine::Mustache render list of 100") do |times|
iodine_view.render(data_100)
end
x.report("Ruby Mustache render list of 1000") do |times|
view.render(data_1000)
end
x.report("Iodine::Mustache render list of 1000") do |times|
iodine_view.render(data_1000)
end
x.report("Ruby Mustache render list of 1000 with escaped data") do |times|
view.render(data_1000_escaped)
end
x.report("Iodine::Mustache render list of 1000 with escaped data") do |times|
iodine_view.render(data_1000_escaped)
end
end
end
benchmark_mustache
Instance Method Summary collapse
-
#initialize(filename) ⇒ Object
constructor
Loads a mustache template (and any partials).
-
#render(data) ⇒ Object
Renders the mustache template using the data provided in the ‘data` argument.
Constructor Details
#initialize(filename) ⇒ Object
Loads a mustache template (and any partials).
Once a template was loaded, it could be rendered using #render.
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 |
# File 'ext/iodine/iodine_mustache.c', line 287
static VALUE iodine_mustache_new(VALUE self, VALUE filename) {
mustache_s **m = NULL;
TypedData_Get_Struct(self, mustache_s *, &iodine_mustache_data_type, m);
if (!m) {
rb_raise(rb_eRuntimeError, "Iodine::Mustache allocation error.");
}
Check_Type(filename, T_STRING);
mustache_error_en err;
*m = mustache_load(.filename = RSTRING_PTR(filename),
.filename_len = RSTRING_LEN(filename), .err = &err);
if (!*m)
goto error;
return self;
error:
switch (err) {
case MUSTACHE_OK:
rb_raise(rb_eRuntimeError, "Iodine::Mustache template ok, unknown error.");
break;
case MUSTACHE_ERR_TOO_DEEP:
rb_raise(rb_eRuntimeError, "Iodine::Mustache element nesting too deep.");
break;
case MUSTACHE_ERR_CLOSURE_MISMATCH:
rb_raise(rb_eRuntimeError,
"Iodine::Mustache template error, closure mismatch.");
break;
case MUSTACHE_ERR_FILE_NOT_FOUND:
rb_raise(rb_eRuntimeError, "Iodine::Mustache template not found.");
break;
case MUSTACHE_ERR_FILE_TOO_BIG:
rb_raise(rb_eRuntimeError, "Iodine::Mustache template too big.");
break;
case MUSTACHE_ERR_FILE_NAME_TOO_LONG:
rb_raise(rb_eRuntimeError, "Iodine::Mustache template name too long.");
break;
case MUSTACHE_ERR_EMPTY_TEMPLATE:
rb_raise(rb_eRuntimeError, "Iodine::Mustache template is empty.");
break;
case MUSTACHE_ERR_UNKNOWN:
rb_raise(rb_eRuntimeError, "Iodine::Mustache unknown error.");
break;
case MUSTACHE_ERR_USER_ERROR:
rb_raise(rb_eRuntimeError, "Iodine::Mustache internal error.");
break;
}
return self;
}
|
Instance Method Details
#render(data) ⇒ Object
Renders the mustache template using the data provided in the ‘data` argument.
Returns a String.
Raises an exception on error.
NOTE:
As one might notice, no binding is provided. Instead, a ‘data` Hash is assumed. Iodine will search the Hash for any data while protecting against code execution.
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 |
# File 'ext/iodine/iodine_mustache.c', line 351
static VALUE iodine_mustache_render(VALUE self, VALUE data) {
fio_str_s str = FIO_STR_INIT;
mustache_s **m = NULL;
TypedData_Get_Struct(self, mustache_s *, &iodine_mustache_data_type, m);
if (!m) {
rb_raise(rb_eRuntimeError, "Iodine::Mustache allocation error.");
}
if (mustache_build(*m, .udata1 = &str, .udata2 = (void *)data))
goto error;
fio_str_info_s i = fio_str_info(&str);
VALUE ret = rb_str_new(i.data, i.len);
fio_str_free(&str);
return ret;
error:
fio_str_free(&str);
rb_raise(rb_eRuntimeError, "Couldn't build template frome data.");
}
|