[Jifty-commit] r7523 - in Template-Declare/branches/mixmaster: . lib/Template lib/Template/Declare

Jifty commits jifty-commit at lists.jifty.org
Wed Oct 7 15:42:05 EDT 2009


Author: theory
Date: Wed Oct  7 15:42:05 2009
New Revision: 7523

Added:
   Template-Declare/branches/mixmaster/t/deep_mixing.t
   Template-Declare/branches/mixmaster/t/mixing.t
   Template-Declare/branches/mixmaster/t/relative-mixing.t
Modified:
   Template-Declare/branches/mixmaster/Changes
   Template-Declare/branches/mixmaster/lib/Template/Declare.pm
   Template-Declare/branches/mixmaster/lib/Template/Declare/Tags.pm
   Template-Declare/branches/mixmaster/t/dispatch_order.t

Log:
Added `mix`.

Basically, it behaves just like `alias`, only now you can use the `into`
keyword to specify what class to mix the templates into. It also adds a `set`
keyword, which is just sugar for the optional hash reference of package
variables.

Also updated the documentation to emphasize `mix` and to deprecate `alias` and
`import_templates`.

Moved the private mixin subs `_import` and `_import_code` to the end of the
package with all the other private subs.


Modified: Template-Declare/branches/mixmaster/Changes
==============================================================================
--- Template-Declare/branches/mixmaster/Changes	(original)
+++ Template-Declare/branches/mixmaster/Changes	Wed Oct  7 15:42:05 2009
@@ -7,6 +7,10 @@
   This won't be an issue if you only use a single temlate class.
 * Converted the implementation of "alias" to be the same as that used for
   "import_templates".
+* Changed aliases imported from subclasses such that their invocants are the
+  classes in which they are defined, rather than the subclass. This makes
+  "alias" consistent with "import_templates".
+* Added "mix" and deprecated "alias" and "import_templates".
 
 0.40_01 2009-08-12
 * Support for inline tagset definitions. Thanks to Olivier 'dolmen' Mengué

Modified: Template-Declare/branches/mixmaster/lib/Template/Declare.pm
==============================================================================
--- Template-Declare/branches/mixmaster/lib/Template/Declare.pm	(original)
+++ Template-Declare/branches/mixmaster/lib/Template/Declare.pm	Wed Oct  7 15:42:05 2009
@@ -592,89 +592,91 @@
 
 }
 
-=head2 alias TEMPLATE_CLASS under PATH
+=head2 mix
 
-    alias Some::Clever::Mixin under '/mixin';
-    alias Some::Other::Mixin  under '/mymix', { name => 'Larry' };
-
-Sometimes you want to alias templates to a subpath, or mix them into an
-existing template path. Use C<alias> to do so. In the first example, if
-Some::Clever::Mixin creates templates named "foo" and "bar", they will be
-aliased to "mixin/foo" and "mixin/bar".
+    mix Some::Clever::Mixin      under '/mixin';
+    mix Some::Other::Mixin       under '/otmix', set { name => 'Larry' };
+    mix My::Mixin into My::View, under '/mymix';
+
+Sometimes you want to mix templates from one class into another class; C<mix>
+is your key to doing so. In the first example, if Some::Clever::Mixin creates
+templates named C<foo> and C<bar>, they will be mixed into the calling
+template class as C<mixin/foo> and C<mixin/bar>.
 
 The second example mixes in the templates defined in Some::Other::Mixin into
-the "/mymix" path and defines a package variable for use only by the alias.
-If this template was defined in Some::Other::Mixin:
+into the calling class under the "/mymix" path. Furthermore, those mixed-in
+templates have package variables set for them that are accessible only from
+their mixed-in paths. For example, if this template was defined in
+Some::Other::Mixin:
 
