GithubHelp home page GithubHelp logo

sunng87 / handlebars-rust Goto Github PK

View Code? Open in Web Editor NEW
1.2K 14.0 136.0 9.4 MB

Rust templating with Handlebars

License: MIT License

Rust 98.84% Shell 0.05% Handlebars 0.01% HTML 0.45% JavaScript 0.66%
rust handlebars-js template-engine handlebars

handlebars-rust's Introduction

handlebars-rust

Handlebars templating language implemented in Rust and for Rust.

CI Coverage Status MIT licensed Docs Donate

Getting Started

Quick Start

use handlebars::Handlebars;
use serde_json::json;
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    let mut reg = Handlebars::new();
    // render without register
    println!(
        "{}",
        reg.render_template("Hello {{name}}", &json!({"name": "foo"}))?
    );

    // register template using given name
    reg.register_template_string("tpl_1", "Good afternoon, {{name}}")?;
    println!("{}", reg.render("tpl_1", &json!({"name": "foo"}))?);

    Ok(())
}

Code Example

If you are not familiar with handlebars language syntax, it is recommended to walk through their introduction first.

Examples are provided in source tree to demo usage of various api.

  • quick the very basic example of registry and render apis
  • render how to define custom helpers with function, trait impl or macro, and also how to use custom helpers.
  • render_file similar to render, but render to file instead of string
  • helper_macro demos usage of handlebars_helper! to simplify helper development
  • partials template inheritance with handlebars
  • decorator how to use decorator to change data or define custom helper
  • script how to define custom helper with rhai scripting language, just like using javascript for handlebarsjs
  • error simple case for error
  • dev_mode a web server hosts handlebars in dev_mode, you can edit the template and see the change without restarting your server.

Web Playground

We have github action to compile latest master branch into WebAssembly and serve it on github pages. You can test and verify your template with both handlebars-rust and handlebarjs.

Minimum Rust Version Policy

Handlebars will track Rust nightly and stable channel. When dropping support for previous stable versions, I will bump patch version and clarify in CHANGELOG.

Docs

Rust doc.

Changelog

Changelog is available in the source tree named as CHANGELOG.md.

Contributor Guide

Any contribution to this library is welcomed. To get started into development, I have several Help Wanted issues, with the difficulty level labeled. When running into any problem, feel free to contact me on github.

I'm always looking for maintainers to work together on this library, let me know (via email or anywhere in the issue tracker) if you want to join.

Why (this) Handlebars?

Handlebars is a real-world templating system that you can use to build your application without pain.

Features

Isolation of Rust and HTML

This library doesn't attempt to use some macro magic to allow you to write your template within your rust code. I admit that it's fun to do that but it doesn't fit real-world use cases.

Limited but essential control structures built-in

Only essential control directives if and each are built-in. This prevents you from putting too much application logic into your template.

Extensible helper system

You can write your own helper with Rust! It can be a block helper or inline helper. Put your logic into the helper and don't repeat yourself.

A helper can be as a simple as a Rust function like:

handlebars_helper!(hex: |v: i64| format!("0x{:x}", v));

/// register the helper
handlebars.register_helper("hex", Box::new(hex));

And using it in your template:

{{hex 16}}

By default, handlebars-rust ships additional helpers (compared with original js version) that is useful when working with if.

With script_helper feature flag enabled, you can also create helpers using rhai script, just like JavaScript for handlebars-js. This feature was in early stage. Its API was limited at the moment, and can change in future.

Template inheritance

Every time I look into a templating system, I will investigate its support for template inheritance.

Template include is not sufficient for template reuse. In most cases you will need a skeleton of page as parent (header, footer, etc.), and embed your page into this parent.

You can find a real example of template inheritance in examples/partials.rs and templates used by this file.

Auto-reload in dev mode

By turning on dev_mode, handlebars auto reloads any template and scripts that loaded from files or directory. This can be handy for template development.

WebAssembly compatible

Handlebars 3.0 can be used in WebAssembly projects.

