File Coverage

blib/lib/Raisin/Param.pm
Criterion Covered Total %
statement 74 75 98.6
branch 39 44 88.6
condition 15 17 88.2
subroutine 11 11 100.0
pod 3 4 75.0
total 142 151 94.0


line stmt bran cond sub pod time code
1             #!perl
2             #PODNAME: Raisin::Param
3             #ABSTRACT: Parameter class for Raisin
4              
5 16     16   286804 use strict;
  16         67  
  16         591  
6 16     16   129 use warnings;
  16         31  
  16         753  
7              
8             package Raisin::Param;
9             $Raisin::Param::VERSION = '0.92';
10 16     16   113 use Carp;
  16         27  
  16         1162  
11 16         132 use Plack::Util::Accessor qw(
12             named
13             required
14              
15             default
16             desc
17             enclosed
18             name
19             regex
20             type
21             coerce
22 16     16   1585 );
  16         875  
23              
24 16     16   8210 use Raisin::Util;
  16         43  
  16         13645  
25              
26             my @ATTRIBUTES = qw(name type default regex desc coerce);
27             my @LOCATIONS = qw(path formData body header query);
28              
29             sub new {
30 160     160 0 166393 my ($class, %args) = @_;
31 160         355 my $self = bless {}, $class;
32              
33 160   100     657 $self->{named} = $args{named} || 0;
34 160 100       727 $self->{required} = $args{type} =~ /^require(s|d)$/ ? 1 : 0;
35              
36 160 100       354 return unless $self->_parse($args{spec});
37              
38 159         595 $self;
39             }
40              
41             sub _parse {
42 160     160   289 my ($self, $spec) = @_;
43              
44 160         985 $self->{$_} = $spec->{$_} for @ATTRIBUTES;
45              
46 160 100       395 if ($spec->{in}) {
47 8 100       22 return unless $self->in($spec->{in});
48             }
49              
50 159 100       301 if ($spec->{encloses}) {
51 30 100       81 if ($self->type->name eq 'HashRef') {
52 28         237 $self->{enclosed} = _compile_enclosed($spec->{encloses});
53             }
54             else {
55 2         17 Raisin::log(
56             warn => 'Ignoring enclosed parameters for `%s`, type should be `HashRef` not `%s`',
57             $self->name, $self->type->name
58             );
59             }
60             }
61              
62 159 50       1070 $self->{coerce} = defined($spec->{coerce}) ? $spec->{coerce} : 1;
63              
64 159         334 return 1;
65             }
66              
67             sub _compile_enclosed {
68 28     28   40 my $params = shift;
69              
70 28         36 my @enclosed;
71 28         55 my $next_param = Raisin::Util::iterate_params($params);
72 28         103 while (my ($type, $spec) = $next_param->()) {
73 84 100       161 last unless $type;
74              
75 56         116 push @enclosed, Raisin::Param->new(
76             named => 0,
77             type => $type, # -> requires/optional
78             spec => $spec, # -> { name => ..., type => ... }
79             );
80             }
81              
82 28         107 \@enclosed;
83             }
84              
85 16     16 1 28 sub display_name { shift->name }
86              
87             sub in {
88 58     58 1 2169 my ($self, $value) = @_;
89              
90 58 100       134 if (defined $value) {
91 8 100       18 unless (grep { $value eq $_ } @LOCATIONS) {
  40         81  
92 1         4 Raisin::log(warn => '`%s` should be one of: %s',
93             $self->name, join ', ', @LOCATIONS);
94 1         407 return;
95             }
96              
97 7         16 $self->{in} = $value;
98             }
99              
100 57         186 $self->{in};
101             }
102              
103             sub validate {
104 122     122 1 7130 my ($self, $ref_value, $quiet) = @_;
105              
106             # Required and empty
107             # Only optional parameters can have default value
108 122 100 100     260 if ($self->required && !defined($$ref_value)) {
109 3 50       32 Raisin::log(warn => '`%s` is required', $self->name) unless $quiet;
110 3         1555 return;
111             }
112              
113             # Optional and empty
114 119 100 100     745 if (!$self->required && !defined($$ref_value)) {
115 21         136 Raisin::log(info => '`%s` optional and empty', $self->name);
116 21         8543 return 1;
117             }
118              
119             # Type check
120 98         464 eval {
121 98 100 66     206 if ($self->type->has_coercion && $self->coerce) {
122 1         31 $$ref_value = $self->type->coerce($$ref_value);
123             }
124              
125 98 50       3102 if ($self->type->isa('Moose::Meta::TypeConstraint')) {
126 0         0 $self->type->assert_valid($$ref_value);
127             }
128             else {
129 98         1451 $$ref_value = $self->type->($$ref_value);
130             }
131             };
132 98 100       13549 if (my $e = $@) {
133 6 100       48 unless ($quiet) {
134 5         18 Raisin::log(warn => 'Param `%s` didn\'t pass constraint `%s` with value "%s"',
135             $self->name, $self->type->name, $$ref_value);
136             }
137 6         2281 return;
138             }
139              
140             # Nested
141 92 100 66     256 if ($self->type->name eq 'HashRef' && $self->enclosed) {
    100 100        
142 11         114 for my $p (@{ $self->enclosed }) {
  11         21  
143 19         53 my $v = $$ref_value;
144              
145 19 50       34 if ($self->type->name eq 'HashRef') {
146 19         110 $v = $v->{ $p->name };
147             }
148              
149 19 100       97 return unless $p->validate(\$v, $quiet);
150             }
151             }
152             # Regex
153             elsif ($self->regex && $$ref_value !~ $self->regex) {
154 3 50       56 unless ($quiet) {
155 3         8 Raisin::log(warn => 'Param `%s` didn\'t match regex `%s` with value "%s"',
156             $self->name, $self->regex, $$ref_value);
157             }
158 3         1157 return;
159             }
160              
161 85         1222 1;
162             }
163              
164             1;
165              
166             __END__