-    template 'howdy' => sub {
+    template howdy => sub {
         my $self = shift;
         outs "Howdy, " . $self->package_variable('name') || 'Jesse';
     };
 
-Then use of the "mymixin/howdy" template will output "Howdy, Larry", while the
-output from original template, "howdy", will output "Howdy, Jesse". In other
-words, package variables defined for the alias are available only to the
-alias, and not to the original.
+Then C<show('mymixin/howdy')> will output "Howdy, Larry", while the output
+from C<show('howdy')> will output "Howdy, Jesse". In other words, package
+variables defined for the mixed-in templates are available only to the mixins
+and not to the original.
 
 In either case, ineritance continues to work. A template package that inherits
 from Some::Other::Mixin, for example, will be able to access both
-"mymixin/howdy" and "howdy".
+C<mymixin/howdy> and C<howdy>.
+
+By default, C<mix> will mix templates into the class from which it's called.
+But sometimes you might want to mix templates into some other template class.
+Such might be useful for end users to compose template structures from
+collections of template classes. In such a case, use the C<into> keyword to
+specify into what class the templates should be mixed in. The third example
+demonstrates this, where My::Mixin templates are mixed into My::View. Of
+course, you can still specify variables to set for those mixins.
+
+For those who prefer a direct OO syntax for mixins, just call C<mix> as a
+method on the class to be mixed in. To replicate the above three exmaples
+without the use of the sugar:
+
+  Some::Clver::Mixin->mix( '/mixin' );
+  Some::Other::Mixin->mix( '/otmix', { name => 'Larry' } );
+  My::Mixin->mix('My::View', '/mymix');
 
 =cut
 
-sub alias { shift->_import(scalar caller(0), @_) }
+sub mix {
+    my $mixin = shift;
+    my $into  = eval { $_[0]->isa(__PACKAGE__) } ? shift : caller(0);
+    $mixin->_import($into, @_);
+}
 
-sub _import {
-    return undef if $_[0] eq __PACKAGE__;
-    my ($mixin, $into, $prefix, $vars) = @_;
+=head2 into
 
-    $prefix =~ s|/+/|/|g;
-    $prefix =~ s|/$||;
-    $mixin->imported_into($prefix);
+  $class = into $class;
 
-    my @packages = reverse grep { $_->isa(__PACKAGE__) }
-        Class::ISA::self_and_super_path( $mixin );
+C<into> is a helper method providing semantic sugar for the C<mix> method. All
+it does is return the name of the class on which it was called.
 
-    foreach my $from (@packages) {
-        for my $tname (  __PACKAGE__->_templates_for($from) ) {
-            $into->register_template(
-                "$prefix/$tname",
-                _import_code( $tname, $from, $vars )
-            );
-        }
-        for my $tname (  __PACKAGE__->_private_templates_for($from) ) {
-            $into->register_private_template(
-                "$prefix/$tname",
-                _import_code( $tname, $from, $vars )
-            );
-        }
-    }
-}
+=cut
 
-sub _import_code {
-    my ($tname, $class, $vars) = @_;
-    my $code = $class->_find_template_sub( _template_name_to_sub($tname) );
-    return $code unless $vars;
-    return sub {
-        # XXX This does not seem to be needed.
-        # shift @_;  # Get rid of the passed-in "$self" class.
-        local $TEMPLATE_VARS->{$class} = $vars;
-        $code->($class, @_);
-    };
-}
+sub into { shift }
+
+=head2 alias
+
+    alias Some::Clever::Mixin under '/mixin';
+    alias Some::Other::Mixin  under '/mymix', { name => 'Larry' };
+
+Like C<mix>, but without support for the C<into> keyword. That is, it mixes
+templates into the calling template class. Deprecated in favor of C<mix>.
+
+=cut
+
+sub alias { shift->_import(scalar caller(0), @_) }
 
 =head2 import_templates
 
     import_templates MyApp::Templates under '/something';
 
-Import the templates defined in a template class into a subpath via the
-C<import_templates> function. In this example, the templates defined in
-MyApp::Templates will be imported into the "/something" path. Thus, a template
-deined in MyApp::Templates named "foo" will also be accessible via
-"something/foo". This is not unlike mixing in templates with C<alias>, but is
-lighter-weight and package variables cannot be assigned. This is because it is
-really like a hard link to the template, whereas an alias is a copy with the
-variable information attached to it.
+Like C<mix>, but without support for the C<into> or C<set> keywords. That is,
+it mixes templates into the calling template class and does not support
+package variables for those mixins. Deprecated in favor of C<mix>.
 
 =cut
 
@@ -781,6 +783,45 @@
     *{ $class . '::' . $subname } = $coderef;
 }
 