Fully scriptable

With rhai script support, you can implement your own helper with the scripting language. Together with the template lanaguage itself, template development can be fully scriptable without changing rust code.

Related Projects

Web frameworks

Adopters

The adopters page lists projects that uses handlebars for part of their functionalities.

Extensions

The extensions page has libraries that provide additional helpers, decorators and outputs to handlebars-rust, and you can use in your own projects.

License

This library (handlebars-rust) is open sourced under the MIT License.

handlebars-rust's People

Contributors

5225225 avatar alextalker avatar azerupi avatar blaenk avatar chaserhkj avatar darnuria avatar dependabot[bot] avatar eh2406 avatar fallen4eyes avatar fauxfaux avatar gabhijit avatar gamebox avatar joshleeb avatar keruspe avatar killercup avatar linw1995 avatar lovasoa avatar matthiasbeyer avatar mkantor avatar nickvollmar avatar progmboy avatar robinst avatar sirver avatar softprops avatar sunng87 avatar tailhook avatar thegedge avatar ubnt-intrepid avatar untitaker avatar yhakbar avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

handlebars-rust's Issues

Running code using handlebars with template > 2.4K gives me stackoverflow

Something strange is happening to me with rust, cargo and handlebars.

When i register a template with > 2.4k and use cargo run it FAILS with stackoverflow

When i register a template with < 2.4k and use cargo run it works OK.

When i register a template with > 2.4k and use cargo run --release it works perfectly

When i register a template with > 2.4k, mark a method as test and use cargo test it works perfectly

Can't post the template because of privacy, but believe me it works, as i am porting from a java app to rust.

Provide a way to load templates from files

I know there is a function in the examples to do exactly this but I'm certain most people using this library will want to load files from the filesystem at some point. It's probably useful to provide this functionality directly in the library for convenience.

Problem with partials and #each?

Minimal test case:

base.hbs

