File Coverage

lib/Web/Components/Role/Email.pm
Criterion Covered Total %
statement 49 49 100.0
branch 3 4 75.0
condition n/a
subroutine 17 17 100.0
pod 2 2 100.0
total 71 72 98.6


line stmt bran cond sub pod time code
1             package Web::Components::Role::Email;
2              
3 1     1   2365 use 5.010001;
  1         3  
4 1     1   5 use namespace::autoclean;
  1         1  
  1         8  
5 1     1   56 use version; our $VERSION = qv( sprintf '0.2.%d', q$Rev: 1 $ =~ /\d+/gmx );
  1         1  
  1         6  
6              
7 1     1   643 use Email::MIME;
  1         38420  
  1         27  
8 1     1   5 use Encode qw( encode );
  1         2  
  1         62  
9 1     1   4 use File::DataClass::Constants qw( EXCEPTION_CLASS TRUE );
  1         3  
  1         44  
10 1     1   4 use File::DataClass::Functions qw( ensure_class_loaded is_hashref );
  1         2  
  1         36  
11 1     1   4 use File::DataClass::IO;
  1         1  
  1         8  
12 1     1   461 use MIME::Types;
  1         3447  
  1         38  
13 1     1   5 use Scalar::Util qw( blessed weaken );
  1         1  
  1         36  
14 1     1   4 use Try::Tiny;
  1         1  
  1         40  
15 1     1   5 use Unexpected::Functions qw( Unspecified throw );
  1         1  
  1         8  
16 1     1   330 use Moo::Role;
  1         1  
  1         9  
17              
18             requires qw( config log );
19              
20             with 'Web::Components::Role::TT';
21              
22             # Private subroutines
23             my $_add_attachments = sub {
24             my ($args, $email) = @_;
25              
26             my $types = MIME::Types->new( only_complete => TRUE );
27             my $part = Email::MIME->create
28             ( attributes => $email->{attributes}, body => delete $email->{body} );
29              
30             $email->{parts} = [ $part ];
31              
32             for my $name (sort keys %{ $args->{attachments} }) {
33             my $path = io( $args->{attachments}->{ $name } )->binary->lock;
34             my $mime = $types->mimeTypeOf( my $file = $path->basename );
35             my $attr = { content_type => $mime->type,
36             encoding => $mime->encoding,
37             filename => $file,
38             name => $name };
39              
40             $part = Email::MIME->create( attributes => $attr, body => $path->all );
41             push @{ $email->{parts} }, $part;
42             }
43              
44             return;
45             };
46              
47             my $_make_f = sub {
48             my ($obj, $f) = @_; weaken $obj; return sub { $obj->$f( @_ ) };
49             };
50              
51             my $_stash_functions = sub {
52             my ($self, $obj, $stash, $funcs) = @_; defined $obj or return;
53              
54             $funcs //= []; $funcs->[ 0 ] or push @{ $funcs }, 'loc';
55              
56             for my $f (@{ $funcs }) { $stash->{ $f } = $_make_f->( $obj, $f ) }
57              
58             return;
59             };
60              
61             my $_get_email_body = sub {
62             my ($self, $args) = @_; my $obj = delete $args->{subprovider};
63              
64             exists $args->{body} and defined $args->{body} and return $args->{body};
65              
66             $args->{template} or throw Unspecified, [ 'template' ];
67              
68             my $stash = $args->{stash} //= {}; $stash->{page} //= {};
69              
70             $stash->{page}->{layout} //= $args->{template};
71              
72             $_stash_functions->( $self, $obj, $stash, $args->{functions} );
73              
74             return $self->render_template( $stash );
75             };
76              
77             my $_create_email = sub {
78             my ($self, $args) = @_; $args->{email} and return $args->{email};
79              
80             my $conf = $self->config;
81             my $attr = $conf->can( 'email_attr' ) ? $conf->email_attr : {};
82             my $email = { attributes => { %{ $attr }, %{ $args->{attributes} // {}}}};
83             my $from = $args->{from} or throw Unspecified, [ 'from' ];
84             my $to = $args->{to } or throw Unspecified, [ 'to' ];
85             my $subject = encode( 'MIME-Header', $args->{subject} // 'No subject' );
86             my $encoding = $email->{attributes}->{charset};
87              
88             $email->{header} = [ From => $from, To => $to, Subject => $subject ];
89             $email->{body } = $_get_email_body->( $self, $args );
90              
91             $encoding and $email->{body} = encode( $encoding, $email->{body} );
92              
93             exists $args->{attachments} and $_add_attachments->( $args, $email );
94              
95             return Email::MIME->create( %{ $email } );
96             };
97              
98             my $_transport_email = sub {
99             my ($self, $args) = @_; $args->{email} or throw Unspecified, [ 'email' ];
100              
101             my $attr = {}; my $conf = $self->config;
102              
103             $conf->can( 'transport_attr' ) and $attr = { %{ $conf->transport_attr } };
104              
105             exists $args->{transport_attr}
106             and $attr = { %{ $attr }, %{ $args->{transport_attr} } };
107             exists $args->{host} and $attr->{host} = $args->{host};
108              
109             $attr->{host} //= 'localhost'; my $class = delete $attr->{class};
110              
111             $class = $args->{mailer} // $class // 'SMTP';
112              
113             if ('+' eq substr $class, 0, 1) { $class = substr $class, 1 }
114             else { $class = "Email::Sender::Transport::${class}" }
115              
116             ensure_class_loaded $class;
117              
118             my $mailer = $class->new( $attr );
119             my $send_args = { from => $args->{from}, to => $args->{to} };
120             my $result;
121              
122             try { $result = $mailer->send( $args->{email}, $send_args ) }
123             catch { throw $_ };
124              
125             $result->can( 'failure' ) and throw $result->message;
126              
127             (blessed $result and $result->isa( 'Email::Sender::Success' ))
128             or throw 'Send failed: [_1]', [ $result ];
129              
130             return ($result->can( 'message' ) and defined $result->message
131             and length $result->message) ? $result->message : 'OK Message sent';
132             };
133              
134             # Public methods
135             sub send_email {
136 13     13 1 40273 my ($self, @args) = @_;
137              
138 13 100       37 defined $args[ 0 ] or throw Unspecified, [ 'email args' ];
139              
140 12 50       33 my $args = (is_hashref $args[ 0 ]) ? $args[ 0 ] : { @args };
141              
142 12         74 $args->{email} = $_create_email->( $self, $args );
143              
144 7         7566 return $_transport_email->( $self, $args );
145             }
146              
147             sub try_to_send_email {
148 2     2 1 2693 my ($self, @args) = @_; my $res;
  2         2  
149              
150 2     2   49 try { $res = $self->send_email( @args ) }
151 2     1   15 catch { $self->log->error( $res = $_ ) };
  1         1639  
152              
153 2         20 return $res;
154             }
155              
156             1;
157              
158             __END__