refactor of the API and mainly Catalyst::View::Email::Template
Alexander Hartmaier [Fri, 19 Oct 2007 21:42:40 +0000 (21:42 +0000)]
Changes
lib/Catalyst/View/Email.pm
lib/Catalyst/View/Email/Template.pm
t/07mason.t
t/lib/TestApp.pm
t/lib/TestApp/Controller/Root.pm
t/lib/TestApp/View/Email.pm
t/lib/TestApp/View/Email/Template.pm
t/lib/TestApp/View/Mason.pm

diff --git a/Changes b/Changes
index f5734d8..812e6a8 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,5 +1,7 @@
 Revision history for Perl extension Catalyst::View::Email.
 
+0.07    - refactored by Alexander Hartmaier with slight api changes
+
 0.06
         - Fixing some slight issues with configuration not being handled
           appropriately (thanks dwc and mst)
index 4af0484..8b4ea33 100644 (file)
@@ -11,9 +11,10 @@ use Email::MIME::Creator;
 
 use base qw|Catalyst::View|;
 
-our $VERSION = '0.08';
+our $VERSION = '0.07';
 
-__PACKAGE__->mk_accessors(qw(sender stash_key content_type mailer));
+#__PACKAGE__->mk_accessors(qw(sender stash_key content_type mailer));
+__PACKAGE__->mk_accessors(qw(stash_key content_type mailer));
 
 =head1 NAME
 
@@ -32,7 +33,7 @@ In your app configuration (example in L<YAML>):
         stash_key: email
         content_type: text/plain 
         sender:
-            method:     SMTP
+            mailer: SMTP
             # mailer_args is passed directly into Email::Send 
             mailer_args:
                 Host:       smtp.example.com # defaults to localhost
@@ -48,7 +49,10 @@ somewhere and not be delivered.
 =cut
 
 __PACKAGE__->config(
-    stash_key => 'email',
+    stash_key   => 'email',
+    default     => {
+        content_type    => 'text/html',
+    },
 );
 
 =head1 SENDING EMAIL
@@ -108,7 +112,8 @@ have L<Catalyst::View::Email::Template> for use.  You can also toggle
 this as being used by setting up your configuration to look like this:
 
     View::Email:
-        default_view: TT
+        default:
+            view: TT
 
 Then, Catalyst::View::Email will forward to your View::TT by default.
 