+sub _import {
+    return undef if $_[0] eq __PACKAGE__;
+    my ($mixin, $into, $prefix, $vars) = @_;
+
+    $prefix =~ s|/+/|/|g;
+    $prefix =~ s|/$||;
+    $mixin->imported_into($prefix);
+
+    my @packages = reverse grep { $_->isa(__PACKAGE__) }
+        Class::ISA::self_and_super_path( $mixin );
+
+    foreach my $from (@packages) {
+        for my $tname (  __PACKAGE__->_templates_for($from) ) {
+            $into->register_template(
+                "$prefix/$tname",
+                _import_code( $tname, $from, $vars )
+            );
+        }
+        for my $tname (  __PACKAGE__->_private_templates_for($from) ) {
+            $into->register_private_template(
+                "$prefix/$tname",
+                _import_code( $tname, $from, $vars )
+            );
+        }
+    }
+}
+
+sub _import_code {
+    my ($tname, $class, $vars) = @_;
+    my $code = $class->_find_template_sub( _template_name_to_sub($tname) );
+    return $code unless $vars;
+    return sub {
+        # XXX This does not seem to be needed.
+        # shift @_;  # Get rid of the passed-in "$self" class.
+        local $TEMPLATE_VARS->{$class} = $vars;
+        $code->($class, @_);
+    };
+}
+
 =head1 PITFALLS
 
 We're reusing the perl interpreter for our templating langauge, but Perl was

Modified: Template-Declare/branches/mixmaster/lib/Template/Declare/Tags.pm
==============================================================================
--- Template-Declare/branches/mixmaster/lib/Template/Declare/Tags.pm	(original)
+++ Template-Declare/branches/mixmaster/lib/Template/Declare/Tags.pm	Wed Oct  7 15:42:05 2009
@@ -16,7 +16,7 @@
 
 our @EXPORT
     = qw( with template private show show_page attr outs
-          outs_raw in_isolation $self under
+          outs_raw in_isolation $self under set
           get_current_attr xml_decl
           smart_tag_wrapper current_template create_wrapper );
 our @TAG_SUB_LIST;
@@ -885,14 +885,22 @@
 
 =head2 under
 
-C<under> is a helper function providing semantic sugar for the
-C<import_templates> and C<alias> methods of
-L<Template::Declare|Template::Declare>.
+C<under> is a helper function providing semantic sugar for the C<mix> method
+of L<Template::Declare|Template::Declare/"mix">.
 
 =cut
 
 sub under ($) { return shift }
 
+=head2 set
+
+C<set> is a helper function providing semantic sugar for the C<mix> method of
+L<Template::Declare|Template::Declare/"mix">.
+
+=cut
+
+sub set ($) { return shift }
+
 =begin comment
 
 =head2 append_attr

