File Coverage

blib/lib/String/Replace/Safe.pm
Criterion Covered Total %
statement 74 83 89.1
branch 20 32 62.5
condition 17 33 51.5
subroutine 17 17 100.0
pod 0 4 0.0
total 128 169 75.7


line stmt bran cond sub pod time code
1             package String::Replace::Safe;
2             our $VERSION = '0.02';
3 1     1   1958 use strict;
  1         2  
  1         36  
4 1     1   6 use warnings;
  1         2  
  1         28  
5 1     1   4 use Exporter 'import';
  1         6  
  1         31  
6 1     1   6 use Scalar::Util 'reftype', 'blessed';
  1         2  
  1         2323  
7 1     1   1327 use List::MoreUtils 'natatime';
  1         2464  
  1         91  
8 1     1   8 use Carp;
  1         2  
  1         2881  
9              
10             our @EXPORT_OK = ('replace', 'unreplace');
11             our %EXPORT_TAGS = ('all' => [ @EXPORT_OK ] );
12              
13             # There is a lot of code duplication between String::Replace and
14             # String::Replace::Safe, but I don't see a simple way to reduce it without
15             # adding a additionnal indirection level.
16              
17             sub __prepare_replace {
18 13     13   22 my %param;
19 13 100 66     93 if (@_ == 1 && ref($_[0]) && reftype($_[0]) eq 'HASH') {
    50 66        
      33        
      33        
20 3         5 %param = %{$_[0]};
  3         13  
21             } elsif (@_ == 1 && ref($_[0]) && reftype($_[0]) eq 'ARRAY') {
22 0 0       0 croak 'The replace list must have an even number of element' if @{$_[0]} & 1;
  0         0  
23 0         0 %param = @{$_[0]};
  0         0  
24             } else {
25 10 50       23 croak 'The replace list must have an even number of element' if @_ & 1;
26 10         30 %param = @_;
27             }
28            
29 13         18 my @repl;
30 13         35 for my $k (keys %param) {
31 26         60 push @repl, "\Q$k\E";
32             }
33 13         46 my $regexp = '('.(join '|', @repl).')';
34              
35 13         452 return { regexp => qr/$regexp/, replace => \%param };
36             }
37              
38              
39             sub __prepare_unreplace {
40 4     4   6 my %param;
41 4 100 66     38 if (@_ == 1 && ref($_[0]) && reftype($_[0]) eq 'HASH') {
    50 66        
      33        
      33        
42 2         2 %param = %{$_[0]};
  2         8  
43             } elsif (@_ == 1 && ref($_[0]) && reftype($_[0]) eq 'ARRAY') {
44 0 0       0 croak 'The replace list must have an even number of element' if @{$_[0]} & 1;
  0         0  
45 0         0 %param = @{$_[0]};
  0         0  
46             } else {
47 2 50       7 croak 'The replace list must have an even number of element' if @_ & 1;
48 2         7 %param = @_;
49             }
50            
51 4         6 my %rparam;
52 4         22 while (my ($k, $val) = each %param) {
53 8 50 33     29 my @lv = (ref $val && reftype $val eq 'ARRAY') ? @{$val} : $val;
  0         0  
54 8         12 for my $v (@lv) {
55 8         42 $rparam{$v} = $k;
56             }
57             }
58              
59 4         10 return __prepare_replace(%rparam);
60             }
61              
62             # This function is the same for replace and unreplace.
63             sub __execute_replace {
64 23     23   50 my ($str, $repl) = @_;
65            
66 22         182 $str =~ s/$repl->{regexp}/$repl->{replace}{$1}/ge;
  22         87  
67             #return $str =~ s/$repl->{regexp}/$repl->{replace}{$1}/gre; require v5.14
68              
69 22         151 return $str;
70             }
71              
72             sub __execute_replace_in {
73 2     2   5 my (undef, $repl) = @_;
74              
75 2         15 $_[0] =~ s/$repl->{regexp}/$repl->{replace}{$1}/ge;
  2         8  
76              
77 2         7 return;
78             }
79              
80             sub new {
81 2     2 0 295 my ($class, @param) = @_;
82            
83 2         7 my $self = __prepare_replace(@param);
84              
85 2         11 return bless $self, $class;
86             }
87              
88             sub new_unreplace {
89 2     2 0 14 my ($class, @param) = @_;
90            
91 2         6 my $self = __prepare_unreplace(@param);
92              
93 2         11 return bless $self, $class;
94             }
95              
96              
97             sub __replace_method {
98 12     12   17 my $repl = shift;
99            
100 12 100       30 if (wantarray) {
    100          
101 5         10 return map { __execute_replace($_, $repl) } @_;
  8         28  
102             } elsif (defined wantarray) {
103 6 50       18 return @_ ? __execute_replace($_[0], $repl) : undef;
104             } else {
105 1         6 __execute_replace_in($_, $repl) for @_;
106 1         3 return;
107             }
108             }
109              
110             sub __replace_fun {
111 7     7   22 my ($str, @list) = @_;
112              
113 7         15 return __execute_replace($str, __prepare_replace(@list))
114             }
115              
116             sub __unreplace_fun {
117 2     2   5 my ($str, @list) = @_;
118              
119 2         6 return __execute_replace($str, __prepare_unreplace(@list))
120             }
121              
122             sub replace {
123 17 50   17 0 4831 croak 'Missing argument to '.__PACKAGE__.'::replace' unless @_;
124              
125 17 100 66     122 if (blessed($_[0]) && $_[0]->isa(__PACKAGE__)) {
126 10         21 return &__replace_method;
127             } else {
128 7         13 return &__replace_fun;
129             }
130             }
131              
132             sub unreplace {
133 4 50   4 0 12 croak 'Missing argument to '.__PACKAGE__.'::unreplace' unless @_;
134              
135 4 100 66     28 if (blessed($_[0]) && $_[0]->isa(__PACKAGE__)) {
136 2         29 return &__replace_method;
137             } else {
138 2         5 return &__unreplace_fun;
139             }
140             }
141              
142             =cut
143              
144             1;
145              
146              
147             =encoding utf-8
148              
149             =head1 NAME
150              
151             String::Replace::Safe - Performs arbitrary replacement in strings, safely
152              
153             =head1 SYNOPSIS
154              
155             use String::Replace::Safe ':all';
156            
157             print replace('hello name', 'name' => 'world');
158             print unreplace('hello world', {'name' => 'world'});
159            
160             my $r = String::Replace::Safe->new('name' => 'world');
161             print $r->replace('hello world');
162              
163             =head1 DESCRIPTION
164              
165             C is a safe version of the C> library.
166             That is that this version does not depend on the order of evaluation of the
167             argument to its function. This version is also consistently slower than the I
168             version (by a factor of approximately 50%).
169              
170             Apart from that, the interface of the safe version is exactly the same (both
171             functionnal and object oriented) as the interface of the C>
172             library. Hence the absence of documentation here.
173              
174             =head1 BUGS
175              
176             Please report any bugs or feature requests to C, or
177             through the web interface at L.
178              
179             =head1 AUTHOR
180              
181             Mathias Kende (mathias@cpan.org)
182              
183             =head1 VERSION
184              
185             Version 0.02 (January 2013)
186              
187             =head1 COPYRIGHT & LICENSE
188              
189             Copyright 2013 © Mathias Kende. All rights reserved.
190              
191             This program is free software; you can redistribute it and/or
192             modify it under the same terms as Perl itself.
193              
194             =cut
195              
196              
197              
198