File Coverage

blib/lib/Hook/Filter.pm
Criterion Covered Total %
statement 66 66 100.0
branch 27 28 96.4
condition 3 3 100.0
subroutine 14 14 100.0
pod n/a
total 110 111 99.1


line stmt bran cond sub pod time code
1             #################################################################
2             #
3             # Hook::Filter - A runtime filtering layer on top of subroutine calls
4             #
5             # $Id: Filter.pm,v 1.12 2008/08/26 08:13:30 erwan_lemonnier Exp $
6             #
7             # 051105 erwan Created
8             # 060301 erwan Recreated
9             # 070516 erwan Updated POD and license, added flush_rules and add_rule
10             # 070522 erwan More POD + don't use rule file unless 'rules' specified in import
11             # 070523 erwan Can use 'rules' multiple time if same rule file specified
12             # 070523 erwan POD updates
13             # 070524 erwan Import parameter 'hook' is now mandatory
14             #
15              
16             package Hook::Filter;
17              
18 6     6   306694 use 5.006;
  6         26  
  6         256  
19 6     6   38 use strict;
  6         25  
  6         213  
20 6     6   31 use warnings;
  6         15  
  6         276  
21 6     6   171 use Carp qw(confess croak);
  6         17  
  6         501  
22 6     6   41 use File::Spec;
  6         12  
  6         171  
23 6     6   2774 use Hook::Filter::Rule;
  6         21  
  6         212  
24 6     6   44 use Hook::Filter::RulePool qw(get_rule_pool);
  6         13  
  6         374  
25 6     6   39 use Hook::Filter::Hooker qw(filter_sub);
  6         13  
  6         529  
26 6     6   37 use base qw(Exporter);
  6         16  
  6         550  
27 6     6   38 use Data::Dumper;
  6         14  
  6         8068  
28              
29             our @EXPORT = qw();
30              
31             our $VERSION = '0.10';
32              
33             #----------------------------------------------------------------
34             #
35             # Global vars
36             #
37             #----------------------------------------------------------------
38              
39             # the rule file actually used by Hook::Filter, and as declared with parameter 'rules'
40             my $RULES_FILE;
41              
42             # list of subs to hijack
43             my %HOOK_SUBS;
44              
45             sub _queue_sub {
46 19     19   29 my ($pkg,$name) = @_;
47 19 100       100 ($name =~ /::/) ? $HOOK_SUBS{$name}=1 : $HOOK_SUBS{$pkg."::".$name}=1;
48             }
49              
50             #----------------------------------------------------------------
51             #
52             # import - verify import parameters, filter the subs and load the rule file
53             #
54              
55             sub import {
56 17     17   18708 my($class,%args) = @_;
57 17         43 my $pkg = caller(0);
58              
59             #
60             # check parameter 'rules', indicating path to the rule file
61             #
62              
63 17 100       72 if (exists $args{rules}) {
64              
65 7 100       279 croak "import parameter 'rules' for Hook::Filter should be a string, but was undef."
66             if (!defined $args{rules});
67              
68 6 100       27 croak "import parameter 'rules' for Hook::Filter should be a string, but was [".Dumper($args{rules})."]."
69             if (ref \$args{rules} ne 'SCALAR');
70              
71 5 100 100     261 croak "you tried to specify 2 different Hook::Filter rule file: [$RULES_FILE] and [".$args{rules}."]. you may have only 1 rule file."
72             if (defined $RULES_FILE && $RULES_FILE ne $args{rules});
73              
74 4         8 $RULES_FILE = $args{rules};
75 4         9 delete $args{rules};
76              
77             #
78             # load the rule file, if any
79             #
80              
81 4         13 my $pool = get_rule_pool();
82              
83 4 100       86 if (-f $RULES_FILE) {
84             # TODO: support runtime monitoring of rules file and update of rules upon changes in file
85              
86 1 50       46 open(IN,"$RULES_FILE")
87             or confess "failed to open Hook::Filter rules file [$RULES_FILE]: $!";
88 1         21 while (my $line = ) {
89 9         16 chomp $line;
90 9 100       39 next if ($line =~ /^\s*\#/);
91 6 100       23 next if ($line =~ /^\s*$/);
92              
93 5         18 my $rule = new Hook::Filter::Rule($line);
94 5         15 $rule->source($RULES_FILE);
95 5         25 $pool->add_rule($rule);
96             }
97 1         11 close(IN);
98             }
99             }
100              
101             #
102             # check parameter 'hook', indicating which subroutines to filter
103             #
104              
105 14 100       354 croak "you must call Hook::Filter with the import parameter 'hook' set to something"
106             if (!exists $args{hook});
107              
108 12 100       203 croak "Invalid parameter: 'hook' should be a string or an array of strings, but was undef."
109             if (!defined $args{hook});
110              
111 11 100       160 if (ref $args{hook} eq 'ARRAY') {
    100          
112 5         12 foreach my $name (@{$args{hook}}) {
  5         19  
113 15 100       294 if (ref \$name ne 'SCALAR') {
114 1         5 croak "Invalid parameter: 'hook' for Hook::Filter should be a string or an array of strings, but was [".Dumper($args{hook})."].";
115             }
116 14         27 _queue_sub($pkg,$name);
117             }
118             } elsif (ref \$args{hook} eq 'SCALAR') {
119 5         17 _queue_sub($pkg,$args{hook});
120             } else {
121 1         9 croak "Invalid parameter: 'hook' for Hook::Filter should be a string or an array of strings, but was [".Dumper($args{hook})."].";
122             }
123              
124 9         24 delete $args{hook};
125              
126             # propagate super class's import
127 9         915 $class->export_to_level(1,undef,());
128             }
129              
130             #
131             # when all is compiled, do filter all the subs
132             #
133              
134             sub _filter_subs {
135 6     6   132 map { filter_sub($_) } keys %HOOK_SUBS;
  19         343  
136             }
137              
138             # this init block won't be executed if Hook::Filter is used from an eval/require
139             INIT {
140             # add a filtering closure around each sub
141 5     5   24 _filter_subs;
142             }
143              
144             1;
145              
146             __END__