{{ #block "layout" }}
{{/block}}

index.hbs

{{#partial "layout" }}
{{> posts }}
{{/partial}}

{{> base }}

posts.hbs

{{ log this }}
{{#each posts }}
{{ title }}
{{ content }}
{{/each}}

The log prints my context as { "posts": { "title": "test", "content": "test" } }, but the rendering fails with DEBUG:handlebars::helpers::helper_each: each value Null. Am I doing something wrong? I'm using handlebars-iron if that helps.

Doesn't allow dynamic partials

Handlebars has a "dynamically named partials". It looks like {{> (partialTplName) }}

partialTplName is a context variable which hbs should interpolate and use proper partial.

I took handlebars-iron's example, extracted partial and passed it's name to the Template's context. If I use partial's filename directly or just printing partialTplName with {{partialTplName}} it looks like expected. If I trying to use DNP, template outputs zero characters without any errors.

Support array indexing

It would be nice if templates could support the array indexing syntax described here.

Also, can the {{lookup}} helper be used with integer literals as the indices? It seems like the answer is no, this would also be nice to have.

Hex_helper example in readme is broken + JsonRender is a private trait.

I think this are 2 bugs, one in the documentation and one in the library.

Compiling the hex_helper from the readme yields:

error: the trait `core::fmt::LowerHex` is not implemented for the type `collections::string::String` [E0277]
src/bin/server.rs:135     let rendered = format!("{:x}", c.navigate(rc.get_path(), param).render());

Messing around with the code finally yields

error: source trait is private
src/bin/server.rs:137     try!(rc.writer.write(rendered.into_bytes().as_ref()));

The trait referred to here is JsonRender. The same error can be get by copy & pasting https://github.com/sunng87/handlebars-rust/blob/master/examples/render.rs#L19, i.e. the format_helper into a binary that is not inside the handlebars-rs crate.

Pub use JSON crate

Could you maybe "pub use" the rustc-serialize / serde JSON library that you're using? Otherwise I have to add serde_json as an explicit dependency to my Cargo.toml which might not be the same version as the one used by handlebars.

Examples crash

I'm trying to run examples in the library and partials work fine, but render and render_file do crash with the following backtrace:

     Running `target/debug/examples/render`
thread '<main>' panicked at 'called `Option::unwrap()` on a `None` value', ../src/libcore/option.rs:325
stack backtrace:
   1:     0x558c84eac70f - std::sys::backtrace::tracing::imp::write::h3800f45f421043b8
   2:     0x558c84eaf40b - std::panicking::default_hook::_$u7b$$u7b$closure$u7d$$u7d$::h0ef6c8db532f55dc
   3:     0x558c84eaf093 - std::panicking::default_hook::hf3839060ccbb8764
   4:     0x558c84ea5d3d - std::panicking::rust_panic_with_hook::h5dd7da6bb3d06020
   5:     0x558c84eaf651 - std::panicking::begin_panic::h9bf160aee246b9f6
   6:     0x558c84ea6cba - std::panicking::begin_panic_fmt::haf08a9a70a097ee1
   7:     0x558c84eaf5ee - rust_begin_unwind
   8:     0x558c84ee517f - core::panicking::panic_fmt::h93df64e7370b5253
   9:     0x558c84ee5458 - core::panicking::panic::h9d5bd65bbb401959
  10:     0x558c84cc4982 - _<std..option..Option<T>>::unwrap::h1d2386177dc008f1
                        at ../src/libcore/macros.rs:21
  11:     0x558c84cc42e6 - render::rank_helper::hb4e197b8830cc302
                        at examples/render.rs:36
  12:     0x558c84cd0748 - fn(&handlebars..Context, &handlebars..Helper<'_>, &handlebars..Registry, &mut handlebars..RenderContext<'_>) .> std..
result..Result<(), handlebars..RenderError> $u7b$rank_helper$u7d$::fn_pointer_shim.12163::hff212c1b973e2bff
  13:     0x558c84cd071b - _<F as handlebars..HelperDef>::call::h34ade6fc4b16b78b
                        at src/helpers/mod.rs:36
  14:     0x558c84d4ecb3 - _<template..TemplateElement as render..Renderable>::render::h4d6ca29b82b9f52c
                        at src/render.rs:446
  15:     0x558c84d3519f - _<template..Template as render..Renderable>::render::hbda4631c4cc78527
                        at src/render.rs:394
  16:     0x558c84d0f180 - _<helpers..helper_each..EachHelper as helpers..HelperDef>::call::h69f04b59ea7c3464
                        at src/helpers/helper_each.rs:51

A problem with regex_macros

At runtime, I'm getting an error:

error while loading shared libraries: libregex_macros-[...].so

after adding a reference to this library. After asking about this on irc, burntsushi said

you need to use #[no_link] #[plugin] extern crate regex_macros;

He then added why:

the reason why you need all this special stuff is because regex! is a compiler plugin and not just a normal macro. in fact, it won't work in the stable channel of Rust 1.0!

Could you fix this or get back to me on how I can continue on without it? Thanks!

Port the template parser Pest

The current custom parser is difficult to maintain, and still contains some issue (for example, Json array literal as parameter). Pest is a PEG parser looks promising. I will investigate the possibility to port the parser to PEG and Pest.

support "mass" rendering

If I have a lot of templates and I want to render all of them with the same context It may be useful to convert the parameter into JSON once and use it on every templates. That would spare a to_string() in renderw().

If you are open for this feature I think I can contribute it.

Provide a way to directly render files

This one might be controversial given your design. My idea here is that currently rendering something is more verbose than it needs to be. I would like something like this:

handlebars.render("templates/index.hbs", &data);

as opposed to having to manually register the template first. I don't think the registry adds any benefit here. In fact, I think, I think it is an unnecessary layer of indirection (at least to the user). I recognize it might make sense from an implementation point of view but I'm not knowledgeable enough about your library to judge that.

Zero param helper expression

Currently a helper expression with zero argument will be treated as expression. We need to treat them same at parser level and detect whether it's an expression or helper at render time.

Performance when compiled under debug

Registering (in debug) a template through register_template_string() which has an embedded 700-line SVG (xml tags) takes something like 3 minutes on my fairly modern computer. That's orders of magnitude slower than I would expect.

In release mode it happens in less than 2 seconds.

Normally I wouldn't care about debug mode, but my website rebuilds all templates on every page load during debug mode, which is helpful while I'm developing to see changes immediately w/o recompiling.

Parent scope seems to be wrong

The template is:

{{# each departments }}
    Department: {{ name }}
    Employees:
        {{# each employees }}
            {{ name }} ({{ ../salary }})
        {{/ each }}
{{/ each }}

And the json:

{
    "departments": [{
        "name": "developers",
        "salary": 100,
        "employees": [{
            "name": "Jonh Smith"
        }]
    }]
}

In http://tryhandlebarsjs.com/ I get the following output:

    Department: developers
    Employees:
            Jonh Smith (100)

But with this library I get the following output:


    Department: developers
    Employees:

            Jonh Smith ()

As far as I can observe it skips one scope. I.e. I can access ../departments from the internal {{#each employees}} loop (but I would expect it to be ../../departments). And I can't access the scope of the {{#each departments}} loop (so the ../salary is empty)

Template compile fails to notice an invalid template

First of all, thanks for this library :)

I noticed that this unit test would panic as Template successfully compiles source:

#[test]
fn test_invalid_template_cannot_be_compiled() {
    let source = "{{invalid}".to_string();
    let _ = Template::compile(source).err().unwrap();
}

Is this the intended behavior?

I'm in the middle of writing unit tests for my library and I wanted to include one when the template cannot be compiled. I tried this one first but the compilation was successful, so I came here.

Update: {invalid}} cannot be compiled.
Also, could you provide me an example which causes a RenderError? I tried {{log}} (without any parameters), but it seems like it's ok.

Block Params

Any plans to add support for Handlebars block params?

Change render function to work with a writer

The current implementation is totally string based, that means we need to store everything in memory. A writer oriented impl would compatible with current one, but also be flexible to larger data.

Triple stash excaping potential bug

I'm sending a string of html in the model. Say it looks something like this:

{ body : "<h1>hello</h1>\n<p>world</p>" }

{{{body}}} would get rendered like this:

hello

\n
world

in the browser. The '\n" actually shows up when the page is rendered. Instead, I think \n's should just be new lines in html, not \n in the html string.

Thanks!

subexpression problem

Hi, I have been using handlebars-rust in my project: mdBook and before I bother you with my problem I want to thank you for your work on this crate ๐Ÿ‘

So it's my first time using handlebars, so maybe i am doing something wrong but I have this code:

{{#if (previous)}}
    <a href="{{#previous}}{{/previous}}" class="nav-chapters previous">
        <i class="fa fa-angle-left"></i>
    </a>
{{else}}
    {{#previous}}{{/previous}}
{{/if}}

And basically what I am trying to do is hide the block if the previous helper returns "".
But it was always hidden. I have added the else block only to confirm that previous did return something.

Could this be a bug or am I doing something stupid?

Consider using rustc_serialize::json::encode to obtain json

Rather than force every structure to implement the ToJson trait, it might be preferable to allow users to just add a #[derive(RustcEncodable)] on their structs, and handlebars can obtain a JSON value via the rustc_serialize::json::encode function.

serde vs rustc_serialize

I've been told by Nick Cameron and James Miller that the rustc_serialize crate is long past expired and that serde should be used instead.

It should probably be setup as feature options (choose either rustc_serialize or serde) so downstream users aren't forced to move over to serde immediately.

If I get a chance I'll give it a try and submit a PR, but don't expect anything for at least a few days

Relicense under dual MIT/Apache-2.0

This issue was automatically generated. Feel free to close without ceremony if
you do not agree with re-licensing or if it is not possible for other reasons.
Respond to @cmr with any questions or concerns, or pop over to
#rust-offtopic on IRC to discuss.

You're receiving this because someone (perhaps the project maintainer)
published a crates.io package with the license as "MIT" xor "Apache-2.0" and
the repository field pointing here.

TL;DR the Rust ecosystem is largely Apache-2.0. Being available under that
license is good for interoperation. The MIT license as an add-on can be nice
for GPLv2 projects to use your code.

Why?

The MIT license requires reproducing countless copies of the same copyright
header with different names in the copyright field, for every MIT library in
use. The Apache license does not have this drawback. However, this is not the
primary motivation for me creating these issues. The Apache license also has
protections from patent trolls and an explicit contribution licensing clause.
However, the Apache license is incompatible with GPLv2. This is why Rust is
dual-licensed as MIT/Apache (the "primary" license being Apache, MIT only for
GPLv2 compat), and doing so would be wise for this project. This also makes
this crate suitable for inclusion and unrestricted sharing in the Rust
standard distribution and other projects using dual MIT/Apache, such as my
personal ulterior motive, the Robigalia project.

Some ask, "Does this really apply to binary redistributions? Does MIT really
require reproducing the whole thing?" I'm not a lawyer, and I can't give legal
advice, but some Google Android apps include open source attributions using
this interpretation. Others also agree with
it
.
But, again, the copyright notice redistribution is not the primary motivation
for the dual-licensing. It's stronger protections to licensees and better
interoperation with the wider Rust ecosystem.

How?

To do this, get explicit approval from each contributor of copyrightable work
(as not all contributions qualify for copyright) and then add the following to
your README:

## License

Licensed under either of
 * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
 * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.

### Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any
additional terms or conditions.

and in your license headers, use the following boilerplate (based on that used in Rust):

// Copyright (c) 2016 handlebars-rust developers
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

Be sure to add the relevant LICENSE-{MIT,APACHE} files. You can copy these
from the Rust repo for a plain-text
version.

And don't forget to update the license metadata in your Cargo.toml to:

license = "MIT/Apache-2.0"

I'll be going through projects which agree to be relicensed and have approval
by the necessary contributors and doing this changes, so feel free to leave
the heavy lifting to me!

Contributor checkoff

To agree to relicensing, comment with :

I license past and future contributions under the dual MIT/Apache-2.0 license, allowing licensees to chose either at their option

Or, if you're a contributor, you can check the box in this repo next to your
name. My scripts will pick this exact phrase up and check your checkbox, but
I'll come through and manually review this issue later as well.

Partial names cannot contain hyphens after update

Pretty sure this is related to the parser updates. Partial names could previously be of the form "pre-content" or "page-scripts", but now I've had to replace hyphens with underscores to make things work.

Rewrite README

The current README is not quite friendly to those users who are new to handlebars templating language. So I'm going to rewrite it from scratch. Also we need to emphasis on the difference between this library and the original js one.

related to #57

"Use template name directly" points to wrong template file

Steps to reproduce:

  1. Modify examples/base0.hbs such that the block name is incorrectly quoted. See https://gist.github.com/untitaker/87311dc36dd42f1791542b7dc61a968f
  2. Run cargo run --example partials

The example panics as expected. However, when modifying partials.rs such that the error message is printed, the following is printed:

Err(RenderError { desc: "Do not use literal here, use template name directly.", template_name: Some("template"), line_no: Some(5), column_no: Some(5) })

There are two thing wrong with this:

  • "page" is not a template name
  • The line/column information should point to base0.

^ as else

Support

{{#if ...}}
...
{{^}}
...
{{/if}}

Enum handling

If I had an enum like:

pub enum Value {
    Float(f64),
    Bool(bool),
}

How would I render something like:

{{#if value == Float(val)}}
    <input type='number' value={{val}} />
{{/if}}

{{#if value == Bool(val)}}
    <input type='checkbox' checked={{val}} />
{{/if}}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.