Added: Template-Declare/branches/mixmaster/t/deep_mixing.t
==============================================================================
--- (empty file)
+++ Template-Declare/branches/mixmaster/t/deep_mixing.t	Wed Oct  7 15:42:05 2009
@@ -0,0 +1,83 @@
+use warnings;
+use strict;
+
+##############################################################################
+package SearchPlugin::View;
+
+use base qw/Template::Declare/;
+use Template::Declare::Tags;
+
+template 'search' => sub {
+    h1 {'SearchPlugin::View::search'};
+};
+
+##############################################################################
+package ListPlugin::View;
+
+use base qw/Template::Declare/;
+use Template::Declare::Tags;
+
+template 'listing' => sub {
+    h1 {'ListPlugin::View::listing'};
+};
+
+mix SearchPlugin::View under '/';
+
+##############################################################################
+package MyApp::View;
+
+use base qw/Template::Declare/;
+use Template::Declare::Tags;
+
+template 'toplevel' => sub {h1{'Toplevel'}};
+
+mix ListPlugin::View under '/plugin';
+
+##############################################################################
+package main;
+Template::Declare->init( dispatch_to => ['MyApp::View'] );
+
+use Test::More tests => 14;
+use Test::Warn;
+require "t/utils.pl";
+
+ok( MyApp::View->has_template('toplevel'), 'Should have toplevel template' );
+ok( !MyApp::View->has_template('listing'), "the listing template isn't imported to the top level");
+ok( !MyApp::View->has_template('search'), "The search template isn't imported to the top level" );
+ok( MyApp::View->has_template('/plugin/listing'), 'has listing template' );
+ok( MyApp::View->has_template('/plugin/search'), 'has search template' );
+
+{
+    my $simple = ( Template::Declare->show('toplevel'));
+    like( $simple, qr'Toplevel', 'Can execute toplevel template' );
+}
+{
+    warning_like {
+        my $simple = ( Template::Declare->show('listing') ||'');
+        unlike( $simple, qr'listing',
+            'Cannot call a toplevel "listing" template' );
+        }
+        qr/The template 'listing' could not be found/,
+        "listing is private"
+
+}
+warning_like {
+    my $simple = ( Template::Declare->show('search')||'');
+    unlike( $simple, qr'search', "Cannot call a toplevel /search" );
+} qr/The template 'search' could not be found/, "Search could not be found";
+
+{
+
+    my $simple = ( Template::Declare->show('/plugin/listing'));
+    like( $simple, qr'listing', "Can call /plugin/listing" );
+    $simple = ( Template::Declare->show('plugin/listing'));
+    like( $simple, qr'listing', "Can call plugin/listing" );
+}
+{
+    my $simple = ( Template::Declare->show('/plugin/search'));
+    like( $simple, qr'search' , "Can call /plugin/search");
+    $simple = ( Template::Declare->show('plugin/search'));
+    like( $simple, qr'search' , "Can call plugin/search");
+}
+
+1;

Modified: Template-Declare/branches/mixmaster/t/dispatch_order.t
==============================================================================
--- Template-Declare/branches/mixmaster/t/dispatch_order.t	(original)
+++ Template-Declare/branches/mixmaster/t/dispatch_order.t	Wed Oct  7 15:42:05 2009
@@ -27,7 +27,7 @@
 
 ##############################################################################
 package main;
-use Test::More tests => 16;
+use Test::More tests => 22;
 
 # Check template resolution with the deprecated `roots` parameterx.
 ok !Template::Declare->init( roots => ['Wifty::Foo', 'Wifty::Bar'] ),
@@ -77,7 +77,7 @@
     'Bip should now have precedence';
 
 ##############################################################################
-# Now try the dame stuff with aliases.
+# Now try the same stuff with aliases.
 ##############################################################################
 package Mifty::Foo;
 use base qw/Template::Declare/;
@@ -134,3 +134,62 @@
 
 is +Template::Declare->show('hello'), 'hello from Bip',
     'Mifty::Bip should now have precedence';
+
+##############################################################################
+# Now try the same stuff with mixes.
+##############################################################################
+package Sifty::Foo;
+use base qw/Template::Declare/;
+use Template::Declare::Tags;
+template hello => sub { outs 'hello from Foo' };
+
+##############################################################################
+package Sifty::Bar;
+use base qw/Template::Declare/;
+use Template::Declare::Tags;
+template hello => sub { outs 'hello from Bar' };
+
+##############################################################################
+package Sifty::Baz;
+use base qw/Template::Declare/;
+use Template::Declare::Tags;
+template hello => sub { outs 'hello from Baz' };
+
+##############################################################################
+package Sifty::Bip;
+use base qw/Template::Declare/;
+use Template::Declare::Tags;
+template hello => sub { outs 'hello from Bip' };
+
+##############################################################################
+# Import the Baz templates into Bar.
+package Sifty::Bar;
+mix Sifty::Baz under '/';
+
+##############################################################################
+package main;
+ok !Template::Declare->init( dispatch_to => ['Sifty::Foo', 'Sifty::Bar'] ),
+    'init to dispatch to Sifty::Foo and Sifty::Bar';
+
+is +Template::Declare->show('hello'), 'hello from Foo',
+    'Sifty::Foo should have precedence';
+
+##############################################################################
+# Import the Baz templates into Foo.
+package Sifty::Foo;
+import_templates Sifty::Baz under '/';
+
+##############################################################################
+package main;
+ok !Template::Declare->init( dispatch_to => ['Sifty::Foo', 'Sifty::Bar'] ),
+    'init to dispatch to Sifty::Foo and Sifty::Bar again';
+
+is +Template::Declare->show('hello'), 'hello from Baz',
+    'Sifty::Baz::hello should have replaced Sifty::Foo::hello';
+
+# Now dispatch only to Bip and Foo.
+ok !Template::Declare->init( dispatch_to => ['Sifty::Bip', 'Sifty::Foo'] ),
+    'init to dispatch to Sifty::Bip and Sifty::Foo';
+
+is +Template::Declare->show('hello'), 'hello from Bip',
+    'Sifty::Bip should now have precedence';

