added from tag
authorFabien Potencier <fabien.potencier@gmail.com>
Sun, 19 Dec 2010 10:26:38 +0000 (11:26 +0100)
committerFabien Potencier <fabien.potencier@gmail.com>
Sun, 19 Dec 2010 18:50:00 +0000 (19:50 +0100)
CHANGELOG
doc/templates.rst
lib/Twig/Extension/Core.php
lib/Twig/Node/From.php [new file with mode: 0644]
lib/Twig/TokenParser/From.php [new file with mode: 0644]
test/Twig/Tests/Fixtures/tags/macro/from.test [new file with mode: 0644]

index baf47ee..945352d 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -6,6 +6,7 @@ Backward incompatibilities:
 
 Changes:
 
+ * added the "from" tag to import macros as functions
  * added support for functions (a function is just syntactic sugar for a getAttribute() call)
  * made macros callable when sandbox mode is enabled
  * added an exception when a macro uses a reserved name
index 33e0844..633a2d4 100644 (file)
@@ -647,7 +647,7 @@ Here is a small example of a macro that renders a form element:
 .. code-block:: jinja
 
     {% macro input(name, value, type, size) %}
-      <input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
+        <input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
     {% endmacro %}
 
 Macros differs from native PHP functions in a few ways:
@@ -858,41 +858,60 @@ Import
 Twig supports putting often used code into macros. These macros can go into
 different templates and get imported from there.
 
+There are two ways to import templates. You can import the complete template
+into a variable or request specific macros from it.
+
 Imagine we have a helper module that renders forms (called ``forms.html``):
 
 .. code-block:: jinja
 
     {% macro input(name, value, type, size) %}
-      <input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
+        <input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
     {% endmacro %}
 
     {% macro textarea(name, value, rows) %}
-      <textarea name="{{ name }}" rows="{{ rows|default(10) }}" cols="{{ cols|default(40) }}">{{ value|e }}</textarea>
+        <textarea name="{{ name }}" rows="{{ rows|default(10) }}" cols="{{ cols|default(40) }}">{{ value|e }}</textarea>
     {% endmacro %}
 
-Importing these macros in a template is as easy as using the ``import`` tag:
+The easiest and most flexible is importing the whole module into a variable.
+That way you can access the attributes:
 
 .. code-block:: jinja
 
     {% import 'forms.html' as forms %}
 
     <dl>
-      <dt>Username</dt>
-      <dd>{{ forms.input('username') }}</dd>
-      <dt>Password</dt>
-      <dd>{{ forms.input('password', none, 'password') }}</dd>
+        <dt>Username</dt>
+        <dd>{{ forms.input('username') }}</dd>
+        <dt>Password</dt>
+        <dd>{{ forms.input('password', none, 'password') }}</dd>
     </dl>
     <p>{{ forms.textarea('comment') }}</p>
 
