File Coverage

blib/lib/Data/Text.pm
Criterion Covered Total %
statement 95 103 92.2
branch 36 38 94.7
condition 4 6 66.6
subroutine 16 17 94.1
pod 10 10 100.0
total 161 174 92.5


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