File Coverage

blib/lib/Config/Strict.pm
Criterion Covered Total %
statement 160 170 94.1
branch 59 80 73.7
condition 27 49 55.1
subroutine 31 34 91.1
pod 14 14 100.0
total 291 347 83.8


line stmt bran cond sub pod time code
1             package Config::Strict;
2 6     6   558924 use warnings;
  6         15  
  6         443  
3 6     6   39 use strict;
  6         10  
  6         372  
4 6     6   8247 use Data::Dumper;
  6         91334  
  6         655  
5 6     6   87 use Scalar::Util qw(blessed weaken);
  6         12  
  6         1198  
6             $Data::Dumper::Indent = 0;
7 6     6   41 use Carp qw(confess croak);
  6         15  
  6         752  
8              
9             our $VERSION = '0.07';
10              
11 6     6   8072 use Declare::Constraints::Simple -All;
  6         149410  
  6         88  
12 6     6   211232 use Config::Strict::UserConstraints;
  6         19  
  6         75  
13              
14             my %type_registry = (
15             Bool => IsOneOf( 0, 1 ),
16             Num => IsNumber,
17             Int => IsInt,
18             Str => HasLength,
19             ArrayRef => IsArrayRef,
20             HashRef => IsHashRef,
21             CodeRef => IsCodeRef,
22             Regexp => IsRegex,
23             # Enum => undef,
24             # Anon => undef,
25             );
26              
27             sub register_types {
28             # Allow user type registration
29 3     3 1 1688 my $class = shift;
30 3 50 33     20 croak "No class" unless $class and not ref $class;
31 3 50 33     29 croak "Invalid name-constraint pairs args (@_)" unless @_ and @_ % 2 == 0;
32 3         13 my %nc = @_;
33 3         14 while ( my ( $name, $constraint ) = each %nc ) {
34 7 50       15 croak "No name" unless $name;
35 7 50       17 croak "No constraint" unless $constraint;
36 7 100 66     272 croak "$name is already a registered type"
      66        
37             if exists $type_registry{ $name }
38             or $name eq 'Enum'
39             or $name eq 'Anon';
40             # print "Creating user type $name...\n";
41 6 100       16 if ( _check_is_profile( $constraint ) ) {
42             # Already a profile
43 4         18 $type_registry{ $name } = $constraint;
44             }
45             else {
46             # Make a profile from a bare sub
47 1         6 my $made = _make_constraint( $name => $constraint );
48 1         9 $type_registry{ $name } = $made;
49             }
50             }
51             }
52              
53             sub _create_profile {
54 15     15   24 my $param = shift;
55              
56             # Check parameter hash structure
57 15         46 _validate_params( $param );
58              
59 112         136 my %profile = (
60             # Built-in types
61             (
62             map {
63             my $type = $_;
64 112         286 map { $_ => $type_registry{ $type } }
  31         103  
65             _flatten( $param->{ $_ } )
66             } keys %type_registry
67             ),
68             (
69 1         3 map { $_ => IsOneOf( @{ $param->{ Enum }{ $_ } } ) }
  1         7  
70 13         61 keys %{ $param->{ Enum } }
71             ),
72             # Anon types
73             (
74             map {
75 5         11 my $sub = $param->{ Anon }{ $_ };
76 5 100       16 $sub = _make_constraint( undef => $sub )
77             # confess
78             #"Anon code blocks must be a Declare::Constraints::Simple profile."
79             # . " Use register_types to implement bare coderefs."
80             unless _check_is_profile( $sub );
81 5         24 $_ => $sub
82 14         62 } keys %{ $param->{ Anon } }
  13         78  
83             ),
84             );
85 13         47 \%profile;
86             } ## end sub _create_profile
87              
88             # Constructor
89             sub new {
90 17     17 1 6100 my $class = shift;
91 17         31 my $opts = shift;
92 17 50       57 confess "Invalid construction arguments: @_" if @_;
93              
94             # Get the parameter hash
95             croak "No 'params' key in constructor"
96             unless exists $opts->{ params }
97 17 100 66     785 and ( my $param = delete $opts->{ params } );
98              
99             # Get required, default values
100 15   100     78 my $required = delete $opts->{ required } || [];
101 15   100     80 my $default = delete $opts->{ defaults } || {};
102              
103             # Check that options hash now empty
104 15 50       46 confess sprintf( "Invalid option(s): %s", Dumper( $opts ) )
105             if %$opts;
106              
107             # Create the configuration profile
108 15         44 my $profile = _create_profile( $param );
109              
110             # Set required to all parameters if *
111 13 100       61 $required = [ keys %$profile ] if $required eq '*';
112 13 50 33     101 croak "Required parameters not an arrayref"
113             unless ref $required and ref $required eq 'ARRAY';
114             # @$required == 1 and $required->[ 0 ] eq '_all';
115              
116             # Validate required and defaults
117 13         36 _validate_required( $required, $profile );
118 12         31 _validate_defaults( $default, $required );
119              
120             # Construct
121 9         134 my $self = bless( {
122 10         46 _required => { map { $_ => 1 } @$required }
123             , # Convert to hash lookup
124             _profile => $profile,
125             },
126             $class
127             );
128 10         58 $self->set( %$default );
129 9         50 $self;
130             } ## end sub new
131              
132             sub get {
133 34     34 1 5095 my $self = shift;
134 34         95 $self->_get_check( @_ );
135 33         65 my $params = $self->{ _params };
136             return (
137 33 100       154 wantarray ? ( map { $params->{ $_ } } @_ ) : $params->{ $_[ 0 ] } );
  15         74  
138             }
139              
140             sub set {
141 45     45 1 7134 my $self = shift;
142 45         120 $self->_set_check( @_ );
143 24         260 my %pv = @_;
144 24         99 while ( my ( $p, $v ) = each %pv ) {
145 32         131 $self->{ _params }{ $p } = $v;
146             }
147 24         89 1;
148             }
149              
150             sub unset {
151 2     2 1 4 my $self = shift;
152 2         10 $self->_unset_check( @_ );
153 1         6 delete $self->{ _params }{ $_ } for @_;
154             }
155              
156             sub param_is_set {
157 25     25 1 422 my $self = shift;
158 25 50       61 croak "No parameter passed" unless @_;
159 25         135 return exists $self->{ _params }{ $_[ 0 ] };
160             }
161              
162             sub all_set_params {
163 0     0 1 0 keys %{ shift->{ _params } };
  0         0  
164             }
165              
166             sub param_hash {
167 0     0 1 0 %{ shift->{ _params } };
  0         0  
168             }
169              
170             sub param_array {
171 0     0 1 0 my $self = shift;
172 0         0 my $params = $self->{ _params };
173 0         0 map { [ $_ => $params->{ $_ } ] } keys %$params;
  0         0  
174             }
175              
176             sub param_exists {
177 48     48 1 8900 my $self = shift;
178 48 50       116 croak "No parameter passed" unless @_;
179 48         280 return exists $self->{ _profile }{ $_[ 0 ] };
180             }
181              
182             sub all_params {
183 1     1 1 2 keys %{ shift->{ _profile } };
  1         8  
184             }
185              
186             sub get_profile {
187 24     24 1 36 my $self = shift;
188 24 50       50 croak "No parameter passed" unless @_;
189 24         103 $self->{ _profile }{ $_[ 0 ] };
190             }
191              
192             sub _get_check {
193 34     34   71 my ( $self, @params ) = @_;
194 34         56 my $profile = $self->{ _profile };
195 34         112 _profile_check( $profile, $_ ) for @params;
196             }
197              
198             sub _set_check {
199 45     45   125 my ( $self, %value ) = @_;
200 45         91 my $profile = $self->{ _profile };
201 45         179 while ( my ( $k, $v ) = each %value ) {
202 54         247 _profile_check( $profile, $k => $v );
203             }
204             }
205              
206             sub _unset_check {
207 2     2   5 my ( $self, @params ) = @_;
208             # Check against required parameters
209 2         13 for ( @params ) {
210 2 100       6 confess "$_ is a required parameter" if $self->param_is_required( $_ );
211             }
212             # Check against profile
213 1         4 my $profile = $self->{ _profile };
214 1         5 _profile_check( $profile, $_ ) for @params;
215             }
216              
217             sub validate {
218 24     24 1 22349 my $self = shift;
219 24 50       68 confess "No parameter-values pairs passed" unless @_ >= 2;
220 24 50       65 confess "Uneven number of parameter-values pairs passed" if @_ % 2;
221 24         66 my %pair = @_;
222 24         87 while ( my ( $param, $value ) = each %pair ) {
223 25 100 100     127 return 0
224             unless $self->param_exists( $param )
225             and $self->get_profile( $param )->( $value );
226             }
227 10         791 1;
228             }
229              
230             sub param_is_required {
231 4     4 1 8 my ( $self, $param ) = @_;
232 4 50       11 return unless $param;
233 4 100       252 return 1 if $self->{ _required }{ $param };
234 2         18 0;
235             }
236              
237             # Static validator from profile
238             sub _profile_check {
239 92     92   156 my ( $profile, $param ) = ( shift, shift );
240 92 50       211 confess "No parameter passed" unless defined $param;
241 92 100       596 confess "Invalid parameter: $param doesn't exist"
242             unless exists $profile->{ $param };
243 90 100       274 if ( @_ ) {
244 53         79 my $value = shift;
245 53         214 my $result = $profile->{ $param }->( $value );
246 53 100       3180 unless ( $result ) {
247             # Failed validation
248 20 50 33     360 confess $result->message
249             if ref $result
250             and $result->isa( 'Declare::Constraints::Simple::Result' );
251 0 0       0 confess sprintf( "Invalid value (%s) for config parameter $param",
252             defined $value ? $value : 'undef' );
253             }
254             }
255             }
256              
257             sub _validate_params {
258 15     15   24 my $param = shift;
259 15 50 33     301 confess "No parameters passed"
      33        
      33        
260             unless defined $param
261             and ref $param
262             and ref $param eq 'HASH'
263             and %$param;
264 105         3718 my $param_profile = OnHashKeys( (
265 15         39 map { $_ => Or( HasLength, IsArrayRef ) }
266             qw( Bool Int Num Str Regexp ArrayRef HashRef )
267             ),
268             Enum => IsHashRef(
269             -keys => HasLength,
270             -values => IsArrayRef
271             ),
272             Anon => IsHashRef(
273             -keys => HasLength,
274             -values => IsCodeRef
275             ),
276             );
277 15         2888 my $result = $param_profile->( $param );
278 15 100       6179 confess $result->message unless $result;
279 14         3768 $result;
280             }
281              
282             sub _validate_defaults {
283 12     12   25 my ( $default, $required ) = @_;
284 12         26 for ( @$required ) {
285 12 100       322 confess "$_ is a required parameter but isn't in the defaults"
286             unless exists $default->{ $_ };
287             }
288 10         26 1;
289             }
290              
291             sub _validate_required {
292 13     13   24 my ( $required, $profile ) = @_;
293 13         33 for ( @$required ) {
294 13 100       204 confess "Required parameter '$_' not in the configuration profile"
295             unless exists $profile->{ $_ };
296             }
297 12         161 1;
298             }
299              
300             sub _flatten {
301 112     112   305 my $val = shift;
302 112 100       439 return unless defined $val;
303 28 100       87 return ( $val ) unless ref $val;
304 6 100       55 return @{ $val } if ref $val eq 'ARRAY';
  5         17  
305 1         174 confess "Not a valid parameter ref: " . ref $val;
306             }
307              
308             sub _check_is_profile {
309 15     15   21 my ( $sub ) = @_;
310 15 100 66     1820 confess "Given constraint not a coderef"
      66        
311             unless $sub
312             and ref $sub
313             and ref $sub eq 'CODE';
314 14         74 my $class = blessed( $sub->( 1 ) );
315             # print $class;
316 14 100 66     770 return 0
317             unless $class and $class eq "Declare::Constraints::Simple::Result";
318 10         36 1;
319             }
320              
321             {
322             my $anon_count = 0;
323              
324             sub _make_constraint {
325 4     4   9 my ( $name, $sub ) = @_;
326             # confess "No name provided" unless $name;
327 4 50       17 unless ( defined $name ) {
328             # Anonymous constraint
329 0         0 $name = sprintf( "__ANON%d__", $anon_count++ );
330             }
331 4 50 33     55 confess "Not a coderef"
332             unless $sub and ref $sub eq 'CODE';
333             # Make the constraint
334 4         38 Config::Strict::UserConstraints->make_constraint( $name => $sub );
335             # Return the new constraint
336 4         99 my $class = 'Config::Strict::UserConstraints';
337             # print "Declarations: ",Dumper( [ $class->fetch_constraint_declarations ]),"\n";
338 6     6   32351 no strict 'refs';
  6         17  
  6         442  
339 4         6 my $made = ${ $class . '::CONSTRAINT_GENERATORS' }{ $name };
  4         21  
340 6     6   38 use strict 'refs';
  6         40  
  6         543  
341             # Sanity check
342 4 50       14 croak "(assert) Generated constraint doesn't return a Result object"
343             unless _check_is_profile( $made );
344 4         10 return $made;
345             }
346             }
347              
348             1;