File Coverage

lib/Badger/Codecs.pm
Criterion Covered Total %
statement 54 56 96.4
branch 17 22 77.2
condition 2 2 100.0
subroutine 13 15 86.6
pod 9 9 100.0
total 95 104 91.3


line stmt bran cond sub pod time code
1             #========================================================================
2             #
3             # Badger::Codecs
4             #
5             # DESCRIPTION
6             # Manager of Badger::Codec modules for encoding and decoding data.
7             #
8             # AUTHOR
9             # Andy Wardley
10             #
11             #========================================================================
12              
13             package Badger::Codecs;
14              
15 15     15   7316 use Carp;
  15         37  
  15         1092  
16 15     15   6034 use Badger::Codec::Chain 'CHAIN CHAINED';
  15         62  
  15         115  
17             use Badger::Factory::Class
18 15         133 version => 0.01,
19             debug => 0,
20             item => 'codec',
21             path => 'Badger(X)::Codec',
22             import => 'class CLASS',
23             constants => 'HASH ARRAY DELIMITER PKG',
24             constant => {
25             CODEC_METHOD => 'codec',
26             ENCODE_METHOD => 'encode',
27             DECODE_METHOD => 'decode',
28             ENCODING => 'Badger::Codec::Encoding',
29             },
30             exports => {
31             any => 'Codec',
32 15     15   6216 };
  15         36  
33              
34             our $CODECS = {
35             # any codecs with non-standard capitalisation can go here, but
36             # generally we grok the module name from the $CODEC_PATH, e.g.
37             uri => 'Badger::Codec::URI',
38             url => 'Badger::Codec::URL',
39             yaml => 'Badger::Codec::YAML',
40             json => 'Badger::Codec::JSON',
41             html => 'Badger::Codec::HTML',
42             tt => 'Badger::Codec::TT',
43             map {
44             my $name = $_; $name =~ s/\W//g;
45             $_ => [ENCODING, ENCODING.PKG.$name],
46             } qw( utf8 UTF8 UTF16BE UTF16LE UTF32BE UTF32LE )
47             };
48              
49              
50             sub Codec {
51 0     0 1 0 CLASS->codec(@_);
52             }
53              
54              
55             sub codec {
56 54     54 1 234 my $self = shift->prototype;
57              
58             # quick turn-around if we're handling chains
59 54 100       301 return $_[0] =~ CHAINED
60             ? $self->chain(@_)
61             : $self->item(@_);
62             }
63              
64              
65             sub chain {
66 4     4 1 7 my $self = shift;
67 4 50       21 $self->debug("creating chain for $_[0]\n") if $DEBUG;
68 4         27 return CHAIN->new(@_);
69             }
70              
71              
72             sub found_object {
73 25     25 1 67 my ($self, $name, $item, $args) = @_;
74              
75             # TODO: assert $item is a codec object?
76              
77             # We cache any codecs that are created without configuration items
78             # but we can only use those "bare" codecs if $args is empty. Otherwise
79             # we must create a new object
80 25 100       56 if (@$args) {
81 1         2 $self->debug("creating new ", ref $item, " codec for $name\n") if DEBUG;
82 1         8 return $self->construct($name, ref $item, $args);
83             }
84             else {
85 24 50       53 $self->debug("re-using cached codec for $name: $item\n") if $DEBUG;
86 24         56 return $item;
87             }
88             }
89              
90              
91             sub result {
92 50     50 1 102 my ($self, $name, $codec, $args) = @_;
93             # only cache codec objects created with no arguments
94 50 100       121 unless (@$args) {
95 49         65 $self->debug("Caching $name codec for subsequent re-used: $codec") if DEBUG;
96 49         101 $self->{ codecs }->{ $name } = $codec;
97             }
98 50         238 return $codec;
99             }
100              
101              
102             sub encode {
103 8     8 1 70 shift->codec(shift)->encode(@_);
104             }
105              
106              
107             sub decode {
108 8     8 1 29 shift->codec(shift)->decode(@_);
109             }
110              
111              
112             #-----------------------------------------------------------------------
113             # export hooks
114             #-----------------------------------------------------------------------
115              
116             class->exports(
117             hooks => {
118             map { ($_ => \&_export_hook) }
119             qw( codec codecs )
120             }
121             );
122              
123              
124             sub _export_hook {
125 15     15   50 my ($class, $target, $key, $symbols) = @_;
126 15 50       62 croak "You didn't specify a value for the '$key' load option."
127             unless @$symbols;
128 15         38 my $method = "export_$key";
129 15         56 $class->$method($target, shift @$symbols);
130             }
131              
132              
133             sub export_codec {
134 19     19 1 47 my ($class, $target, $name, $alias) = @_;
135 19         46 my $codec = $class->codec($name);
136 19   100     84 my $cmethod = $alias || CODEC_METHOD;
137 19 100       69 my $emethod = $alias ? join('_', ENCODE_METHOD, $alias) : ENCODE_METHOD;
138 19 100       47 my $dmethod = $alias ? join('_', DECODE_METHOD, $alias) : DECODE_METHOD;
139 15     15   128 no strict 'refs';
  15         32  
  15         4277  
140            
141             # prefix target class onto above method names
142 19         92 $_= "${target}::$_" for ($cmethod, $emethod, $dmethod);
143            
144 19 50       48 $class->debug("exporting $codec codec to $target\n") if $DEBUG;
145              
146             # NOTE: I think it's more correct to attempt the export regardless of
147             # any existing sub and allow a redefine warning to be raised. This is
148             # better than silently failing to export the requested items.
149 19         30 my $temp = $codec; # make sure this is a constant on 5.22
150 19     0   156 *{$cmethod} = sub() { $temp }; # unless defined &{$cmethod};
  19         98  
  0         0  
151 19         89 *{$emethod} = $codec->encoder; # unless defined &{$emethod};
  19         67  
152 19         85 *{$dmethod} = $codec->decoder; # unless defined &{$dmethod};
  19         105  
153             }
154              
155              
156             sub export_codecs {
157 3     3 1 8 my ($class, $target, $names) = @_;
158 3 100       10 if (ref $names eq HASH) {
159 1         40 while (my ($key, $value) = each %$names) {
160 1         5 $class->export_codec($target, $value, $key);
161             }
162             }
163             else {
164 2 50       19 $names = [ split(DELIMITER, $names) ] unless ref $names eq ARRAY;
165 2         15 $class->export_codec($target, $_, $_) for @$names;
166             }
167             }
168              
169             1;
170              
171              
172             __END__