File Coverage

blib/lib/Data/Printer/Filter.pm
Criterion Covered Total %
statement 31 31 100.0
branch 1 2 50.0
condition 2 6 33.3
subroutine 9 9 100.0
pod n/a
total 43 48 89.5


line stmt bran cond sub pod time code
1             package Data::Printer::Filter;
2 35     35   225 use strict;
  35         68  
  35         1325  
3 35     35   165 use warnings;
  35         91  
  35         1639  
4 35     35   225 use Data::Printer::Common;
  35         82  
  35         886  
5 35     35   156 use Scalar::Util;
  35         78  
  35         8462  
6              
7             sub import {
8 389     389   1167 my $caller = caller;
9              
10 389         864 my %_filters_for = ();
11             my $filter = sub {
12 461     461   1519 my ($name, $code) = @_;
13 461 50 33     5114 Data::Printer::Common::_die( "syntax: filter 'Class', sub { ... }" )
      33        
14             unless defined $name
15             && defined $code
16             && Scalar::Util::reftype($code) eq 'CODE';
17              
18 461         1491 my $target = Data::Printer::Common::_filter_category_for($name);
19 461         3810 unshift @{$_filters_for{$target}{$name}}, sub {
20 661     661   1341 my ($item, $ddp) = @_;
21 661         2552 $code->($item, $ddp);
22 461         809 };
23 389         2217 };
24              
25             {
26 35     35   254 no strict 'refs';
  35         134  
  35         4381  
  389         718  
27 389         629 *{"$caller\::filter"} = $filter;
  389         2507  
28 389     2836   1239 *{"$caller\::_filter_list"} = sub { \%_filters_for };
  389         21180  
  2836         7243  
29             }
30             };
31              
32             1;
33             __END__
34              
35             =head1 NAME
36              
37             Data::Printer::Filter - Create powerful stand-alone filters for Data::Printer
38              
39             =head1 SYNOPSIS
40              
41             Every time you say in your C<.dataprinter> file:
42              
43             filters = SomeFilter, OtherFilter
44              
45             Data::Printer will look for C<Data::Printer::Filter::SomeFilter> and
46             C<Data::Printer::Filter::OtherFilter> on your C<@INC> and load them.
47             To load filters without a configuration file:
48              
49             use DDP filters => ['SomeFilter', 'OtherFilter'];
50              
51             Creating your own filter module is super easy:
52              
53             package Data::Printer::Filter::MyFilter;
54             use Data::Printer::Filter;
55              
56             # this filter will run every time DDP runs into a string/number
57             filter 'SCALAR' => sub {
58             my ($scalar_ref, $ddp) = @_;
59              
60             if ($$scalar_ref =~ /password/) {
61             return '*******';
62             }
63             return; # <-- let other SCALAR filters have a go!
64             };
65              
66             # you can also filter objects of any class!
67             filter 'Some::Class' => sub {
68             my ($object, $ddp) = @_;
69              
70             if (exists $object->{some_data}) {
71             return $ddp->parse( $object->{some_data} );
72             }
73             else {
74             return $object->some_method;
75             }
76             };
77              
78             Later, in your main code:
79              
80             use DDP filters => ['MyFilter'];
81              
82             Or, in your C<.dataprinter> file:
83              
84             filters = MyFilter
85              
86             =head1 DESCRIPTION
87              
88             L<Data::Printer> lets you add custom filters to display data structures and
89             objects as you see fit to better understand and inspect/debug its contents.
90              
91             While you I<can> put your filters inline in either your C<use> statements
92             or your inline calls to C<p()>, like so:
93              
94             use DDP filters => [{
95             SCALAR => sub { 'OMG A SCALAR!!' }
96             }];
97              
98             p @x, filters => [{ HASH => sub { die 'oh, noes! found a hash in my array' } }];
99              
100             Most of the time you probably want to create full-featured filters as a
101             standalone module, to use in many different environments and maybe even
102             upload and share them on CPAN.
103              
104             This is where C<Data::Printer::Filter> comes in. Every time you C<use> it
105             in a package it will export the C<filter> keyword which you can use to
106             create your own filters.
107              
108             Note: the loading B<order of filters matter>. They will be called in order
109             and the first one to return something for the data being analysed will be
110             used.
111              
112             =head1 HELPER FUNCTIONS
113              
114             =head2 filter TYPE, sub { ... };
115              
116             The C<filter> function creates a new filter for I<TYPE>, using the given
117             subref. The subref receives two arguments: the item itself - be it an object
118             or a reference to a standard Perl type - and the current
119             L<Data::Printer::Object> being used to parse the data.
120              
121             Inside your filter you are expected to either return a string with whatever
122             you want to display for that type/object, or an empty "C<return;>" statement
123             meaning I<"Nothing to do, my mistake, let other filters have a go"> (which
124             includes core filters from Data::Printer itself).
125              
126             You may use the current L<Data::Printer::Object> to issue formatting calls
127             like:
128              
129             =over 4
130              
131             =item * C<< $ddp->indent >> - adds to the current indentation level.
132              
133             =item * C<< $ddp->outdent >> - subtracts from the current indentation level.
134              
135             =item * C<< $ddp->newline >> - returns a string containing a lineabreak
136             and the proper number of spaces for the right indentation. It also
137             accounts for the C<multiline> option so you don't have to worry about it.
138              
139             =item * C<< $ddp->maybe_colorize( $string, 'label', 'default_color' ) >> -
140             returns the given string either unmodified (if the output is not colored) or
141             with the color set for I<'label'> (e.g. "class", "array", "brackets"). You are
142             encouraged to provide your own custom colors by labelling them C<filter_*>,
143             which is guaranteed to never collide with a core color label.
144              
145             =item * C<< $ddp->extra_config >> - all options set by the user either in
146             calls to DDP or in the C<.dataprinter> file that are not used by
147             Data::Printer itself will be put here. You are encouraged to provide your
148             own customization options by labelling them C<filter_*>, which is guaranteed
149             to never collide with a local setting.
150              
151             =item * C<< $ddp->parse( $data ) >> - parses and returns the string output of
152             the given data structure.
153              
154             =back
155              
156             =head1 COMPLETE ANNOTATED EXAMPLE
157              
158             As an example, let's create a custom filter for arrays using
159             all the options above:
160              
161             filter ARRAY => sub {
162             my ($array_ref, $ddp) = @_;
163             my $output;
164              
165             if ($ddp->extra_config->{filter_array}{header}) {
166             $output = $ddp->maybe_colorize(
167             'got this array:',
168             'filter_array_header',
169             '#cc7fa2'
170             );
171             }
172              
173             $ddp->indent;
174             foreach my $element (@$ref) {
175             $output .= $ddp->newline . $ddp->parse($element);
176             }
177             $ddp->outdent;
178              
179             return $output;
180             };
181              
182             Then whenever you pass an array to Data::Printer, it will call this code.
183             First it checks if the user has our made up custom option
184             I<'filter_array.header'>. It can be set either with:
185              
186             use DDP filter_array => { header => 1 };
187              
188             Or on C<.dataprinter> as:
189              
190             filter_array.header = 1
191              
192             If it is set, we'll start the output string with I<"got this array">, colored
193             in whatever color was set by the user under the C<filter_array_header>
194             color tag - and defaulting to '#cc7fa2' in this case.
195              
196             Then it updates the indentation, so any call to C<< $ddp->newline >> will add
197             an extra level of indentation to our output.
198              
199             After that we walk through the array using C<foreach> and append each element
200             to our output string as I<newline + content>, where the content is whatever
201             string was returned from C<< $ddp->parse >>. Note that, if the element or any
202             of its subelements is an array, our filter will be called again, this time
203             for the new content.
204              
205             Check L<Data::Printer::Object> for extra documentation on the methods used
206             above and many others!
207              
208             =head1 DECORATING EXISTING FILTERS
209              
210             It may be the case where you want to call this filter and manipulate the
211             result. To do so, make sure you make a named subroutine for your filters
212             instead of using an anonymous one. For instance, all of Data::Printer's
213             filters for core types have a 'parse' public function you can use:
214              
215             my $str = Data::Printer::Filter::HASH::parse($ref, $ddp);
216              
217             =head1 AVAILABLE FILTERS
218              
219             Data::Printer comes with filters for all Perl data types and several filters
220             for popular Perl modules available on CPAN. Take a look at
221             L<< the Data::Printer::Filter namespace|https://metacpan.org/search?q=Data%3A%3APrinter%3A%3AFilter >> for a complete list!
222              
223             =head1 SEE ALSO
224              
225             L<Data::Printer>