Added: Template-Declare/branches/mixmaster/t/mixing.t
==============================================================================
--- (empty file)
+++ Template-Declare/branches/mixmaster/t/mixing.t	Wed Oct  7 15:42:05 2009
@@ -0,0 +1,113 @@
+use warnings;
+use strict;
+
+##############################################################################
+package Wifty::UI::mixed_pkg;
+use base qw/Template::Declare/;
+use Template::Declare::Tags;
+
+template 'mixed' => sub {
+    my $self = shift;
+    div { outs_raw "Invocant:  '$self'" };
+    div { 'Variable ', $self->package_variable('VARIABLE') };
+};
+
+##############################################################################
+package Wifty::UI::mixed_subclass_pkg;
+use base qw/Wifty::UI::mixed_pkg/;
+use Template::Declare::Tags;
+
+##############################################################################
+package Wifty::UI;
+use base qw/Template::Declare/;
+use Template::Declare::Tags;
+
+template simple => sub {
+
+    html {
+        head {};
+        body { show 'private-content'; };
+    }
+
+};
+
+private template 'private-content' => sub {
+    my $self = shift;
+    with( id => 'body' ), div {
+        outs( 'This is my content from' . $self );
+    };
+};
+
+mix Wifty::UI::mixed_pkg under '/mixed_pkg', set { VARIABLE => 'SET' } ;
+mix Wifty::UI::mixed_pkg under '/mixed_pkg2';
+mix Wifty::UI::mixed_subclass_pkg under '/mixed_subclass_pkg';
+
+##############################################################################
+package main;
+use Template::Declare::Tags;
+
+# Mix from outside the class.
+mix Wifty::UI::mixed_pkg into Wifty::UI, under '/mixed_pkg3';
+
+# Fire it up.
+Template::Declare->init( dispatch_to => ['Wifty::UI'] );
+
+use Test::More tests => 24;
+require "t/utils.pl";
+
+ok( Wifty::UI::mixed_pkg->has_template('mixed'), 'Mixed package should have template' );
+ok( !  Wifty::UI->has_template('mixed'), 'Unrelated package should not' );
+ok( Wifty::UI::mixed_subclass_pkg->has_template('mixed'), 'Subclass should' );
+
+ok( Template::Declare->has_template('mixed_pkg/mixed'), 'TD should find mix' );
+
+ok( Template::Declare->has_template('mixed_subclass_pkg/mixed'),
+    'Mix should be visible in a subclass, too' );
+
+{
+    # Try the first mix with a variable set.
+    ok my $simple = ( show('mixed_pkg/mixed') ), 'Should get output from mix template';
+    like( $simple, qr'Invocant:', 'Its output should be right' );
+    like( $simple, qr'Variable SET' , "The variable was set");
+    like( $simple, qr{'Wifty::UI::mixed_pkg'},
+        '$self is correct in template block' );
+    ok_lint($simple);
+}
+
+{
+    # Try the second mix with no variable.
+    ok my $simple = ( show('mixed_pkg2/mixed') ), 'Should get output from second mix';
+    like( $simple, qr'Invocant:', 'Its output should be right' );
+    unlike( $simple, qr'Varialble SET' , 'But the variable should not be set');
+    like( $simple, qr{'Wifty::UI::mixed_pkg'},
+        '$self is correct in template block' );
+    ok_lint($simple);
+}
+
+{
+    # Try the third mix using `into`.
+    ok my $simple = ( show('mixed_pkg3/mixed') ), 'Should get output from third mix';
+    like( $simple, qr'Invocant:', 'Its output should be right' );
+    unlike( $simple, qr'Varialble SET' , 'But the variable should not be set');
+    like( $simple, qr{'Wifty::UI::mixed_pkg'},
+        '$self is correct in template block' );
+    ok_lint($simple);
+}
+
+{
+    ok my $simple = ( show('mixed_subclass_pkg/mixed') ),
+        'Should get output from superclass template';
+    like(
+        $simple,
+        qr'Invocant:',
+        "We should get the mixed version in the subclass"
+    );
+    like(
+        $simple,
+        qr{'Wifty::UI::mixed_pkg'},
+        '$self is correct in template block'
+    );
+    ok_lint($simple);
+}
+
+1;

