File Coverage

blib/lib/Message/Rules.pm
Criterion Covered Total %
statement 77 78 98.7
branch 16 30 53.3
condition 1 3 33.3
subroutine 12 13 92.3
pod 2 6 33.3
total 108 130 83.0


line stmt bran cond sub pod time code
1             package Message::Rules;
2             $Message::Rules::VERSION = '1.142940';
3             $Message::Rules::VERSION = '1.142101';
4             {
5             $Message::Rules::VERSION = '1.132770';
6             }
7              
8 3     3   1383 use strict;use warnings;
  3     3   3  
  3         93  
  3         12  
  3         3  
  3         76  
9 3     3   1158 use Message::Match qw(mmatch);
  3         1291  
  3         185  
10 3     3   1246 use Message::Transform qw(mtransform);
  3         968  
  3         160  
11 3     3   13 use File::Find;
  3         3  
  3         119  
12 3     3   12 use JSON;
  3         3  
  3         11  
13              
14             sub new {
15 3     3 0 1706 my $class = shift;
16 3         7 my $self = {};
17 3         8 bless ($self, $class);
18 3         14 return $self;
19             }
20              
21             {
22             my @loaded_configs;
23             my $add_config = sub {
24             my $thing = shift;
25             $thing->{order} = 0 unless $thing->{order};
26             push @loaded_configs, $thing;
27             };
28             my $wanted = sub {
29             my $f = $File::Find::name;
30             $f =~ s/.*\///;
31             return unless -f $f;
32             return if $f =~ /\/\./;
33             my $contents;
34             eval {
35             open my $fh, '<', $f or die "open of $f failed: $!\n";
36             read $fh, $contents, 10240000 or die "read of $f failed: $!\n";
37             close $fh or die "close of $f failed: $!\n";
38             };
39             die "Message::Rules::load_rules_from_directory: $@\n" if $@;
40             my $conf;
41             eval {
42             $conf = decode_json $contents or die 'failed to decode_json';
43             };
44             return unless $conf;
45             if(not ref $conf) {
46             # die "Message::Rules::load_rules_from_directory: $f did not contain a reference";
47             return;
48             }
49             if(ref $conf eq 'HASH') {
50             $add_config->($conf);
51             return;
52             }
53             if(ref $conf eq 'ARRAY') {
54             $add_config->($_) for @{$conf};
55             return;
56             }
57             die "Message::Rules::load_rules_from_directory: $f did not contain either a HASH or ARRAY reference";
58             return;
59             };
60             my $get_sorted_configs = sub {
61             my @configs = sort { $a->{order} <=> $b->{order}} @loaded_configs;
62             @loaded_configs = ();
63             return \@configs;
64             };
65              
66             sub load_rules_from_directory {
67 3     3 1 3 my $self = shift;
68 3         6 my $directory = shift;
69 3 50       50 die "Message::Rules::load_rules_from_directory: passed directory ($directory) does not exist\n"
70             if not -e $directory;
71 3 50       25 die "Message::Rules::load_rules_from_directory: passed directory ($directory) is not a directory\n"
72             if not -d $directory;
73 3         290 find($wanted, $directory);
74 3         8 $self->{rules} = $get_sorted_configs->();
75 3         13 return $self->{rules};
76             }
77             }
78              
79             sub apply_rules {
80 2     2 0 3 my $self = shift;
81 2         3 my $messages = shift;
82 2         8 while(my($key,$value) = each %$messages) {
83 2         2 my $message = $value;
84 2         4 $self->merge_rules($message);
85 2         6 $messages->{$key} = $message;
86             }
87 2         9 return $messages;
88             }
89              
90             sub output_apply_rules {
91 1     1 0 3 my $self = shift;
92 1         4 my $incoming_directory = shift;
93 1         1 my $outgoing_directory = shift;
94 1         6 my $messages = $self->load_messages($incoming_directory);
95 1         3 $self->apply_rules($messages);
96 1         3 while(my($filename, $message) = each %$messages) {
97 1         1 eval {
98 1         2 my $path = "$outgoing_directory/$filename";
99 1 50       65 open my $fh, '>', $path or die "failed to open $path for write: $!";
100 1         49 print $fh JSON->new->canonical(1)->pretty(1)->encode($message);
101 1 50       46 close $fh or die "failed to close $path: $!";
102             };
103 1 50       6 die "Message::Rules::output_apply_rules: (\$filename=$filename): failed: $@\n" if $@;
104             }
105 1         7 return 1;
106             }
107              
108             sub load_messages {
109 2     2 0 8 my $self = shift;
110 2         5 my $directory = shift;
111 2         6 my $messages = {};
112 2         5 eval {
113 2 50 33     61 die 'passed argument not a readable directory'
114             if not -d $directory or not -r $directory;
115 2     0   41 local $SIG{ALRM} = sub { die "timed out\n"; };
  0         0  
116 2         17 alarm 5;
117 2 50       51 opendir (my $dh, $directory) or die "opendir failed: $!";
118 2 100       17 my @files = grep { -f "$directory/$_" and not "$directory/$_" =~ /^\./ } readdir($dh);
  6         75  
119 2 50       19 closedir $dh or die "closedir failed: $!";
120 2         3 foreach my $filename (@files) {
121 2         3 my $contents;
122 2 50       47 open my $fh, "$directory/$filename" or die "failed to open file ($filename): $!";
123 2 50       45 read $fh, $contents, 1024000 or die "failed to read ($filename): $!";
124 2 50       16 close $fh or die "failed to close file ($filename): $!";
125 2 50       17 my $conf = decode_json $contents or die 'failed to decode_json';
126 2         25 $messages->{$filename} = $conf;
127             }
128             };
129 2         7 alarm 0;
130 2 50       8 die "Message::Rules::load_messages: failed (\$directory=$directory) $@\n"
131             if $@;
132 2         8 return $messages;
133             }
134              
135             sub merge_rules {
136 3     3 1 3 my $self = shift;
137 3         3 my $message = shift;
138              
139 3         3 foreach my $conf (@{$self->{rules}}) {
  3         8  
140 3 50       15 next unless mmatch $message, $conf->{match};
141 3         89 mtransform($message, $conf->{transform});
142             }
143 3         44 return $message;
144             }
145              
146              
147             1;
148              
149             __END__
150              
151             =head1 NAME
152              
153             Message::Rules - Apply a pile of rules to incoming messages
154              
155             =head1 SYNOPSIS
156              
157             use Message::Rules;
158              
159             =head1 DESCRIPTION
160              
161             my $r = Message::Rules->new();
162             $r->load_rules_from_directory('conf/dir');
163             my $m = $r->merge_rules({main => 'thing'});
164              
165              
166             =head1 METHODS
167              
168             =head2 load_rules_from_directory($directory);
169              
170             Iterate through the passed directory tree and load all of
171             the rules found therein.
172              
173             =head2 merge_rules($message);
174              
175             Pass $message through the loaded ruleset, and return the
176             updated message.
177              
178             =head1 TODO
179              
180             Tons.
181              
182             =head1 BUGS
183              
184             None known.
185              
186             =head1 COPYRIGHT
187              
188             Copyright (c) 2013 Dana M. Diederich. All Rights Reserved.
189              
190             =head1 AUTHOR
191              
192             Dana M. Diederich <dana@realms.org>
193              
194             =cut
195