File Coverage

blib/lib/Courriel/Part/Multipart.pm
Criterion Covered Total %
statement 30 41 73.1
branch 0 4 0.0
condition n/a
subroutine 11 14 78.5
pod 4 4 100.0
total 45 63 71.4


line stmt bran cond sub pod time code
1             package Courriel::Part::Multipart;
2              
3 7     7   671 use strict;
  7         12  
  7         263  
4 7     7   35 use warnings;
  7         11  
  7         255  
5 7     7   33 use namespace::autoclean;
  7         9  
  7         71  
6              
7             our $VERSION = '0.43';
8              
9 7     7   627 use Courriel::HeaderAttribute;
  7         8  
  7         200  
10 7     7   23 use Courriel::Helpers qw( unique_boundary );
  7         10  
  7         352  
11 7     7   31 use Courriel::Types qw( ArrayRef NonEmptyStr Part );
  7         10  
  7         139  
12 7     7   68224 use Email::MessageID;
  7         5320  
  7         233  
13              
14 7     7   44 use Moose;
  7         14  
  7         63  
15 7     7   43643 use MooseX::StrictConstructor;
  7         17  
  7         68  
16              
17             with 'Courriel::Role::Part';
18              
19             has preamble => (
20             is => 'ro',
21             isa => NonEmptyStr,
22             predicate => 'has_preamble',
23             );
24              
25             has epilogue => (
26             is => 'ro',
27             isa => NonEmptyStr,
28             predicate => 'has_epilogue',
29             );
30              
31             has _parts => (
32             traits => ['Array'],
33             isa => ArrayRef [Part],
34             init_arg => 'parts',
35             required => 1,
36             handles => {
37             parts => 'elements',
38             part_count => 'count',
39             },
40             );
41              
42             sub BUILD {
43             my $self = shift;
44             my $p = shift;
45              
46             my $boundary = delete $p->{boundary} // unique_boundary;
47             my $existing = $self->content_type->attribute('boundary');
48              
49             ## no critic (Subroutines::ProtectPrivateSubs)
50             $self->content_type->_set_attribute(
51             boundary => Courriel::HeaderAttribute->new(
52             name => ( $existing ? $existing->name : 'boundary' ),
53             value => $boundary,
54             )
55             );
56             ## use critic
57              
58             $_->_set_container($self) for $self->parts;
59              
60             return;
61             }
62              
63 0     0 1 0 sub is_attachment {0}
64 0     0 1 0 sub is_inline {0}
65 2     2 1 75 sub is_multipart {1}
66              
67             ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
68             sub _stream_content {
69 0     0   0 my $self = shift;
70 0         0 my $output = shift;
71              
72 0 0       0 $output->( $self->preamble, $Courriel::Helpers::CRLF )
73             if $self->has_preamble;
74              
75 0         0 for my $part ( $self->parts ) {
76 0         0 $output->(
77             $Courriel::Helpers::CRLF,
78             '--',
79             $self->boundary,
80             $Courriel::Helpers::CRLF,
81             );
82              
83 0         0 $part->stream_to( output => $output );
84             }
85              
86 0         0 $output->(
87             $Courriel::Helpers::CRLF,
88             '--',
89             $self->boundary,
90             '--',
91             $Courriel::Helpers::CRLF
92             );
93              
94 0 0       0 $output->( $self->epilogue, $Courriel::Helpers::CRLF )
95             if $self->has_epilogue;
96              
97 0         0 return;
98             }
99             ## use critic
100              
101             sub boundary {
102 2     2 1 10 my $self = shift;
103              
104 2         57 return $self->content_type->attribute_value('boundary');
105             }
106              
107             __PACKAGE__->meta->make_immutable;
108              
109             1;
110              
111             # ABSTRACT: A part which contains other parts
112              
113             __END__
114              
115             =pod
116              
117             =encoding UTF-8
118              
119             =head1 NAME
120              
121             Courriel::Part::Multipart - A part which contains other parts
122              
123             =head1 VERSION
124              
125             version 0.43
126              
127             =head1 SYNOPSIS
128              
129             my $headers = $part->headers;
130             my $ct = $part->content_type;
131              
132             for my $subpart ( $part->parts ) { ... }
133              
134             =head1 DESCRIPTION
135              
136             This class represents a multipart email part which contains other parts.
137              
138             =head1 API
139              
140             This class provides the following methods:
141              
142             =head2 Courriel::Part::Multipart->new( ... )
143              
144             This method creates a new part object. It accepts the following parameters:
145              
146             =over 4
147              
148             =item * parts
149              
150             An array reference of part objects (either Single or Multipart). This is
151             required, but could be empty.
152              
153             =item * content_type
154              
155             A L<Courriel::Header::ContentType> object. This defaults to one with a mime type of
156             "multipart/mixed".
157              
158             =item * boundary
159              
160             The part boundary. If none is provided, a unique value will be generated.
161              
162             =item * preamble
163              
164             Content that appears before the first part boundary. This will be seen by
165             email clients that don't understand multipart messages.
166              
167             =item * epilogue
168              
169             Content that appears after the final part boundary. The spec allows for this,
170             but it's probably not very useful.
171              
172             =item * headers
173              
174             A L<Courriel::Headers> object containing headers for this part.
175              
176             =back
177              
178             =head2 $part->parts()
179              
180             Returns an array (not a reference) of the parts this part contains.
181              
182             =head2 $part->part_count()
183              
184             Returns the number of parts this part contains.
185              
186             =head2 $part->boundary()
187              
188             Returns the part boundary.
189              
190             =head2 $part->mime_type()
191              
192             Returns the mime type for this part.
193              
194             =head2 $part->content_type()
195              
196             Returns the L<Courriel::Header::ContentType> object for this part.
197              
198             =head2 $part->headers()
199              
200             Returns the L<Courriel::Headers> object for this part.
201              
202             =head2 $part->is_inline(), $part->is_attachment()
203              
204             These methods always return false, but exist for the sake of providing a
205             consistent API between Single and Multipart part objects.
206              
207             =head2 $part->is_multipart()
208              
209             Returns true.
210              
211             =head2 $part->preamble()
212              
213             The preamble as passed to the constructor.
214              
215             =head2 $part->epilogue()
216              
217             The epilogue as passed to the constructor.
218              
219             =head2 $part->container()
220              
221             Returns the L<Courriel> or L<Courriel::Part::Multipart> object to which this
222             part belongs, if any. This is set when the part is added to another object.
223              
224             =head2 $part->stream_to( output => $output )
225              
226             This method will send the stringified part to the specified output. The
227             output can be a subroutine reference, a filehandle, or an object with a
228             C<print()> method. The output may be sent as a single string, as a list of
229             strings, or via multiple calls to the output.
230              
231             =head2 $part->as_string()
232              
233             Returns the part as a string, along with its headers. Lines will be terminated
234             with "\r\n".
235              
236             =head1 ROLES
237              
238             This class does the C<Courriel::Role::Part> and C<Courriel::Role::Streams>
239             roles.
240              
241             =head1 SUPPORT
242              
243             Bugs may be submitted through L<the RT bug tracker|http://rt.cpan.org/Public/Dist/Display.html?Name=Courriel>
244             (or L<bug-courriel@rt.cpan.org|mailto:bug-courriel@rt.cpan.org>).
245              
246             I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
247              
248             =head1 AUTHOR
249              
250             Dave Rolsky <autarch@urth.org>
251              
252             =head1 COPYRIGHT AND LICENSE
253              
254             This software is Copyright (c) 2016 by Dave Rolsky.
255              
256             This is free software, licensed under:
257              
258             The Artistic License 2.0 (GPL Compatible)
259              
260             =cut