Added: Template-Declare/branches/mixmaster/t/relative-mixing.t
==============================================================================
--- (empty file)
+++ Template-Declare/branches/mixmaster/t/relative-mixing.t	Wed Oct  7 15:42:05 2009
@@ -0,0 +1,83 @@
+use warnings;
+use strict;
+
+##############################################################################
+package SearchPlugin::View;
+
+use base qw/Template::Declare/;
+use Template::Declare::Tags;
+
+template 'search' => sub {
+    h1 {'SearchPlugin::View::search'};
+};
+
+##############################################################################
+package ListPlugin::View;
+
+use base qw/Template::Declare/;
+use Template::Declare::Tags;
+
+template 'listing' => sub {
+    h1 {'ListPlugin::View::listing'};
+};
+
+mix SearchPlugin::View under '/';
+
+##############################################################################
+package MyApp::View;
+
+use base qw/Template::Declare/;
+use Template::Declare::Tags;
+
+template 'toplevel' => sub {h1{'Toplevel'}};
+
+mix ListPlugin::View under 'plugin/';
+
+##############################################################################
+package main;
+Template::Declare->init( dispatch_to => ['MyApp::View'] );
+
+use Test::More tests => 14;
+use Test::Warn;
+require "t/utils.pl";
+
+ok( MyApp::View->has_template('toplevel'), 'Should have toplevel template' );
+ok( !MyApp::View->has_template('listing'), "the listing template isn't imported to the top level");
+ok( !MyApp::View->has_template('search'), "The search template isn't imported to the top level" );
+ok( MyApp::View->has_template('/plugin/listing'), 'has listing template' );
+ok( MyApp::View->has_template('/plugin/search'), 'has search template' );
+
+{
+    my $simple = ( Template::Declare->show('toplevel'));
+    like( $simple, qr'Toplevel', 'Can execute toplevel template' );
+}
+{
+    warning_like {
+        my $simple = ( Template::Declare->show('listing') ||'');
+        unlike( $simple, qr'listing',
+            'Cannot call a toplevel "listing" template' );
+        }
+        qr/The template 'listing' could not be found/,
+        "listing is private"
+
+}
+warning_like {
+    my $simple = ( Template::Declare->show('search')||'');
+    unlike( $simple, qr'search', "Cannot call a toplevel /search" );
+} qr/The template 'search' could not be found/, "Search could not be found";
+
+{
+
+    my $simple = ( Template::Declare->show('/plugin/listing'));
+    like( $simple, qr'listing', "Can call /plugin/listing" );
+    $simple = ( Template::Declare->show('plugin/listing'));
+    like( $simple, qr'listing', "Can call plugin/listing" );
+}
+{
+    my $simple = ( Template::Declare->show('/plugin/search'));
+    like( $simple, qr'search' , "Can call /plugin/search");
+    $simple = ( Template::Declare->show('plugin/search'));
+    like( $simple, qr'search' , "Can call plugin/search");
+}
+
+1;


More information about the Jifty-commit mailing list