-Importing is not needed if the macros and the template are defined in the file;
-use the special ``_self`` variable instead:
+Alternatively you can import names from the template into the current
+namespace:
+
+.. code-block:: jinja
+
+    {% from 'forms.html' import input as input_field, textarea %}
+
+    <dl>
+        <dt>Username</dt>
+        <dd>{{ input_field('username') }}</dd>
+        <dt>Password</dt>
+        <dd>{{ input_field('password', type='password') }}</dd>
+    </dl>
+    <p>{{ textarea('comment') }}</p>
+
+Importing is not needed if the macros and the template are defined in the same
+file; use the special ``_self`` variable instead:
 
 .. code-block:: jinja
 
     {# index.html template #}
 
     {% macro textarea(name, value, rows) %}
-      <textarea name="{{ name }}" rows="{{ rows|default(10) }}" cols="{{ cols|default(40) }}">{{ value|e }}</textarea>
+        <textarea name="{{ name }}" rows="{{ rows|default(10) }}" cols="{{ cols|default(40) }}">{{ value|e }}</textarea>
     {% endmacro %}
 
     <p>{{ _self.textarea('comment') }}</p>
@@ -904,7 +923,7 @@ But you can still create an alias by importing from the ``_self`` variable:
     {# index.html template #}
 
     {% macro textarea(name, value, rows) %}
-      <textarea name="{{ name }}" rows="{{ rows|default(10) }}" cols="{{ cols|default(40) }}">{{ value|e }}</textarea>
+        <textarea name="{{ name }}" rows="{{ rows|default(10) }}" cols="{{ cols|default(40) }}">{{ value|e }}</textarea>
     {% endmacro %}
 
     {% import _self as forms %}
index f647163..74517b5 100644 (file)
@@ -28,6 +28,7 @@ class Twig_Extension_Core extends Twig_Extension
             new Twig_TokenParser_Filter(),
             new Twig_TokenParser_Macro(),
             new Twig_TokenParser_Import(),
+            new Twig_TokenParser_From(),
             new Twig_TokenParser_Set(),
             new Twig_TokenParser_Spaceless(),
         );
diff --git a/lib/Twig/Node/From.php b/lib/Twig/Node/From.php
new file mode 100644 (file)
index 0000000..a71a7e2
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) 2010 Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Represents a from node.
+ *
+ * @package    twig
+ * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
+ */
+class Twig_Node_From extends Twig_Node_Import
+{
+    public function __construct(Twig_Node_Expression $expr, array $imports, $lineno, $tag = null)
+    {
+        parent::__construct($expr, new Twig_Node_Expression_AssignName('_imported_'.rand(10000, 99999), $lineno), $lineno, $tag);
+
+        $this->setAttribute('imports', $imports);
+    }
+
+    /**
+     * Compiles the node to PHP.
+     *
+     * @param Twig_Compiler A Twig_Compiler instance
+     */
+    public function compile($compiler)
+    {
+        parent::compile($compiler);
+
+        foreach ($this->getAttribute('imports') as $name => $alias) {
+            $compiler
+                ->write('$context[')
+                ->repr($alias)
+                ->raw('] = new Twig_Function(')
+                ->subcompile($this->getNode('var'))
+                ->raw(', ')
+                ->repr($name)
+                ->raw(");\n")
+            ;
+        }
+    }
+}
diff --git a/lib/Twig/TokenParser/From.php b/lib/Twig/TokenParser/From.php
new file mode 100644 (file)
index 0000000..9398f1d
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) 2010 Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+class Twig_TokenParser_From extends Twig_TokenParser
+{
+    /**
+     * Parses a token and returns a node.
+     *
+     * @param Twig_Token $token A Twig_Token instance
+     *
+     * @return Twig_NodeInterface A Twig_NodeInterface instance
+     */
+    public function parse(Twig_Token $token)
+    {
+        $macro = $this->parser->getExpressionParser()->parseExpression();
+        $stream = $this->parser->getStream();
+        $stream->expect('import');
+
+        $targets = array();
+        do {
+            $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
+
+            $alias = $name;
+            if ($stream->test('as')) {
+                $stream->next();
+
+                $alias = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
+            }
+
+            $targets[$name] = $alias;
+
+            if (!$stream->test(Twig_Token::PUNCTUATION_TYPE, ',')) {
+                break;
+            }
+
+            $stream->next();
+        } while (true);
+
+        $stream->expect(Twig_Token::BLOCK_END_TYPE);
+
+        return new Twig_Node_From($macro, $targets, $token->getLine(), $this->getTag());
+    }
+
+    /**
+     * Gets the tag name associated with this token parser.
+     *
+     * @param string The tag name
+     */
+    public function getTag()
+    {
+        return 'from';
+    }
+}
diff --git a/test/Twig/Tests/Fixtures/tags/macro/from.test b/test/Twig/Tests/Fixtures/tags/macro/from.test
new file mode 100644 (file)
index 0000000..205f591
--- /dev/null
@@ -0,0 +1,18 @@
+--TEST--
+"macro" tag
+--TEMPLATE--
+{% from 'forms.twig' import foo %}
+{% from 'forms.twig' import foo as foobar, bar %}
+
+{{ foo('foo') }}
+{{ foobar('foo') }}
+{{ bar('foo') }}
+--TEMPLATE(forms.twig)--
+{% macro foo(name) %}foo{{ name }}{% endmacro %}
+{% macro bar(name) %}bar{{ name }}{% endmacro %}
+--DATA--
+return array()
+--EXPECT--
+foofoo
+foofoo
+barfoo