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   300610 use strict;
  16         63  
  16         699  
6 16     16   117 use warnings;
  16         30  
  16         821  
7              
8             package Raisin::Param;
9             $Raisin::Param::VERSION = '0.93';
10 16     16   102 use Carp;
  16         30  
  16         1194  
11 16         136 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   1575 );
  16         905  
23              
24 16     16   8304 use Raisin::Util;
  16         45  
  16         13635  
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 167819 my ($class, %args) = @_;
31 160         338 my $self = bless {}, $class;
32              
33 160   100     649 $self->{named} = $args{named} || 0;
34 160 100       766 $self->{required} = $args{type} =~ /^require(s|d)$/ ? 1 : 0;
35              
36 160 100       359 return unless $self->_parse($args{spec});
37              
38 159         599 $self;
39             }
40              
41             sub _parse {
42 160     160   274 my ($self, $spec) = @_;
43              
44 160         1079 $self->{$_} = $spec->{$_} for @ATTRIBUTES;
45              
46 160 100       376 if ($spec->{in}) {
47 8 100       25 return unless $self->in($spec->{in});
48             }
49              
50 159 100       303 if ($spec->{encloses}) {
51 30 100       81 if ($self->type->name eq 'HashRef') {
52 28         283 $self->{enclosed} = _compile_enclosed($spec->{encloses});
53             }
54             else {
55 2         16 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       1090 $self->{coerce} = defined($spec->{coerce}) ? $spec->{coerce} : 1;
63              
64 159         348 return 1;
65             }
66              
67             sub _compile_enclosed {
68 28     28   44 my $params = shift;
69              
70 28         40 my @enclosed;
71 28         62 my $next_param = Raisin::Util::iterate_params($params);
72 28         64 while (my ($type, $spec) = $next_param->()) {
73 84 100       159 last unless $type;
74              
75 56         121 push @enclosed, Raisin::Param->new(
76             named => 0,
77             type => $type, # -> requires/optional
78             spec => $spec, # -> { name => ..., type => ... }
79             );
80             }
81              
82 28         114 \@enclosed;
83             }
84              
85 16     16 1 29 sub display_name { shift->name }
86              
87             sub in {
88 58     58 1 2164 my ($self, $value) = @_;
89              
90 58 100       142 if (defined $value) {
91 8 100       19 unless (grep { $value eq $_ } @LOCATIONS) {
  40         82  
92 1         5 Raisin::log(warn => '`%s` should be one of: %s',
93             $self->name, join ', ', @LOCATIONS);
94 1         374 return;
95             }
96              
97 7         14 $self->{in} = $value;
98             }
99              
100 57         192 $self->{in};
101             }
102              
103             sub validate {
104 122     122 1 7125 my ($self, $ref_value, $quiet) = @_;
105              
106             # Required and empty
107             # Only optional parameters can have default value
108 122 100 100     308 if ($self->required && !defined($$ref_value)) {
109 3 50       27 Raisin::log(warn => '`%s` is required', $self->name) unless $quiet;
110 3         1247 return;
111             }
112              
113             # Optional and empty
114 119 100 100     734 if (!$self->required && !defined($$ref_value)) {
115 21         143 Raisin::log(info => '`%s` optional and empty', $self->name);
116 21         8324 return 1;
117             }
118              
119             # Type check
120 98         478 eval {
121 98 100 66     211 if ($self->type->has_coercion && $self->coerce) {
122 1         32 $$ref_value = $self->type->coerce($$ref_value);
123             }
124              
125 98 50       3067 if ($self->type->isa('Moose::Meta::TypeConstraint')) {
126 0         0 $self->type->assert_valid($$ref_value);
127             }
128             else {
129 98         1370 $$ref_value = $self->type->($$ref_value);
130             }
131             };
132 98 100       13205 if (my $e = $@) {
133 6 100       47 unless ($quiet) {
134 5         16 Raisin::log(warn => 'Param `%s` didn\'t pass constraint `%s` with value "%s"',
135             $self->name, $self->type->name, $$ref_value);
136             }
137 6         1975 return;
138             }
139              
140             # Nested
141 92 100 66     249 if ($self->type->name eq 'HashRef' && $self->enclosed) {
    100 100        
142 11         109 for my $p (@{ $self->enclosed }) {
  11         21  
143 19         51 my $v = $$ref_value;
144              
145 19 50       36 if ($self->type->name eq 'HashRef') {
146 19         109 $v = $v->{ $p->name };
147             }
148              
149 19 100       96 return unless $p->validate(\$v, $quiet);
150             }
151             }
152             # Regex
153             elsif ($self->regex && $$ref_value !~ $self->regex) {
154 3 50       58 unless ($quiet) {
155 3         9 Raisin::log(warn => 'Param `%s` didn\'t match regex `%s` with value "%s"',
156             $self->name, $self->regex, $$ref_value);
157             }
158 3         1176 return;
159             }
160              
161 85         1210 1;
162             }
163              
164             1;
165              
166             __END__