File Coverage

blib/lib/Data/Text.pm
Criterion Covered Total %
statement 105 111 94.5
branch 37 38 97.3
condition 4 6 66.6
subroutine 16 17 94.1
pod 10 10 100.0
total 172 182 94.5


line stmt bran cond sub pod time code
1             package Data::Text;
2              
3 4     4   389533 use warnings;
  4         29  
  4         142  
4 4     4   34 use strict;
  4         8  
  4         75  
5 4     4   16 use Carp;
  4         7  
  4         229  
6 4     4   2164 use String::Clean;
  4         68280  
  4         124  
7 4     4   2008 use String::Util;
  4         10284  
  4         432  
8              
9             =head1 NAME
10              
11             Data::Text - Class to handle text in an OO way
12              
13             =head1 VERSION
14              
15             Version 0.12
16              
17             =cut
18              
19             our $VERSION = '0.12';
20              
21             use overload (
22             '==' => \&equal,
23             '!=' => \¬_equal,
24             '""' => \&as_string,
25 0     0   0 bool => sub { 1 },
26 4         50 fallback => 1 # So that boolean tests don't cause as_string to be called
27 4     4   31 );
  4         10  
28              
29             =head1 SYNOPSIS
30              
31             Handle text in an OO way.
32              
33             use Data::Text;
34              
35             my $d = Data::Text->new("Hello, World!\n");
36              
37             print $d->as_string();
38              
39             =head1 SUBROUTINES/METHODS
40              
41             =head2 new
42              
43             Creates a Data::Text object.
44              
45             The optional parameter contains a string, or object, to initialise the object with.
46              
47             =cut
48              
49             sub new {
50 26     26 1 4015 my $class = shift;
51 26         38 my $self;
52              
53 26 100       89 if(!defined($class)) {
    100          
54             # Using Data::Text->new(), not Data::Text::new()
55             # carp(__PACKAGE__, ' use ->new() not ::new() to instantiate');
56             # return;
57              
58             # FIXME: this only works when no arguments are given
59 1         3 $self = bless { }, __PACKAGE__;
60             } elsif(ref($class)) {
61             # clone the given object
62 2         17 $self = bless { }, ref($class);
63 2         8 return $self->set($class);
64             } else {
65 23         49 $self = bless { }, $class;
66             }
67              
68 24 100       65 if(scalar(@_)) {
69 7         18 return $self->set(@_);
70             }
71              
72 17         41 return $self;
73             }
74              
75             =head2 set
76              
77             Sets the object to contain the given text.
78              
79             The argument can be a reference to an array of strings, or an object.
80             If called with an object, the message as_string() is sent to it for its contents.
81              
82             $d->set({ text => "Hello, World!\n" });
83             $d->set(text => [ 'Hello, ', 'World!', "\n" ]);
84              
85             =cut
86              
87             sub set {
88 13     13 1 760 my $self = shift;
89              
90 13         23 my %params;
91 13 100       52 if(ref($_[0]) eq 'HASH') {
    100          
92 1         10 %params = %{$_[0]};
  1         5  
93             } elsif(scalar(@_) % 2 == 0) {
94 4         13 %params = @_;
95             } else {
96 8         27 $params{'text'} = shift;
97             }
98              
99 13 100       39 if(!defined($params{'text'})) {
100 1         4 Carp::carp(__PACKAGE__, ': no text given');
101 1         318 return;
102             }
103              
104 12         37 my @call_details = caller(0);
105 12         312 $self->{'file'} = $call_details[1];
106 12         26 $self->{'line'} = $call_details[2];
107              
108 12 100       42 if(ref($params{'text'})) {
109             # Allow the text to be a reference to a list of strings
110 4 100       16 if(ref($params{'text'}) eq 'ARRAY') {
111 1 50       2 if(scalar(@{$params{'text'}}) == 0) {
  1         6  
112 1         4 Carp::carp(__PACKAGE__, ': no text given');
113 1         305 return;
114             }
115 0         0 delete $self->{'text'};
116 0         0 foreach my $text(@{$params{'text'}}) {
  0         0  
117 0         0 $self = $self->append($text);
118             }
119 0         0 return $self;
120             }
121 3         21 $self->{'text'} = $params{'text'}->as_string();
122             } else {
123 8         17 $self->{'text'} = $params{'text'};
124             }
125              
126 11         41 return $self;
127             }
128              
129             =head2 append
130              
131             Adds data given in "text" to the end of the object.
132             Contains a simple sanity test for consecutive punctuation.
133             I expect I'll improve that.
134              
135             Successive calls to append() can be daisy chained.
136              
137             $d->set('Hello ')->append("World!\n");
138              
139             The argument can be a reference to an array of strings, or an object.
140             If called with an object, the message as_string() is sent to it for its contents.
141              
142             =cut
143              
144             sub append {
145 20     20 1 4187 my $self = shift;
146              
147 20         30 my %params;
148 20 100       71 if(ref($_[0]) eq 'HASH') {
    100          
149 1         2 %params = %{$_[0]};
  1         4  
150             } elsif(scalar(@_) % 2 == 0) {
151 3         11 %params = @_;
152             } else {
153 16         40 $params{'text'} = shift;
154             }
155              
156 20 100       46 if(!defined($params{'text'})) {
157 2         9 Carp::carp(__PACKAGE__, ': no text given');
158 2         620 return;
159             }
160              
161             # Make a note of the caller for ease of debugging
162 18         28 my $file = $self->{'file'};
163 18         23 my $line = $self->{'line'};
164 18         42 my @call_details = caller(0);
165 18         317 $self->{'file'} = $call_details[1];
166 18         28 $self->{'line'} = $call_details[2];
167              
168 18 100       51 if(ref($params{'text'})) {
169             # Allow the text to be a reference to a list of strings
170 3 100       11 if(ref($params{'text'}) eq 'ARRAY') {
171 2 100       3 if(scalar(@{$params{'text'}}) == 0) {
  2         9  
172 1         5 Carp::carp(__PACKAGE__, ': no text given');
173 1         337 return;
174             }
175 1         8 foreach my $text(@{$params{'text'}}) {
  1         3  
176 3         8 $self = $self->append($text);
177             }
178 1         10 return $self;
179             }
180 1         5 $params{'text'} = $params{'text'}->as_string();
181             }
182              
183             # FIXME: handle ending with an abbreviation
184              
185 16 100 100     91 if($self->{'text'} && ($self->{'text'} =~ /\s*[\.\,;]\s*$/)) {
186 6 100       30 if($params{'text'} =~ /^\s*[\.\,;]/) {
187             # die(__PACKAGE__,
188             Carp::carp(__PACKAGE__,
189             ": attempt to add consecutive punctuation\n\tCurrent = '",
190             $self->{'text'},
191             "' added at $line of $file\n\tAppend = '",
192 5         26 $params{'text'},
193             "'",
194             );
195 5         1923 return;
196             }
197             }
198 11         25 $self->{'text'} .= $params{'text'};
199              
200 11         51 return $self;
201             }
202              
203             =head2 equal
204              
205             Are two texts the same?
206              
207             my $t1 = Data::Text->new('word');
208             my $t2 = Data::Text->new('word');
209             print ($t1 == $t2), "\n"; # Prints 1
210              
211             =cut
212              
213             sub equal {
214 4     4 1 640 my $self = shift;
215 4         8 my $other = shift;
216              
217 4         9 return $self->as_string() eq $other->as_string();
218             }
219              
220             =head2 not_equal
221              
222             Are two texts different?
223              
224             my $t1 = Data::Text->new('xyzzy');
225             my $t2 = Data::Text->new('plugh');
226             print ($t1 != $t2), "\n"; # Prints 1
227              
228             =cut
229              
230             sub not_equal {
231 3     3 1 8 my $self = shift;
232 3         5 my $other = shift;
233              
234 3         6 return $self->as_string() ne $other->as_string();
235             }
236              
237             =head2 as_string
238              
239             Returns the text as a string.
240              
241             =cut
242              
243             sub as_string {
244 31     31 1 1749 my $self = shift;
245              
246 31         157 return $self->{'text'};
247             }
248              
249             =head2 length
250              
251             Returns the length of the text.
252              
253             =cut
254              
255             sub length {
256 2     2 1 4 my $self = shift;
257              
258 2 100       7 if(!defined($self->{'text'})) {
259 1         5 return 0;
260             }
261              
262 1         5 return length($self->{'text'});
263             }
264              
265             =head2 trim
266              
267             Removes leading and trailing spaces from the text.
268              
269             =cut
270              
271             sub trim {
272 1     1 1 4 my $self = shift;
273              
274 1         4 $self->{'text'} = String::Util::trim($self->{'text'});
275              
276 1         26 return $self;
277             }
278              
279             =head2 rtrim
280              
281             Removes trailing spaces from the text.
282              
283             =cut
284              
285             sub rtrim {
286 1     1 1 3 my $self = shift;
287              
288 1         4 $self->{'text'} = String::Util::rtrim($self->{'text'});
289              
290 1         17 return $self;
291             }
292              
293             =head2 replace
294              
295             Replaces words.
296              
297             use Data::Text;
298              
299             my $dt = Data::Text->new();
300             $dt->append('Hello World');
301             $dt->replace({ 'Hello' => 'Goodbye dear' });
302             print $dt->as_string(), "\n"; # Outputs "Goodbye dear world"
303              
304             =cut
305              
306             sub replace {
307 2     2 1 377 my $self = shift;
308              
309             # avoid assert failure in String::Clean
310 2 100       7 if($self->{'text'}) {
311 1   33     11 $self->{'clean'} ||= String::Clean->new();
312 1         19 $self->{'text'} = $self->{'clean'}->replace(shift, $self->{'text'}, shift);
313             }
314              
315 2         86 return $self;
316             }
317              
318             =head1 AUTHOR
319              
320             Nigel Horne, C<< >>
321              
322             =head1 BUGS
323              
324             =head1 SEE ALSO
325              
326             L, L
327              
328             =head1 SUPPORT
329              
330             You can find documentation for this module with the perldoc command.
331              
332             perldoc Data::Text
333              
334             You can also look for information at:
335              
336             =over 4
337              
338             =item * MetaCPAN
339              
340             L
341              
342             =item * RT: CPAN's request tracker
343              
344             L
345              
346             =item * CPAN Testers' Matrix
347              
348             L
349              
350             =item * CPAN Ratings
351              
352             L
353              
354             =item * CPAN Testers Dependencies
355              
356             L
357              
358             =back
359              
360             =head1 LICENSE AND COPYRIGHT
361              
362             Copyright 2021-2022 Nigel Horne.
363              
364             This program is released under the following licence: GPL2
365              
366             =cut
367              
368             1;