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
filename = "test_template.mustache"
data_1 = {
products: [ {
:external_index=>"This <product> should've been \"properly\" escaped.",
:url=>"/products/7",
:image=>"products/product.jpg"
} ]
}
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(filename)
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 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
x.report("Ruby Mustache - no chaching - render list of 1000") do |times|
tmp = Mustache.new
tmp.template = template
tmp.render(data_1000)
end
x.report("Iodine::Mustache - no chaching - render list of 1000") do |times|
Iodine::Mustache.render(nil, data_1000, template)
end
end
end
benchmark_mustache
Class Method Summary collapse
-
.render(*args) ⇒ Object
Renders the mustache template found in ‘filename`, using the data provided in the `data` argument.
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.
281 282 283 284 285 286 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 |
# File 'ext/iodine/iodine_mustache.c', line 281
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;
}
|
Class Method Details
.render(*args) ⇒ Object
Renders the mustache template found in ‘filename`, using the data provided in the `data` argument. If `template` is provided it will be used instead of reading the file’s content.
Iodine::Mustache.render(filename, data, template = nil)
Returns a String with the rendered template.
Raises an exception on error.
template = "<h1>{{title}}</h1>"
filename = "templates/index"
data = {title: "Home"}
result = Iodine::Mustache.render(filename, data)
# filename will be used to resolve the path to any partials:
result = Iodine::Mustache.render(filename, data, template)
# OR, if we don't need partial template path resolution
result = Iodine::Mustache.render(template: template, data: data)
NOTE 1:
This function doesn’t cache the template data.
The more complext the template the higher the cost of the template parsing stage.
Consider creating a persistent template object using a new object and using the instance #render method.
NOTE 2:
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.
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 |
# File 'ext/iodine/iodine_mustache.c', line 402
static VALUE iodine_mustache_render_klass(int argc, VALUE *argv, VALUE self) {
VALUE filename = Qnil, data = Qnil, template = Qnil;
if (argc == 1) {
/* named arguments */
Check_Type(argv[0], T_HASH);
filename = rb_hash_aref(argv[0], filename_id);
data = rb_hash_aref(argv[0], data_id);
template = rb_hash_aref(argv[0], template_id);
} else {
/* regular arguments */
if (argc < 2 || argc > 3)
rb_raise(rb_eArgError, "expecting 2..3 arguments or named arguments.");
filename = argv[0];
data = argv[1];
if (argc > 2) {
template = argv[2];
}
}
if (filename == Qnil && template == Qnil)
rb_raise(rb_eArgError, "missing both template contents and file name.");
if (template != Qnil)
Check_Type(template, T_STRING);
if (filename != Qnil)
Check_Type(filename, T_STRING);
fio_str_s str = FIO_STR_INIT;
mustache_s *m = NULL;
mustache_error_en err;
m = mustache_load(.filename =
(filename == Qnil ? NULL : RSTRING_PTR(filename)),
.filename_len =
(filename == Qnil ? 0 : RSTRING_LEN(filename)),
.data = (template == Qnil ? NULL : RSTRING_PTR(template)),
.data_len = (template == Qnil ? 0 : RSTRING_LEN(template)),
.err = &err);
if (!m)
goto error;
int e = mustache_build(m, .udata1 = &str, .udata2 = (void *)data);
mustache_free(m);
if (e)
goto render_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:
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 Qnil;
render_error:
fio_str_free(&str);
rb_raise(rb_eRuntimeError, "Couldn't build template frome data.");
}
|
Instance Method Details
#render(data) ⇒ Object
Renders the mustache template using the data provided in the ‘data` argument.
Returns a String with the rendered template.
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.
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 |
# File 'ext/iodine/iodine_mustache.c', line 345
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.");
}
|