@@ -119,31 +124,31 @@ sub new {
 
     my ( $c, $arguments ) = @_;
 
-    my $mailer = Email::Send->new;
+    my $sender = Email::Send->new;
 
-    if ( my $method = $self->sender->{method} ) {
-        croak "$method is not supported, see Email::Send"
-            unless $mailer->mailer_available($method);
-        $mailer->mailer($method);
+    if ( my $mailer = $self->{sender}->{mailer} ) {
+        croak "$mailer is not supported, see Email::Send"
+            unless $sender->mailer_available($mailer);
+        $sender->mailer($mailer);
     } else {
         # Default case, run through the most likely options first.
         for ( qw/SMTP Sendmail Qmail/ ) {
-            $mailer->mailer($_) and last if $mailer->mailer_available($_);
+            $sender->mailer($_) and last if $sender->mailer_available($_);
         }
     }
 
-    if ( my $args = $self->sender->{mailer_args} ) {
+    if ( my $args = $self->{sender}->{mailer_args} ) {
         if ( ref $args eq 'HASH' ) {
-            $mailer->mailer_args([ %$args ]);
+            $sender->mailer_args([ %$args ]);
         }
         elsif ( ref $args eq 'ARRAY' ) {
-            $mailer->mailer_args($args);
+            $sender->mailer_args($args);
         } else {
             croak "Invalid mailer_args specified, check pod for Email::Send!";
         }
     }
 
-    $self->mailer($mailer);
+    $self->mailer($sender);
 
     return $self;
 }
index e213a10..2d58c38 100644 (file)
@@ -5,14 +5,13 @@ use strict;
 
 use Class::C3;
 use Carp;
+use Scalar::Util qw( blessed );
 
 use Email::MIME::Creator;
 
 use base qw|Catalyst::View::Email|;
 
-our $VERSION = '0.08';
-
-__PACKAGE__->mk_accessors( qw(default_view template_prefix) );
+our $VERSION = '0.07';
 
 =head1 NAME
 
@@ -23,53 +22,65 @@ Catalyst::View::Email::Template - Send Templated Email from Catalyst
 Sends Templated mail, based upon your Default View.  Will capture the output
 of the rendering path, slurps in based on mime-types and assembles a multi-part
 email and sends it out.
+It uses Email::MIME to create the mail.
 
 =head2 CONFIGURATION
 
     View::Email::Template:
-        # Set it up so if you have multiple parts, they're alternatives.
-        # This is on the top-level message, not the individual parts.
-        content_type: multipart/alternative
         # Optional prefix to look somewhere under the existing configured
-        # template  paths.
+        # template  paths. Default is none.
         template_prefix: email
-        # Where to look in the stash for the email information
+        # Where to look in the stash for the email information.
+        # 'email' is the default, so you don't have to specify it.
         stash_key: email
+        # Define the defaults for the mail
+        default:
+            # Defines the default content type (mime type) for every template.
+            content_type: text/html
+            # Defines the default charset for every MIME part with the content
+            # type 'text'.
+            # According to RFC2049 such a MIME part without a charset should
+            # be treated as US-ASCII by the mail client.
+            # If the charset is not set it won't be set for all MIME parts
+            # without an overridden one.
+            charset: utf-8
+            # Defines the default view which is used to render the templates.
+            view: TT
         # Setup how to send the email
+        # all those options are passed directly to Email::Send
         sender:
-            method:     SMTP
-            host:       smtp.myhost.com
-            username:   username
-            password:   password
+            mailer: SMTP
+            mailer_args:
+                Host:       smtp.example.com # defaults to localhost
+                username:   username
+                password:   password
 
 =head1 SENDING EMAIL
 
-Sending email is just setting up your stash key, and forwarding to the view.
+Sending email is just setting up your defaults, the stash key and forwarding to the view.
 
     $c->stash->{email} = {
         to      => 'jshirley@gmail.com',
         from    => 'no-reply@foobar.com',
         subject => 'I am a Catalyst generated email',
-        # Specify which templates to include
-        templates => [
-            qw{text_plain/test.tt},
-            qw{text_html/test.tt}
-        ]
+        template => 'test.tt',
     };
     $c->forward('View::Email::Template');
 
-Alternatively if you want more control over your templates you can use the following idiom :-
+Alternatively if you want more control over your templates you can use the following idiom
+to override the defaults:
 
     templates => [
-               {       view => 'TT', 
-                       template => 'email/test.html.tt',
-                       content_type => 'text/html'
-               },
-               {       view => 'TT', 
-                       template => 'email/test.plain.tt',
-                       content_type => 'text/plain'
-               }
-
+        {
+            template        => 'email/test.html.tt',
+            content_type    => 'text/html',
+            view            => 'TT', 
+        },
+        {
+            template        => 'email/test.plain.mason',
+            content_type    => 'text/plain',
+            view            => 'Mason', 
+        }
     ]
 
 
@@ -77,6 +88,9 @@ If it fails $c->error will have the error message.
 
 =cut
 
+# here the defaults of Catalyst::View::Email are extended by the additional
+# ones Template.pm needs.
+
 __PACKAGE__->config(
     template_prefix => '',
 );
@@ -94,97 +108,110 @@ __PACKAGE__->config(
 # into an Email::MIME container.  The mime-type will be stupidly guessed with
 # the subdir on the template.
 #
-# TODO: Make this unretarded.
-#
-sub process {
-    my ( $self, $c ) = @_;
 
-    my $stash_key = $self->stash_key || 'email';
+# Set it up so if you have multiple parts, they're alternatives.
+# This is on the top-level message, not the individual parts.
+#multipart/alternative
 
-    croak "No template specified for rendering"
-        unless $c->stash->{$stash_key}->{template} or
-                $c->stash->{$stash_key}->{templates};
-    # Where to look
-    my $template_prefix = $self->template_prefix;
-    my @templates = ();
-
-    if ( $c->stash->{$stash_key}->{templates} && !ref $c->stash->{$stash_key}->{templates}[0]) {
-        push @templates, map {
-            join('/', $template_prefix, $_);
-        } @{$c->stash->{$stash_key}->{templates}};
-
-    } elsif($c->stash->{$stash_key}->{template}) {
-        push @templates, join('/', $template_prefix,
-            $c->stash->{$stash_key}->{template});
-    }
-   
-    my $default_view = $c->view( $self->default_view );
+sub _validate_view {
+    my ($self, $view) = @_;
+    
+    croak "Email::Template's configured view '$view' isn't an object!"
+        unless (blessed($view));
+
+    croak "Email::Template's configured view '$view' isn't an Catalyst::View!"
+        unless ($view->isa('Catalyst::View'));
+
+    croak "Email::Template's configured view '$view' doesn't have a render method!"
+        unless ($view->can('render'));
+}
 
-    unless ( $default_view->can('render') ) {
-        croak "Email::Template's configured view does not have a render method!";
+sub _generate_part {
+    my ($self, $c, $attrs) = @_;
+    
+    my $template_prefix         = $self->{template_prefix};
+    my $default_view            = $self->{default}->{view};
+    my $default_content_type    = $self->{default}->{content_type};
+
+    my $view = $c->view($attrs->{view} || $default_view);
+    # validate the per template view
+    $self->_validate_view($view);
+#    $c->log->debug("VIEW: $view");
+    
+    # prefix with template_prefix if configured
+    my $template = $template_prefix ne '' ? join('/', $template_prefix, $attrs->{template}) : $attrs->{template};
+            
+    my $content_type = $attrs->{content_type} || $default_content_type;
+    
+    # render the email part
+    my $output = $view->render( $c, $template, { 
+        content_type    => "$content_type",
+        stash_key       => $self->stash_key,
+       %{$c->stash},
+    });
+                               
+    if (ref $output) {
+       croak $output->can('as_string') ? $output->as_string : $output;
     }
+       
+    return Email::MIME->create(
+        attributes => {
+           content_type => "$content_type",
+       },
+       body => $output,
+    );
+}
 
-    #$c->log->_dump($default_view->config);
+sub process {
+    my ( $self, $c ) = @_;
 
-    my @parts = (); 
-    foreach my $template ( @templates ) {
-        $template =~ s#^/+##; # Make sure that we don't have an absolute path.
-        # This seems really stupid to me... argh.  will give me nightmares!
-        my $template_path = $template;
-            $template_path =~ s#^$template_prefix/##;
-        my ( $content_type, $extra ) = split('/', $template_path);
-        if ( $extra ) {
-            $content_type ||= 'text/plain';
-            $content_type =~  s#_#/#;
-        } else {
-            $content_type = 'text/plain';
-        }
+    # validate here so _generate_part doesn't have to do it for every part
+    my $stash_key = $self->{stash_key};
+    croak "Email::Template's stash_key isn't defined!"
+        if ($stash_key eq '');
+    
+    # don't validate template_prefix
 
-        my $output = $default_view->render( $c, $template, {
-            content_type => $content_type,
-            stash_key => $self->stash_key,
-            %{$c->stash},
-        });
+    # the default view is validated on use later anyways...
+    # but just to be sure even if not used
+#    $self->_validate_view($c->view($self->{default}->{view}));
 
-        # Got a ref, not a scalar.  An error!
-        if ( ref $output ) {
-            croak $output->can("as_string") ? $output->as_string : $output;
-        }
-        push @parts, Email::MIME->create(
-            attributes => {
-                content_type => $content_type
-            },
-            body => $output
-        );
-    }
+#    $c->log->debug("SELF: $self");
+#    $c->log->debug('DEFAULT VIEW: ' . $self->{default}->{view});
 
-       #add user parts :-
-       if ( $c->stash->{$stash_key}->{'templates'} && ref $c->stash->{$stash_key}->{templates}[0] ) {
-               foreach my $part (@{$c->stash->{$stash_key}->{'templates'}}) {
-                       my $view = $c->view($part->{'view'} || $self->config->{default_view});
+    # the content type should be validated by Email::MIME::Creator
 
-                   my $content_type = $part->{'content_type'} || 'text/plain';
-                       unless ( $view->can('render') ) {
-                       croak "Part does not have valid render view";
-                   }
+    croak "No template specified for rendering"
+        unless $c->stash->{$stash_key}->{template}
+            or $c->stash->{$stash_key}->{templates};
+    
+    # this array holds the Email::MIME objects
+    # in case of the simple api only one
+    my @parts = (); 
 
-               my $output = $view->render( $c, $part->{'template'}, { 
-                               'content_type' => $content_type, 
-                               %{$c->stash} });
-                               
-               if ( ref $output ) {
-                   croak $output->can("as_string") ? $output->as_string : $output;
-               }
-       
-               push @parts, Email::MIME->create(
-                   attributes => {
-                       content_type => $content_type
-                   },
-                   body => $output
-               );
-               }
+    # now find out if the single or multipart api was used
+    # prefer the multipart one
+    
+    # multipart api
+    if ($c->stash->{$stash_key}->{templates}
+        && ref $c->stash->{$stash_key}->{templates} eq 'ARRAY'
+        && ref $c->stash->{$stash_key}->{templates}[0] eq 'HASH') {
+        # loop through all parts of the mail
+        foreach my $part (@{$c->stash->{$stash_key}->{templates}}) {
+            push @parts, $self->_generate_part($c, {
+                view            => $part->{view},
+                template        => $part->{template},
+                content_type    => $part->{content_type},
+            });
        }
-
+    }
+    # single part api
+    elsif($c->stash->{$stash_key}->{template}) {
+        push @parts, $self->_generate_part($c, {
+            template    => $c->stash->{$stash_key}->{template},
+        });
+    }
+    
     delete $c->stash->{$stash_key}->{body};
     $c->stash->{$stash_key}->{parts} ||= [];
     push @{$c->stash->{$stash_key}->{parts}}, @parts;
@@ -224,6 +251,8 @@ J. Shirley <jshirley@gmail.com>
 
 Simon Elliott <cpan@browsing.co.uk>
 
+Alexander Hartmaier <alex_hartmaier@hotmail.com>
+
 =head1 LICENSE
 
 This library is free software, you can redistribute it and/or modify it under
@@ -232,4 +261,3 @@ the same terms as Perl itself.
 =cut
 
 1;
-
index afa9190..3bfeae3 100644 (file)
@@ -15,7 +15,7 @@ plan tests => 10;
 
 use_ok('Catalyst::Test', 'TestApp');
 
-TestApp->config->{default_view} = 'mason';
+TestApp->config->{default}->{view} = 'mason';
 
 my $response;
 my $time = time;
index 3789de3..8443301 100644 (file)
@@ -6,16 +6,18 @@ use FindBin;
 
 TestApp->config(
     root => "$FindBin::Bin/root",
-    default_view => 'TT',
     'View::Email::AppConfig' => {
         sender => {
-            method => 'Test',
+            mailer => 'Test',
         },
     },
     'View::Email::Template::AppConfig' => {
         stash_key => 'template_email',
         sender => {
-            method => 'Test',
+            mailer => 'Test',
+        },
+        default => {
+            view => 'TT',
         },
     },
 );
index d96fc22..6cabe66 100644 (file)
@@ -62,11 +62,19 @@ sub template_email : Global('template_email') {
         to      => 'test-email@example.com',
         from    => 'no-reply@example.com',
         subject => 'Just a test',
-        content_type => 'multipart/alternative',
+#        content_type => 'multipart/alternative',
         templates => [
-            qw{text_plain/test.tt},
-            qw{text_html/test.tt}
-        ]
+            {
+                view            => 'TT',
+                template        => 'text_plain/test.tt',
+                content_type    => 'text/plain',
+            },
+            {
+                view            => 'TT',
+                template        => 'text_html/test.tt',
+                content_type    => 'text/html',
+            },
+        ],
     };
 
     $c->forward('TestApp::View::Email::Template');    
@@ -88,11 +96,17 @@ sub template_email_app_config : Global('template_email_app_config') {
         to      => 'test-email@example.com',
         from    => 'no-reply@example.com',
         subject => 'Just a test',
-        content_type => 'multipart/alternative',
+#        content_type => 'multipart/alternative',
         templates => [
-            qw{text_plain/test.tt},
-            qw{text_html/test.tt}
-        ]
+            {
+                template        => 'text_plain/test.tt',
+                content_type    => 'text/plain',
+            },
+            {
+                template        => 'text_html/test.tt',
+                content_type    => 'text/html',
+            },
+        ],
     };
 
     $c->forward('TestApp::View::Email::Template::AppConfig');
@@ -116,9 +130,15 @@ sub mason_email : Global('mason_email') {
         subject => 'Just a test',
         content_type => 'multipart/alternative',
         templates => [
-            qw{text_plain/test.m},
-            qw{text_html/test.m}
-        ]
+            {
+                template        => 'text_plain/test.tt',
+                content_type    => 'text/plain',
+            },
+            {
+                template        => 'text_html/test.tt',
+                content_type    => 'text/html',
+            },
+        ],
     };
 
     $c->forward('TestApp::View::Email::Template');    
index dc737a3..d3ca6f8 100644 (file)
@@ -7,7 +7,7 @@ use base 'Catalyst::View::Email';
 
 __PACKAGE__->config(
     sender => {
-        method => 'Test'
+        mailer => 'Test'
     },
 );
 
index e7d6ee2..a594d07 100644 (file)
@@ -6,7 +6,7 @@ use base 'Catalyst::View::Email::Template';
 
 __PACKAGE__->config(
     sender => {
-        method => 'Test'
+        mailer => 'Test'
     },
     stash_key       => 'email',
     template_prefix => ''
index 070749f..d3a3b34 100644 (file)
@@ -4,6 +4,6 @@ package # Hide me.
 use strict;
 eval "use base 'Catalyst::View::Mason';";
 
-__PACKAGE__->config( data_dir => TestApp->path_to('cache')->stringify );
+eval "__PACKAGE__->config( data_dir => TestApp->path_to('cache')->stringify );";
 
 1;