File Coverage

blib/lib/Message/Rules.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


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