File Coverage

lib/Changes/Group.pm
Criterion Covered Total %
statement 100 122 81.9
branch 17 40 42.5
condition 19 42 45.2
subroutine 27 30 90.0
pod 16 18 88.8
total 179 252 71.0


line stmt bran cond sub pod time code
1             ##----------------------------------------------------------------------------
2             ## Changes file management - ~/lib/Changes/Group.pm
3             ## Version v0.2.0
4             ## Copyright(c) 2022 DEGUEST Pte. Ltd.
5             ## Author: Jacques Deguest <jack@deguest.jp>
6             ## Created 2022/11/23
7             ## Modified 2022/12/09
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 Changes::Group;
15             BEGIN
16             {
17 20     20   102918 use strict;
  20         68  
  20         680  
18 20     20   103 use warnings;
  20         43  
  20         569  
19 20     20   109 use warnings::register;
  20         36  
  20         2167  
20 20     20   542 use parent qw( Module::Generic );
  20         328  
  20         128  
21 20     20   12411091 use vars qw( $VERSION );
  20         44  
  20         908  
22 20     20   363 our $VERSION = 'v0.2.0';
23             };
24              
25 20     20   110 use strict;
  20         45  
  20         380  
26 20     20   83 use warnings;
  20         35  
  20         25928  
