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   3112 use 5.010001;
  1         2  
4 1     1   4 use namespace::autoclean;
  1         2  
  1         9  
5 1     1   65 use version; our $VERSION = qv( sprintf '0.3.%d', q$Rev: 1 $ =~ /\d+/gmx );
  1         1  
  1         5  
6              
7 1     1   769 use Email::MIME;
  1         47017  
  1         38  
8 1     1   8 use Encode qw( encode );
  1         2  
  1         83  
9 1     1   9 use File::DataClass::Constants qw( EXCEPTION_CLASS TRUE );
  1         2  
  1         60  
10 1     1   5 use File::DataClass::Functions qw( ensure_class_loaded is_hashref );
  1         2  
  1         40  
11 1     1   4 use File::DataClass::IO;
  1         1  
  1         9  
12 1     1   676 use MIME::Types;
  1         3395  
  1         58  
13 1     1   5 use Scalar::Util qw( blessed weaken );
  1         2  
  1         43  
14 1     1   5 use Try::Tiny;
  1         2  
  1         59  
15 1     1   4 use Unexpected::Functions qw( Unspecified throw );
  1         1  
  1         9  
16 1     1   380 use Moo::Role;
  1         1  
  1         8  
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 $encoding = $email->{attributes}->{charset};
86             my $subject = $args->{subject} // 'No subject';
87              
88             try { $subject = encode( 'MIME-Header', $subject, TRUE ) }
89             catch { throw 'Cannot encode subject as MIME-Header: [_1]', [ $_ ] };
90              
91             $email->{header} = [ From => $from, To => $to, Subject => $subject ];
92             $email->{body } = $_get_email_body->( $self, $args );
93              
94             try {
95             $encoding and $email->{body} = encode( $encoding, $email->{body}, TRUE );
96             }
97             catch { throw 'Cannot encode body as [_1]: [_2]', [ $encoding, $_ ] };
98              
99             exists $args->{attachments} and $_add_attachments->( $args, $email );
100              
101             return Email::MIME->create( %{ $email } );
102             };
103              
104             my $_transport_email = sub {
105             my ($self, $args) = @_; $args->{email} or throw Unspecified, [ 'email' ];
106              
107             my $attr = {}; my $conf = $self->config;
108              
109             $conf->can( 'transport_attr' ) and $attr = { %{ $conf->transport_attr } };
110              
111             exists $args->{transport_attr}
112             and $attr = { %{ $attr }, %{ $args->{transport_attr} } };
113             exists $args->{host} and $attr->{host} = $args->{host};
114              
115             $attr->{host} //= 'localhost'; my $class = delete $attr->{class};
116              
117             $class = $args->{mailer} // $class // 'SMTP';
118              
119             if ('+' eq substr $class, 0, 1) { $class = substr $class, 1 }
120             else { $class = "Email::Sender::Transport::${class}" }
121              
122             ensure_class_loaded $class;
123              
124             my $mailer = $class->new( $attr );
125             my $send_args = { from => $args->{from}, to => $args->{to} };
126             my $result;
127              
128             try { $result = $mailer->send( $args->{email}, $send_args ) }
129             catch { throw $_ };
130              
131             $result->can( 'failure' ) and throw $result->message;
132              
133             (blessed $result and $result->isa( 'Email::Sender::Success' ))
134             or throw 'Send failed: [_1]', [ $result ];
135              
136             return ($result->can( 'message' ) and defined $result->message
137             and length $result->message) ? $result->message : 'OK Message sent';
138             };
139              
140             # Public methods
141             sub send_email {
142 13     13 1 48208 my ($self, @args) = @_;
143              
144 13 100       52 defined $args[ 0 ] or throw Unspecified, [ 'email args' ];
145              
146 12 50       50 my $args = (is_hashref $args[ 0 ]) ? $args[ 0 ] : { @args };
147              
148 12         90 $args->{email} = $_create_email->( $self, $args );
149              
150 7         11613 return $_transport_email->( $self, $args );
151             }
152              
153             sub try_to_send_email {
154 2     2 1 2964 my ($self, @args) = @_; my $res;
  2         3  
155              
156 2     2   58 try { $res = $self->send_email( @args ) }
157 2     1   15 catch { $self->log->error( $res = $_ ) };
  1         3079  
158              
159 2         31 return $res;
160             }
161              
162             1;
163              
164             __END__