Skip Menu |

This queue is for tickets about the Template-Declare CPAN distribution.

Report information
The Basics
Id: 37624
Status: resolved
Priority: 0/
Queue: Template-Declare

People
Owner: Nobody in particular
Requestors: dwheeler [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: (no value)
Broken in: (no value)
Fixed in: (no value)



Subject: Add Support for Wrappers
Date: Fri, 11 Jul 2008 12:13:22 -0700
To: bug-template-declare [...] rt.cpan.org
From: "David E. Wheeler" <dwheeler [...] cpan.org>
Howdy, I've whipped up a patch to add a new function, `wrapper`, that allows users to declare tag-like subs that can be used to wrap output. The patch is attached. It works like so: package MyApp::Templates; use Template::Declare::Tags; use base 'Template::Declare'; BEGIN { wrapper wrap => sub { my $code = shift; my %params = @_; html { head { title { outs "Hello, $params{user}!"} }; body { $code->(); div { outs 'This is the end, my friend' }; }; } }; } template inner => sub { wrap { h1 { outs "Hello, Jesse, s'up?" }; } user => 'Jesse'; }; Cool, eh? The only downside I see at this point is that the `wrap` sub gets installed in Template::Declare::Tags. Ideally it'd just be installed in the caller and exported from there, but I'm not sure how you want to handle that. This way it's just exported for every `use Template::Declare::Tags`, but maybe there'd be some way to specify a wrapper set or something the way you currently specify a tagset? Let me know what you think. Best, David

Message body is not shown because sender requested not to inline it.

Subject: Re: [rt.cpan.org #37624] AutoReply: Add Support for Wrappers
Date: Sat, 12 Jul 2008 14:19:05 -0700
To: bug-Template-Declare [...] rt.cpan.org
From: "David E. Wheeler" <dwheeler [...] cpan.org>
On Jul 11, 2008, at 12:17, Bugs in Template-Declare via RT wrote: Show quoted text
> Cool, eh? The only downside I see at this point is that the `wrap` sub > gets installed in Template::Declare::Tags. Ideally it'd just be > installed in the caller and exported from there, but I'm not sure how > you want to handle that. This way it's just exported for every `use > Template::Declare::Tags`, but maybe there'd be some way to specify a > wrapper set or something the way you currently specify a tagset? Let > me know what you think.
I think I've decided that this is a stupid idea. What I'm thinking of doing instead is just writing a helper module that defines wrapper functions and import it into my template module, just like any other module. So the helper would look something like this: package MyApp::Helper; use base 'Exporter'; our @EXPORT = ('wrap'); sub wrap (&;@) { my $code = shift; my %params = @_; html { head { title { outs "Hello, $params{user}!"} }; body { $code->(); div { outs 'This is the end, my friend' }; }; } }; I guess it'd be okay to have some sort of wrapper module that provides sugar for this. Maybe something like this: package MyApp::Helper; use base 'Template::Declare::Helper'; wrapper wrap sub { ... }; But it's just sugar, and I personally don't care that much. If you're interested, I can go ahead and implement it and provide a patch. Or I guess it could be a separate distribution. Either way, then one could just use it in a T::D module like so: use MyApp::Helper 'wrap'; use Template::Declare::Tags; template inner => sub { wrap { h1 { outs "Hello, Jesse, s'up?" }; } user => 'Jesse'; }; Or perhaps: use MyApp::Helper; template inner => sub { show( '/wrap' => sub { h1 { outs "Hello, Jesse, s'up?" }; }, user => 'Jesse' ); }; But then really it's just another template that expects a sub argument, isn't it? Anyway, I'm fine to just write my own modules for specific apps, as above using Exporter, but thought I'd throw it out for you guys to chew over. Best, David
Hi Theory, Thanks a lot for this patch. Making it easier to define wrappers is definitely a big win! I really disliked the installation of wrappers in Template::Declare::Tags and automatically adding them to @EXPORT. It's too global for my taste. (Template::Declare's existing globalness is my biggest.. nay, only, complaint against it) Having a helper module that defines your wrappers is probably best. :) Thanks again! Shawn
Subject: Re: [rt.cpan.org #37624] Add Support for Wrappers
Date: Fri, 1 Aug 2008 14:41:25 -0700
To: bug-Template-Declare [...] rt.cpan.org
From: "David E. Wheeler" <dwheeler [...] cpan.org>
On Aug 1, 2008, at 14:19, Shawn M Moore via RT wrote: Show quoted text
> Having a helper module that defines your wrappers is probably best. :)
Um, so you decided to just commit my patch after all? I'm really not sure that it's the ideal approach… Best, David
On Fri Aug 01 17:41:43 2008, DWHEELER wrote: Show quoted text
> Um, so you decided to just commit my patch after all? I'm really not > sure that it's the ideal approach…
Oh, I see, you just stick it into the calling class. This is annoying, though, because then it can't easily be used in any other templating module. What it needs is some sugar to put the wrapper into an exporter in the calling package, so that then it can be imported into other packages. I'll take a look at doing that now… David
On Fri Aug 01 17:49:44 2008, DWHEELER wrote: Show quoted text
> Oh, I see, you just stick it into the calling class. This is annoying, > though, because then it can't easily be used in any other templating > module. What it needs is some sugar to put the wrapper into an exporter > in the calling package, so that then it can be imported into other
packages. Show quoted text
> > I'll take a look at doing that now…
Okay, here you go. This patch is against current svn. It modifies the behavior so that a module that defines wrappers should inherit from Template::Declare::Wrapper. The wrapper function then tweaks the inheritance in the calling module by shoving the new wrapper into %EXPORT_TAGS{wrapper} and @EXPORT_OK so that they can easily be imported. Of course, you can still get the original behavior of an unexported, packag-scoped wrapper by not inheriting from T::D::Wrapper and defining your wrappers in a BEGIN block. I think that this is a much cleaner solution, overall, although I'm not thrilled about the Exporter stuff. It's so ugly. But it works, and could be replaced with something more robust in the future, if necessary (maybe Exporter::Tidy, which is quite nice). Best, David
Index: t/wrappers.t =================================================================== --- t/wrappers.t (revision 5650) +++ t/wrappers.t (working copy) @@ -1,11 +1,13 @@ #!/usr/bin/perl -package MyApp::Templates; +package MyApp::Wrappers; use strict; use warnings; use Template::Declare::Tags; -use base 'Template::Declare'; +use base 'Template::Declare::Wrapper'; BEGIN { + $INC{'MyApp/Wrappers.pm'} = __FILE__; + wrapper wrap => sub { my $code = shift; my %params = @_; @@ -19,9 +21,24 @@ }; } +package MyApp::Templates; +use strict; +use warnings; +use Template::Declare::Tags; +use MyApp::Wrappers ':wrappers'; +use base 'Template::Declare'; + +BEGIN { + wrapper bq => sub { + my $code = shift; + blockquote { p { $code->() } }; + }; +} + template inner => sub { wrap { h1 { outs "Hello, Jesse, s'up?" }; + bq { outs outs 'Something profound' } } user => 'Jesse'; }; @@ -40,7 +57,9 @@ </head> <body> <h1>Hello, Jesse, s&#39;up?</h1> + <blockquote> + <p>Something profound</p> + </blockquote> <div>This is the end, my friend</div> </body> </html>', 'Should have the wrapped output'; - Index: lib/Template/Declare/Tags.pm =================================================================== --- lib/Template/Declare/Tags.pm (revision 5650) +++ lib/Template/Declare/Tags.pm (working copy) @@ -276,39 +276,60 @@ =head2 wrapper WRAPPERNAME => sub { 'Implementation' }; C<wrapper> declares a wrapper subroutine that can be called like a tag sub, -but can optionally take arguments to be passed to the wrapper sub. For -example, if you wanted to wrap all of the output of a template in the usual -HTML headers and footers, you can do something like this: +but can optionally take arguments to be passed to the wrapper sub. The idea is +to declare wrappers in one or more helper modules and then use them in your +template modules. For example, if you wanted to wrap all of the output of a +template in the usual HTML headers and footers, you can do something like +this: + package MyApp::Wrappers; + use Template::Declare::Tags; + use base 'Template::Declare::Wrapper'; + + wrapper wrap => sub { + my $code = shift; + my %params = @_; + html { + head { title { outs "Hello, $params{user}!"} }; + body { + $code->(); + div { outs 'This is the end, my friend' }; + }; + } + }; + +Now C<use> your wrapper module in a template module, passing in the special +":wrappers" tag which will import all the wrappers you've defined in that +module. Tempate::Declare::Wrapper is just using L<Exporter|Exporter>, so in +fact you could also import your wrappers by name. Here's an example that uses +the ":wrappers" tag. It also, coincidentally, creates its own, unexported +wrapper, C<bq>, that can just be used in the scope of the module (or by using +the fully-qualified function name in another module): + package MyApp::Templates; + use base 'Template::Declare'; + use MyApp::Wrappers ':wrappers'; use Template::Declare::Tags; - use base 'Template::Declare'; BEGIN { - wrapper wrap => sub { + wrapper bq => sub { my $code = shift; - my %params = @_; - html { - head { title { outs "Hello, $params{user}!"} }; - body { - $code->(); - div { outs 'This is the end, my friend' }; - }; - } + blockquote { p { $code->() } }; }; } - template inner => sub { + template howdy => sub { wrap { h1 { outs "Hello, Jesse, s'up?" }; + bq { outs 'Ask not what your country can do for you.' } } user => 'Jesse'; }; -Note how the C<wrap> wrapper function is available for calling after it has -been declared in a C<BEGIN> block. Also note how you can pass arguments to the -function after the closing brace (you don't need a comma there!). +Note how the C<bq> wrapper function is available for calling after it has been +declared in a C<BEGIN> block. Also note how you can pass arguments to wrapper +function calls after the closing brace (you don't need a comma there!). -The output from the "inner" template will look something like this: +The output from the "howdy" template will look something like this: <html> <head> @@ -316,6 +337,9 @@ </head> <body> <h1>Hello, Jesse, s&#39;up?</h1> + <blockquote> + <p>Something profound</p> + </blockquote> <div>This is the end, my friend</div> </body> </html> @@ -330,6 +354,21 @@ # Shove the code ref into the calling class. no strict 'refs'; *{"$template_class\::$wrapper_name"} = sub (&;@) { goto $coderef }; + + # Just return unless inheriting from Wrapper. + return unless $template_class->isa('Template::Declare::Wrapper'); + + # Make things exportabl from Wrapper by tweaking @EXPORT_OK and %EXPORT_TAGS. + if (my $ok = *{"$template_class\::EXPORT_OK"}{ARRAY}) { + push @{ $ok }, $wrapper_name; + } else { + *{"$template_class\::EXPORT_OK"} = [$wrapper_name ]; + } + if (my $tags = *{"$template_class\::EXPORT_TAGS"}{HASH}) { + push @{ $tags->{wrappers} ||= [] }, $wrapper_name; + } else { + *{"$template_class\::EXPORT_TAGS"} = { wrappers => [$wrapper_name ] }; + } } =head2 private template TEMPLATENAME => sub { 'Implementation' }; @@ -962,4 +1001,7 @@ } } +package Template::Declare::Wrapper; +use Exporter 'import'; + 1;
Not sure where things stand with this; it has been over a year since I looked at it. Is the wrapper stuff still in need of some love? Best, David
Resolved at the request of the requestor :)