File Coverage

blib/lib/Data/Passphrase/Ruleset.pm
Criterion Covered Total %
statement 15 59 25.4
branch 0 34 0.0
condition 0 14 0.0
subroutine 5 10 50.0
pod 2 5 40.0
total 22 122 18.0


line stmt bran cond sub pod time code
1             # $Id: Ruleset.pm,v 1.6 2007/08/14 15:45:51 ajk Exp $
2              
3 1     1   7 use strict;
  1         2  
  1         39  
4 1     1   6 use warnings;
  1         2  
  1         47  
5              
6             package Data::Passphrase::Ruleset; {
7 1     1   6 use Object::InsideOut;
  1         1  
  1         11  
8              
9 1     1   1008 use Data::Passphrase::Rule;
  1         3  
  1         648  
10 1     1   180 use Carp;
  1         2  
  1         1958  
11              
12             # object attributes
13             my @debug :Field( Std => 'debug', Type => 'Numeric' );
14             my @file :Field( Get => 'get_file', );
15             my @passing_score :Field( Std => 'passing_score', Type => 'Numeric' );
16             my @rules :Field( Get => 'get_rules', );
17              
18             my %init_args :InitArgs = (
19             debug => {
20             Def => 0,
21             Field => \@debug,
22             Type => 'Numeric',
23             },
24             file => {
25             Field => \@file,
26             Pre => \&preprocess,
27             },
28             passing_score => {
29             Def => 0.6,
30             Field => \@passing_score,
31             Type => 'Numeric',
32             },
33             rules => {
34             Field => \@rules,
35             Pre => \&preprocess,
36             Type => 'Array_ref',
37             },
38             );
39              
40             sub preprocess {
41 0     0 0   my ($class, $name, $init_ref, $self, $value) = @_;
42              
43             # file & rules attributes are mutually exclusive
44 0 0         if (defined $value) {
45 0 0 0       croak 'file & rules cannot be supplied simultaneously'
      0        
      0        
46             if $name eq 'file' && defined $self->get_rules()
47             || $name eq 'rules' && defined $self->get_file ();
48             }
49              
50 0           return $value;
51             }
52             # overload constructor so we can automatically load the rules file
53             sub new {
54 0     0 1   my ($class, $arg_ref) = @_;
55              
56             # unpack arguments
57 0           my $debug = $arg_ref->{debug};
58              
59 0 0         $debug and warn 'initializing ', __PACKAGE__, ' object';
60              
61             # construct object
62 0           my $self = $class->Object::InsideOut::new($arg_ref);
63              
64             # load rules from file
65 0 0         if (exists $arg_ref->{file}) {
66 0           $self->load();
67             }
68              
69 0           return $self;
70             }
71              
72             # cache rulesets by filename
73             my %Rules_Cache;
74              
75             # load the rules file if we need to
76             sub load {
77 0     0 1   my ($self) = @_;
78              
79             # unpack arguments
80 0           my $debug = $self->get_debug();
81 0 0         my $file = $self->get_file () or croak 'file attribute undefined';
82              
83 0 0         $debug and warn "$file: checking readability";
84 0           my $last_modified = 0;
85 0 0         if (-r $file) {
86              
87             # point the object attribute at the current ruleset
88 0   0       $Rules_Cache{$file}{rules} ||= [];
89 0           $self->set(\@rules, $Rules_Cache{$file}{rules});
90              
91             # don't re-read if file hasn't been modified since last time
92 0           $last_modified = (stat _)[9];
93 0 0         $debug and warn "$file: pid: $$, mod time: $last_modified, ",
94             "last processed: ", $Rules_Cache{$file}{last_read};
95 0 0 0       return if exists $Rules_Cache{$file}{last_read}
96             && $Rules_Cache{$file}{last_read} == $last_modified;
97              
98             # read the configuration file
99 0 0         $debug and warn "$file: processing";
100 0           my $rule_list = do $file;
101 0 0         if (ref $rule_list ne 'ARRAY') {
102 0 0         croak "$file: parse error: $@" if $@;
103 0 0         croak "$file: $!" if $!;
104 0           croak "$file: must return a reference to an array of rules";
105             }
106              
107 0 0         push @{ $Rules_Cache{$file}{rules} }, map {
  0            
108 0           ref eq 'HASH'
109             ? Data::Passphrase::Rule->new(
110             { %$_, debug => $debug }
111             )
112             : $_
113             ;
114             } @$rule_list;
115             }
116              
117             # limp along if the file went away, unless this is the first run
118             else {
119 0           warn "$file: $!";
120 0 0         die if !exists $Rules_Cache{$file}{last_read};
121             }
122              
123             # cache the timestamp for comparison in later calls
124 0           $Rules_Cache{$file}{last_read} = $last_modified;
125             }
126              
127             # load the file after setting the file attribute
128             sub set_file {
129 0     0 0   my ($self, $value) = @_;
130 0           my $return_value = $self->set(\@file, $value);
131 0 0         if (defined $value) {
132 0           $self->load();
133             }
134 0           return $return_value;
135             }
136              
137             # clear file attribute if rules are loaded directly
138             sub set_rules {
139 0     0 0   my ($self, $value) = @_;
140              
141             # check type
142 0 0         croak 'rules attribute may only be set to an array reference'
143             if ref $value ne 'ARRAY';
144              
145 0           my $return_value = $self->set(\@rules, $value);
146 0           $self->set_file();
147              
148 0           return $return_value;
149             }
150             }
151              
152             1;
153             __END__