File Coverage

blib/lib/Data/Transform/Map.pm
Criterion Covered Total %
statement 35 35 100.0
branch 23 24 95.8
condition 11 15 73.3
subroutine 9 9 100.0
pod 3 3 100.0
total 81 86 94.1


line stmt bran cond sub pod time code
1             # vim: ts=3 sw=3 expandtab
2              
3             package Data::Transform::Map;
4 2     2   2867 use strict;
  2         5  
  2         84  
5              
6 2     2   758 use Data::Transform;
  2         4  
  2         53  
7              
8 2     2   11 use vars qw($VERSION @ISA);
  2         4  
  2         115  
9             $VERSION = '0.01';
10             @ISA = qw(Data::Transform);
11              
12 2     2   8 use Carp qw(croak carp);
  2         4  
  2         1138  
13              
14             sub BUFFER () { 0 }
15             sub CODEGET () { 1 }
16             sub CODEPUT () { 2 }
17              
18             =head1 NAME
19              
20             Data::Transform::Map - transform input and/or output within a filter stack
21              
22             =head1 SYNOPSIS
23              
24             use Data::Transform::Map;
25             use Test::More plan => 'no_plan';
26              
27             my $filter = Data::Transform::Map( Code => \&uc );
28             my $out = $filter->get( [qw(foo bar baz)] );
29             is_deeply ( $out, [qw(FOO BAR BAZ)], "shouting it!");
30              
31             =head1 DESCRIPTION
32              
33             Data::Transform::Map transforms data inside the filter stack. It may be
34             used to transform input, output, or both depending on how it is
35             constructed. This filter is named and modeled after Perl's built-in
36             map() function.
37              
38             =head1 PUBLIC FILTER METHODS
39              
40             Data::Transform::Map implements the L API. Only
41             differences and addition to the API are documented here.
42              
43             =cut
44              
45             =head2 new
46              
47             new() constructs a new Data::Transform::Map object. It must either be
48             called with a single Code parameter, or both a Put and a Get
49             parameter. The values for Code, Put and Get are code references that,
50             when invoked, return transformed versions of their sole parameters. A
51             Code function will be used for both input and ouput, while Get and Put
52             functions allow input and output to be filtered in different ways.
53              
54             # Decrypt rot13.
55             sub decrypt_rot13 {
56             my $encrypted = shift;
57             $encrypted =~ tr[a-zA-Z][n-za-mN-ZA-M];
58             return $encrypted;
59             }
60              
61             # Encrypt rot13.
62             sub encrypt_rot13 {
63             my $plaintext = shift;
64             $plaintext =~ tr[a-zA-Z][n-za-mN-ZA-M];
65             return $plaintext;
66             }
67              
68             # Decrypt rot13 on input, and encrypt it on output.
69             my $rot13_transcrypter = Data::Transform::Map->new(
70             Get => \&decrypt_rot13,
71             Put => \&encrypt_rot13,
72             );
73              
74             Rot13 is symmetric, so the above example can be simplified to use a
75             single Code function.
76              
77             my $rot13_transcrypter = Data::Transform::Map->new(
78             Code => sub {
79             local $_ = shift;
80             tr[a-zA-Z][n-za-mN-ZA-M];
81             return $_;
82             }
83             );
84              
85              
86             =cut
87              
88             sub new {
89 11     11 1 8840 my $type = shift;
90 11 100       158 croak "$type must be given an even number of parameters" if @_ & 1;
91 10         26 my %params = @_;
92              
93 10 100 100     535 croak "$type requires a Code or both Get and Put parameters" unless (
      66        
94             defined($params{Code})
95             or (defined($params{Get}) and defined($params{Put}))
96             );
97              
98 7 100       199 croak "Code element is not a subref"
    100          
99             unless (defined $params{Code} ? ref $params{Code} eq 'CODE' : 1);
100              
101 6 100 66     283 croak "Get or Put element is not a subref"
    100          
    100          
102             unless ((defined $params{Get} ? (ref $params{Get} eq 'CODE') : 1)
103             and (defined $params{Put} ? (ref $params{Put} eq 'CODE') : 1)
104             );
105              
106 4   66     56 my $self = bless [
      66        
107             [ ], # BUFFER
108             $params{Code} || $params{Get}, # CODEGET
109             $params{Code} || $params{Put}, # CODEPUT
110             ], $type;
111             }
112              
113             sub clone {
114 2     2 1 638 my $self = shift;
115              
116 2         7 my $new = [
117             [],
118             $self->[CODEGET],
119             $self->[CODEPUT],
120             ];
121              
122 2         9 return bless $new, ref $self;
123             }
124              
125             # get() is inherited from Data::Transform.
126             # get_one_start() is inherited from Data::Transform.
127             # get_one() is inherited from Data::Transform.
128              
129             sub _handle_get_data {
130 40     40   52 my ($self, $data) = @_;
131              
132 40 100       132 return unless defined $data;
133 14         39 return $self->[CODEGET]->($data);
134             }
135              
136             sub _handle_put_data {
137 10     10   16 my ($self, $data) = @_;
138              
139 10         38 return $self->[CODEPUT]->($data);
140             }
141              
142              
143             =head2 modify
144              
145             modify() changes a Data::Transform::Map object's behavior at runtime. It
146             accepts the same parameters as new(), and it replaces the existing
147             transforms with new ones.
148              
149             # Switch to "reverse" encryption for testing.
150             $rot13_transcrypter->modify(
151             Code => sub { return scalar reverse shift }
152             );
153              
154             =cut
155              
156             sub modify {
157 6     6 1 1121 my ($self, %params) = @_;
158              
159 6         11 for (keys %params) {
160 6 100       34 die "Modify $_ element must be given a coderef"
161             unless (ref $params{$_} eq 'CODE');
162              
163 3 100       11 if (lc eq 'code') {
    100          
    50          
164 1         2 $self->[CODEGET] = $params{$_};
165 1         6 $self->[CODEPUT] = $params{$_};
166             } elsif (lc eq 'put') {
167 1         6 $self->[CODEPUT] = $params{$_};
168             } elsif (lc eq 'get') {
169 1         6 $self->[CODEGET] = $params{$_};
170             }
171             }
172             }
173              
174             1;
175              
176             __END__