File Coverage

lib/HTTP/Promise/Headers/Range.pm
Criterion Covered Total %
statement 70 73 95.8
branch 11 16 68.7
condition 7 10 70.0
subroutine 20 22 90.9
pod 5 5 100.0
total 113 126 89.6


line stmt bran cond sub pod time code
1             ##----------------------------------------------------------------------------
2             ## Asynchronous HTTP Request and Promise - ~/lib/HTTP/Promise/Headers/Range.pm
3             ## Version v0.1.0
4             ## Copyright(c) 2022 DEGUEST Pte. Ltd.
5             ## Author: Jacques Deguest <jack@deguest.jp>
6             ## Created 2022/05/08
7             ## Modified 2022/05/08
8             ## All rights reserved.
9             ##
10             ##
11             ## This program is free software; you can redistribute it and/or modify it
12             ## under the same terms as Perl itself.
13             ##----------------------------------------------------------------------------
14             package HTTP::Promise::Headers::Range;
15             BEGIN
16             {
17 3     3   3376 use strict;
  3         7  
  3         94  
18 3     3   16 use warnings;
  3         8  
  3         91  
19 3     3   17 use warnings::register;
  3         5  
  3         413  
20 3     3   18 use parent qw( HTTP::Promise::Headers::Generic );
  3         6  
  3         15  
21 3     3   276 our $VERSION = 'v0.1.0';
22             };
23              
24 3     3   19 use strict;
  3         4  
  3         55  
25 3     3   14 use warnings;
  3         6  
  3         2046  
