Add a "helpers" implementation to 2.0-dev #82

Merged
merged 1 commit into from Mar 15, 2012

Conversation

Projects
None yet
3 participants
@bobthecow
Owner

bobthecow commented Mar 1, 2012

In Mustache.php, "helpers" are implemented by injecting values into base of the context stack. This is very powerful, and also very simple. In my opinion, it's also spec compliant :)

Just like other values in the rendering context, helpers can be "lambdas" (suitable for services) or regular values (which are more akin to constants or global variables in PHP).

For example, if you want to add an i18n translation service to Mustache, you'd do something like this:

<?php

$m = new Mustache;
$m->addHelper('_i18n', function($text) {
    // IRL, you would use something far more robust :)
    $dictionary = array(
        'Hello.' => 'Hola.',
        'My name is {{ name }}.' => 'Me llamo {{ name }}.',
    );

    return array_key_exists($text, $dictionary) ? $dictionary[$text] : $text;
});

$tpl = $m->loadTemplate('{{#_i18n}}Hello.{{/_i18n}} {{#_i18n}}My name is {{ name }}.{{/_i18n}}');
$tpl->render(array('name' => 'Justin'));

And this would render:

Hola. Me llamo Justin.

... Pretty rad, eh?

This is implemented as a "frame 0" on the context stack. This means all data passed in to render() will end up in "frame 1", and will be able to mask helpers. Using the example above, if we passed in a new lambda as _i18n, the call to {{#_i18n}} would resolve to the new value rather than the helper. This is consistent with how the context stack works elsewhere in Mustache.

This was referenced Mar 1, 2012

@scribu

This comment has been minimized.

Show comment
Hide comment
@scribu

scribu Mar 1, 2012

Contributor

That does look useful.

If you use the gettext library for i18n, you'd also have to implement a tool (another helper or static parser) to extract the original strings and put them in a .pot file. Otherwise, you're no better off than passing the translated strings as normal variables.

Contributor

scribu commented Mar 1, 2012

That does look useful.

If you use the gettext library for i18n, you'd also have to implement a tool (another helper or static parser) to extract the original strings and put them in a .pot file. Otherwise, you're no better off than passing the translated strings as normal variables.

@bobthecow

This comment has been minimized.

Show comment
Hide comment
@bobthecow

bobthecow Mar 1, 2012

Owner
<?php

use Mustache\Mustache;
use Mustache\Tokenizer;

class I18nExtractor {
    const I18N_SECTION = '_i18n';

    public function __construct() {
        $this->mustache = new Mustache;
    }

    public function extract($source) {
        $this->source = $source;

        return $this->walk($this->mustache->parse($source));
    }

    private function walk(array $tree) {
        $strings = array();

        foreach ($tree as $node) {
            if (is_array($node)) {
                if ($node[Tokenizer::TYPE] == Tokenizer::T_SECTION && $node[Tokenizer::NAME] == self::I18N_SECTION) {
                    $strings[] = $this->getSource($node[Tokenizer::INDEX], $node[Tokenizer::END]);
                } elseif (in_array($node[Tokenizer::TAG], array(Tokenizer::T_SECTION, Tokenizer::T_INVERTED))) {
                    $strings += $this->walk($node[Tokenizer::NODES]);
                }
            }
        }

        return $strings;
    }

    private function getSource($start, $end) {
        return substr($this->source, $start, $end - $start);
    }
}
Owner

bobthecow commented Mar 1, 2012

<?php

use Mustache\Mustache;
use Mustache\Tokenizer;

class I18nExtractor {
    const I18N_SECTION = '_i18n';

    public function __construct() {
        $this->mustache = new Mustache;
    }

    public function extract($source) {
        $this->source = $source;

        return $this->walk($this->mustache->parse($source));
    }

    private function walk(array $tree) {
        $strings = array();

        foreach ($tree as $node) {
            if (is_array($node)) {
                if ($node[Tokenizer::TYPE] == Tokenizer::T_SECTION && $node[Tokenizer::NAME] == self::I18N_SECTION) {
                    $strings[] = $this->getSource($node[Tokenizer::INDEX], $node[Tokenizer::END]);
                } elseif (in_array($node[Tokenizer::TAG], array(Tokenizer::T_SECTION, Tokenizer::T_INVERTED))) {
                    $strings += $this->walk($node[Tokenizer::NODES]);
                }
            }
        }

        return $strings;
    }

    private function getSource($start, $end) {
        return substr($this->source, $start, $end - $start);
    }
}
@scribu

This comment has been minimized.

Show comment
Hide comment
@scribu

scribu Mar 1, 2012

Contributor

Pretty straightforward. Thanks!

Contributor

scribu commented Mar 1, 2012

Pretty straightforward. Thanks!

bobthecow added a commit that referenced this pull request Mar 15, 2012

Merge pull request #82 from bobthecow/feature/helpers
Add a "helpers" implementation to 2.0-dev

@bobthecow bobthecow merged commit 2bd3804 into dev Mar 15, 2012

@brycecammo

This comment has been minimized.

Show comment
Hide comment
@brycecammo

brycecammo Jun 20, 2012

Is it possible to get the template context or a pre-rendered variable into the helper? I'm hoping to implement something similar to the date parser you posted here.

Is it possible to get the template context or a pre-rendered variable into the helper? I'm hoping to implement something similar to the date parser you posted here.

@bobthecow

This comment has been minimized.

Show comment
Hide comment
@bobthecow

bobthecow Jul 28, 2012

Owner

@brycecammo I think you're looking for this? #101

Owner

bobthecow commented Jul 28, 2012

@brycecammo I think you're looking for this? #101

@brycecammo

This comment has been minimized.

Show comment
Hide comment
@brycecammo

brycecammo Jul 29, 2012

@bobthecow That's exactly what I'm after! Thanks :)

@bobthecow That's exactly what I'm after! Thanks :)

@joshpangell joshpangell referenced this pull request in XaminProject/handlebars.php May 31, 2013

Closed

registerHelpers as external method #12

@dmolsen dmolsen referenced this pull request in pattern-lab/patternlab-php Aug 4, 2014

Closed

Lambdas in json files #226

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment