[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