26              
27             sub init
28             {
29 5     5 1 349242 my $self = shift( @_ );
30 5         234 $self->{ranges} = [];
31 5         21 $self->{unit} = 'bytes';
32 5 50 66     55 @_ = () if( @_ == 1 && $self->_is_a( $_[0] => 'Module::Generic::Null' ) );
33 5 100       116 if( @_ )
34             {
35 4         11 my $this = shift( @_ );
36 4         7 my( $unit, $ref );
37 4 50       26 if( $self->_is_array( $this ) )
38             {
39 0         0 $ref = $this;
40             }
41             else
42             {
43 4 50 33     61 return( $self->error( "Bad argument provided '$this'. You can provide either an array reference or a string." ) ) if( ref( $this ) && !overload::Method( $this, '""' ) );
44 4         68 $this =~ s/^[[:blank:]\h]+|[[:blank:]\h]+$//g;
45 4 50       45 if( $this =~ s/^([a-zA-Z][a-zA-Z\_]+)[[:blank:]\h]*=[[:blank:]\h]*// )
46             {
47 4         18 $unit = $1;
48             }
49 4         28 $ref = [split( /[[:blank:]\h]*,[[:blank:]\h]*/, "$this" )];
50             }
51 4         26 my $ranges = $self->new_array;
52 4         121 foreach( @$ref )
53             {
54 8         80 my( $start, $end ) = split( /[[:blank:]\h]*-[[:blank:]\h]*/ );
55 8 100       36 $start = undef if( !length( $start ) );
56 8 100       21 $end = undef if( !length( $end ) );
57 8         61 my $range = HTTP::Promise::Headers::Range::StartEnd->new( $start, $end );
58 8         549 $ranges->push( $range );
59             }
60 4         75 $self->ranges( $ranges );
61             }
62 5         3744 $self->{_init_strict_use_sub} = 1;
63 5 50       45 $self->SUPER::init( @_ ) || return( $self->pass_error );
64 5         38 $self->_field_name( 'Range' );
65 5         4138 return( $self );
66             }
67              
68             sub as_string
69             {
70 5     5 1 2576 my $self = shift( @_ );
71             # In the sprintf, we use %s and not %d, because start or end may be undef, and it would inadvertently result in 0
72 5   100 11   38 return( $self->unit->scalar . '=' . $self->ranges->map(sub{ sprintf( '%s-%s', ( $_->start // '' ), ( $_->end // '' ) ); })->join( ', ' )->scalar );
  11   100     225149  
73             }
74              
75 0     0 1 0 sub new_range { HTTP::Promise::Headers::Range::StartEnd->new( @_ ) };
76              
77 21     21 1 90759 sub ranges { return( shift->_set_get_array_as_object( 'ranges', @_ ) ); }
78              
79 7     7 1 2948 sub unit { return( shift->_set_get_scalar_as_object( 'unit', @_ ) ); }
80              
81             {
82             package
83             HTTP::Promise::Headers::Range::StartEnd;
84             BEGIN
85 0         0 {
86 3     3   28 use strict;
  3         7  
  3         76  
87 3     3   16 use warnings;
  3         5  
  3         126  
88 3     3   16 use parent qw( Module::Generic );
  3     0   6  
  3         12  
89             };
90 3     3   223 use strict;
  3         6  
  3         68  
91 3     3   13 use warnings;
  3         6  
  3         445  
92            
93             sub init
94             {
95 8     8   649 my $self = shift( @_ );
96 8         27 my @args = @_;
97 8         143 @$self{qw( start end )} = splice( @_, 0, 2 );
98 8         35 return( $self->SUPER::init( @_ ) );
99             }
100            
101 27     27   382961 sub end { return( shift->_set_get_number( 'end', @_ ) ); }
102            
103 22     22   193206 sub start { return( shift->_set_get_number( 'start', @_ ) ); }
104             }
105              
106             1;
107             # NOTE: POD
108             __END__
109              
110             =encoding utf-8
111              
112             =head1 NAME
113              
114             HTTP::Promise::Headers::Range - Range Header Field
115              
116             =head1 SYNOPSIS
117              
118             use HTTP::Promise::Headers::Range;
119             my $range = HTTP::Promise::Headers::Range->new ||
120             die( HTTP::Promise::Headers::Range->error, "\n" );
121             $range->unit( 'bytes' ):
122             $range->ranges->push( $range->new_range( 200, 1000 ) );
123             my $start = $range->ranges->first->start; # 200
124             my $end = $range->ranges->first->end; # 1000
125             $range->ranges->push( $range->new_range( 1001, 2000 ) );
126             say $range->as_string;
127             # or
128             say "$range";
129             # bytes=200-1000, 1001-2000
130              
131             =head1 VERSION
132              
133             v0.1.0
134              
135             =head1 DESCRIPTION
136              
137             The following is an extract from Mozilla documentation.
138              
139             The Range HTTP request header indicates the part of a document that the server should return.
140              
141             Example:
142              
143             # Getting multiple ranges
144             Range: bytes=200-1000, 2000-6576, 19000-
145             # The last 500 bytes
146             Range: bytes=0-499, -500
147              
148             Range: bytes=200-
149             Range: bytes=200-1000
150             Range: bytes=200-1000, 1001-2000
151             Range: bytes=200-1000, 1001-2000, 2001-3000
152             Range: bytes=-4321
153              
154             =head1 METHODS
155              
156             =head2 as_string
157              
158             Returns a string representation of the C<Range> object.
159              
160             =head2 new_range
161              
162             Provided with a start and and offset, and this will return a new C<HTTP::Promise::Headers::Range::StartEnd> object.
163              
164             This object has two methods: C<start> and C<end> each capable of setting or returning its value, which may be C<undef>
165              
166             =head2 ranges
167              
168             Sets or gets the L<array object|Module::Generic::Array> that contains all the C<HTTP::Promise::Headers::Range::StartEnd> objects (see below for a descriptions). Thus you can use all the methods from L<Module::Generic::Array> to manipulate the range objects.
169              
170             =head2 unit
171              
172             The unit in which ranges are specified. This is usually C<bytes>.
173              
174             =head1 HTTP::Promise::Headers::Range::StartEnd
175              
176             =head2 end
177              
178             Sets or gets the end of the range as a L<number object|Module::Generic::Number>
179              
180             =head2 start
181              
182             Sets or gets the start of the range as a L<number object|Module::Generic::Number>
183              
184             =head1 AUTHOR
185              
186             Jacques Deguest E<lt>F<jack@deguest.jp>E<gt>
187              
188             =head1 SEE ALSO
189              
190             See also L<rfc7233, section 3.1|https://tools.ietf.org/html/rfc7233#section-3.1> and L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Range>
191              
192             L<HTTP::Promise>, L<HTTP::Promise::Request>, L<HTTP::Promise::Response>, L<HTTP::Promise::Message>, L<HTTP::Promise::Entity>, L<HTTP::Promise::Headers>, L<HTTP::Promise::Body>, L<HTTP::Promise::Body::Form>, L<HTTP::Promise::Body::Form::Data>, L<HTTP::Promise::Body::Form::Field>, L<HTTP::Promise::Status>, L<HTTP::Promise::MIME>, L<HTTP::Promise::Parser>, L<HTTP::Promise::IO>, L<HTTP::Promise::Stream>, L<HTTP::Promise::Exception>
193              
194             =head1 COPYRIGHT & LICENSE
195              
196             Copyright(c) 2022 DEGUEST Pte. Ltd.
197              
198             All rights reserved.
199              
200             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
201              
202             =cut