27              
28             sub init
29             {
30 9     9 1 917 my $self = shift( @_ );
31 9         298 $self->{defaults} = undef;
32 9         37 $self->{elements} = [];
33 9         59 $self->{line} = undef;
34 9         27 $self->{name} = undef;
35 9         36 $self->{nl} = "\n";
36 9         27 $self->{raw} = undef;
37 9         18 $self->{spacer} = undef;
38 9         42 $self->{type} = 'bracket';
39 9         28 $self->{_init_strict_use_sub} = 1;
40 9 50       64 $self->SUPER::init( @_ ) || return( $self->pass_error );
41 9         50205 $self->{_reset} = 1;
42 9         38 return( $self );
43             }
44              
45             sub add_change
46             {
47 3     3 1 2107 my $self = shift( @_ );
48 3         6 my( $change, $opts );
49 3         18 my $elements = $self->elements;
50 3 50 33     2868 if( scalar( @_ ) == 1 && $self->_is_a( $_[0] => 'Changes::Change' ) )
51             {
52 0         0 $change = shift( @_ );
53 0 0       0 if( $elements->exists( $change ) )
54             {
55 0         0 $self->_load_class( 'overload' );
56 0         0 return( $self->error( "A very same change object (", overload::StrVal( $change ), ") is already registered." ) );
57             }
58             }
59             else
60             {
61 3         17 $opts = $self->_get_args_as_hash( @_ );
62 3   50     439 $change = $self->new_change( %$opts ) || return( $self->pass_error );
63             }
64 3         16 $elements->push( $change );
65 3         43 return( $change );
66             }
67              
68             sub as_string
69             {
70 11     11 1 4084 my $self = shift( @_ );
71 11 50 66     84 if( !exists( $self->{_reset} ) ||
      33        
72             !defined( $self->{_reset} ) ||
73             !CORE::length( $self->{_reset} ) )
74             {
75 8         13 my $cache;
76 8 100 66     93 if( exists( $self->{_cache_value} ) &&
    50 66        
      33        
77             defined( $self->{_cache_value} ) &&
78             length( $self->{_cache_value} ) )
79             {
80 2         17 $cache = $self->{_cache_value};
81             }
82             elsif( defined( $self->{raw} ) && length( "$self->{raw}" ) )
83             {
84 6         53 $cache = $self->{raw};
85             }
86            
87 8         25 my $lines = $self->new_array( $cache->scalar );
88             $self->elements->foreach(sub
89             {
90 10     10   5279 my $this = $_->as_string;
91 10 50       34 if( defined( $this ) )
92             {
93 10         35 $lines->push( $this->scalar );
94             }
95 8         283 });
96             # my $str = $lines->join( "\n" );
97 8         1275 my $str = $lines->join( '' );
98 8         298 return( $str );
99             }
100 3         19 my $nl = $self->nl;
101 3         2703 my $lines = $self->new_array;
102             # Either bracket or colon
103 3   50     68 my $type = $self->type // 'bracket';
104 3 50 50     2861 my $grp_str = $self->new_scalar( ( $self->spacer // '' ) . ( $type eq 'bracket' ? '[' : '' ) . ( $self->name // '' ) . ( $type eq 'bracket' ? ']' : ':' ) . ( $nl // '' ) );
    50 50        
      50        
105 3         2987 $lines->push( $grp_str->scalar );
106             $self->changes->foreach(sub
107             {
108 3     3   216 my $this = $_->as_string;
109 3 50       16 if( defined( $this ) )
110             {
111 3         16 $lines->push( $this->scalar );
112             }
113 3         93 });
114             # my $str = $lines->join( "$nl" );
115 3         451 my $str = $lines->join( '' );
116 3         140 $self->{_cache_value} = $str;
117 3         9 CORE::delete( $self->{_reset} );
118 3         12 return( $str );
119             }
120              
121             sub changes
122             {
123 12     12 1 135161 my $self = shift( @_ );
124 12     15   53 my $a = $self->elements->grep(sub{ $self->_is_a( $_ => 'Changes::Change' ) });
  15         7230  
125 12         1918 return( $a );
126             }
127              
128 5     5 1 2059 sub defaults { return( shift->_set_get_hash_as_mix_object( { field => 'defaults', undef_ok => 1 }, @_ ) ); }
129              
130             sub delete_change
131             {
132 0     0 1 0 my $self = shift( @_ );
133 0         0 my $elements = $self->elements;
134 0 0       0 if( scalar( @_ ) != 1 )
    0          
135             {
136 0         0 return( $self->error( 'Usage: $group->delete_change( $change_object );' ) );
137             }
138             elsif( $self->_is_a( $_[0] => 'Changes::Change' ) )
139             {
140 0         0 my $change = shift( @_ );
141 0         0 my $pos = $elements->pos( $change );
142 0 0       0 if( !defined( $pos ) )
143             {
144 0         0 $self->_load_class( 'overload' );
145 0         0 return( '' );
146             }
147 0         0 $elements->delete( $pos, 1 );
148 0         0 return( $change );
149             }
150             else
151             {
152 0         0 $self->_load_class( 'overload' );
153 0 0 0     0 return( $self->error( "I was expecting a Changes::Change object, but instead got '", ( $_[0] // '' ), "' (", ( defined( $_[0] ) ? overload::StrVal( $_[0] ) : 'undef' ), ")." ) );
154             }
155             }
156              
157 44     44 1 225 sub elements { return( shift->_set_get_array_as_object( 'elements', @_ ) ); }
158              
159             sub freeze
160             {
161 6     6 0 13 my $self = shift( @_ );
162 6         20 CORE::delete( @$self{qw( _reset )} );
163             $self->elements->foreach(sub
164             {
165 7 50   7   3628 if( $self->_can( $_ => 'freeze' ) )
166             {
167 7         164 $_->freeze;
168             }
169 6         19 });
170 6         188 return( $self );
171             }
172              
173 6     6 1 4143 sub line { return( shift->reset(@_)->_set_get_number( 'line', @_ ) ); }
174              
175 22     22 1 16291 sub name { return( shift->reset(@_)->_set_get_scalar_as_object( 'name', @_ ) ); }
176              
177             sub new_change
178             {
179 3     3 1 8 my $self = shift( @_ );
180 3         12 my $opts = $self->_get_args_as_hash( @_ );
181 3 50       339 $self->_load_class( 'Changes::Change' ) || return( $self->pass_error );
182 3         131 my $defaults = $self->defaults;
183 3 50       2695 if( defined( $defaults ) )
184             {
185 3         10 foreach my $opt ( qw( spacer1 marker spacer2 max_width wrapper ) )
186             {
187 15 100 33     365 $opts->{ $opt } //= $defaults->{ $opt } if( defined( $defaults->{ $opt } ) );
188             }
189             }
190 3   50     76 my $c = Changes::Change->new( $opts ) ||
191             return( $self->pass_error( Changes::Change->error ) );
192 3         31 return( $c );
193             }
194              
195             sub new_line
196             {
197 0     0 1 0 my $self = shift( @_ );
198 0 0       0 $self->_load_class( 'Changes::NewLine' ) || return( $self->pass_error );
199 0   0     0 my $nl = Changes::NewLine->new( @_ ) ||
200             return( $self->pass_error( Changes::NewLine->error ) );
201 0         0 return( $nl );
202             }
203              
204 9     9 1 167123 sub nl { return( shift->reset(@_)->_set_get_scalar_as_object( 'nl', @_ ) ); }
205              
206 7     7 1 6599 sub raw { return( shift->_set_get_scalar_as_object( 'raw', @_ ) ); }
207              
208 0     0 1 0 sub remove_change { return( shift->delete_change( @_ ) ); }
209              
210             sub reset
211             {
212 59     59 0 113 my $self = shift( @_ );
213 59 100 33     566 if( (
      100        
214             !exists( $self->{_reset} ) ||
215             !defined( $self->{_reset} ) ||
216             !CORE::length( $self->{_reset} )
217             ) && scalar( @_ ) )
218             {
219 9         95 $self->{_reset} = scalar( @_ );
220             }
221 59         339 return( $self );
222             }
223              
224 13     13 1 3247729 sub spacer { return( shift->reset(@_)->_set_get_scalar_as_object( 'spacer', @_ ) ); }
225              
226 9     9 1 44075 sub type { return( shift->reset(@_)->_set_get_scalar_as_object( 'type', @_ ) ); }
227              
228             1;
229             # NOTE: POD
230             __END__
231              
232             =encoding utf-8
233              
234             =head1 NAME
235              
236             Changes::Group - Group object class
237              
238             =head1 SYNOPSIS
239              
240             use Changes::Group;
241             my $g = Changes::Group->new(
242             line => 12,
243             name => 'Front-end',
244             spacer => "\t",
245             debug => 4,
246             ) || die( Changes::Group->error, "\n" );
247             my $change = $g->add_change( $change_object );
248             # or
249             my $change = $g->add_change( text => 'Some comment here' );
250             $g->delete_change( $change );
251             say $g->as_string;
252              
253             =head1 VERSION
254              
255             v0.2.0
256              
257             =head1 DESCRIPTION
258              
259             This object class represents a C<Changes> file group within a release section. It is completely optional.
260              
261             =head1 METHODS
262              
263             =head2 add_change
264              
265             Provided with a L<Changes::Change> object, or an hash or hash reference of options passed to the constructor of L<Changes::Change>, and this will add the change object to the list of elements for this group object.
266              
267             It returns the L<Changes::Change> object, or an L<error|Module::Generic/error> if an error occurred.
268              
269             =head2 as_string
270              
271             Returns a L<scalar object|Module::Generic::Scalar> of the change group. This is a group name enclosed in square brackets:
272              
273             [my group]
274              
275             It returns a L<scalar object|Module::Generic::Scalar>
276              
277             If an error occurred, it returns an L<error|Module::Generic/error>
278              
279             The result of this method is cached so that the second time it is called, the cache is used unless there has been any change.
280              
281             =head2 changes
282              
283             Read only. This returns an L<array object|Module::Generic::Array> containing all the L<change objects|Changes::Change> within this group object.
284              
285             =head2 defaults
286              
287             Sets or gets an hash of default values for the L<Changes::Change> object when it is instantiated by the C<new_change> method.
288              
289             Default is C<undef>, which means no default value is set.
290              
291             my $ch = Changes->new(
292             file => '/some/where/Changes',
293             defaults => {
294             spacer1 => "\t",
295             spacer2 => ' ',
296             marker => '-',
297             max_width => 72,
298             wrapper => $code_reference,
299             }
300             );
301              
302             =head2 delete_change
303              
304             This takes a list of change to remove and returns an L<array object|Module::Generic::Array> of those changes thus removed.
305              
306             A change provided can only be a L<Changes::Change> object.
307              
308             If an error occurred, this will return an L<error|Module::Generic/error>
309              
310             =head2 elements
311              
312             Sets or gets an L<array object|Module::Generic::Array> of all the elements within this group object. Those elements can be L<Changes::Change> and C<Changes::NewLine> objects.
313              
314             =for Pod::Coverage freeze
315              
316             =head2 line
317              
318             Sets or gets an integer representing the line number where this line containing this group information was found in the original C<Changes> file. If this object was instantiated separately, then obviously this value will be C<undef>
319              
320             =head2 name
321              
322             Sets or gets the group name as a L<scalar object|Module::Generic::Scalar>
323              
324             =head2 new_change
325              
326             Instantiates and returns a new L<Changes::Change>, passing its constructor any argument provided.
327              
328             my $change = $rel->new_change( text => 'Some change' ) ||
329             die( $rel->error );
330              
331             =head2 new_line
332              
333             Returns a new C<Changes::NewLine> object, passing it any parameters provided.
334              
335             If an error occurred, it returns an L<error object|Module::Generic/error>
336              
337             =head2 nl
338              
339             Sets or gets the new line character, which defaults to C<\n>
340              
341             It returns a L<number object|Module::Generic::Number>
342              
343             =head2 raw
344              
345             Sets or gets the raw version of the group as found in the C<Changes> file. If set and nothing has been changed, this will be returned by L</as_string> instead of computing the formatting of the group.
346              
347             It returns a L<scalar object|Module::Generic::Scalar>
348              
349             =head2 remove_change
350              
351             This is an alias for L</delete_change>
352              
353             =for Pod::Coverage reset
354              
355             =head2 spacer
356              
357             Sets or gets the leading space, if any, found before the group.
358              
359             It returns a L<scalar object|Module::Generic::Scalar>
360              
361             =head2 type
362              
363             Sets or gets the type of group for this object. This can either be C<bracket>, which is the default, or C<colon>:
364              
365             [My group]
366             # or
367             My group:
368              
369             =head1 AUTHOR
370              
371             Jacques Deguest E<lt>F<jack@deguest.jp>E<gt>
372              
373             =head1 SEE ALSO
374              
375             L<Changes>, L<Changes::Release>, L<Changes::Change>, L<Changes::Version>, L<Changes::NewLine>
376              
377             =head1 COPYRIGHT & LICENSE
378              
379             Copyright(c) 2022 DEGUEST Pte. Ltd.
380              
381             All rights reserved
382              
383             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
384              
385             =cut