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   228287 use strict;
  16         59  
  16         442  
6 16     16   90 use warnings;
  16         27  
  16         579  
7              
8             package Raisin::Param;
9             $Raisin::Param::VERSION = '0.94';
10 16     16   70 use Carp;
  16         25  
  16         1028  
11 16         97 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   1134 );
  16         713  
23              
24 16     16   6418 use Raisin::Util;
  16         32  
  16         11561  
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 146623 my ($class, %args) = @_;
31 160         277 my $self = bless {}, $class;
32              
33 160   100     508 $self->{named} = $args{named} || 0;
34 160 100       610 $self->{required} = $args{type} =~ /^require(s|d)$/ ? 1 : 0;
35              
36 160 100       317 return unless $self->_parse($args{spec});
37              
38 159         495 $self;
39             }
40              
41             sub _parse {
42 160     160   223 my ($self, $spec) = @_;
43              
44 160         825 $self->{$_} = $spec->{$_} for @ATTRIBUTES;
45              
46 160 100       297 if ($spec->{in}) {
47 8 100       18 return unless $self->in($spec->{in});
48             }
49              
50 159 100       252 if ($spec->{encloses}) {
51 30 100       61 if ($self->type->name eq 'HashRef') {
52 28         191 $self->{enclosed} = _compile_enclosed($spec->{encloses});
53             }
54             else {
55 2         14 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       978 $self->{coerce} = defined($spec->{coerce}) ? $spec->{coerce} : 1;
63              
64 159         266 return 1;
65             }
66              
67             sub _compile_enclosed {
68 28     28   31 my $params = shift;
69              
70 28         30 my @enclosed;
71 28         50 my $next_param = Raisin::Util::iterate_params($params);
72 28         52 while (my ($type, $spec) = $next_param->()) {
73 84 100       121 last unless $type;
74              
75 56         114 push @enclosed, Raisin::Param->new(
76             named => 0,
77             type => $type, # -> requires/optional
78             spec => $spec, # -> { name => ..., type => ... }
79             );
80             }
81              
82 28         90 \@enclosed;
83             }
84              
85 16     16 1 24 sub display_name { shift->name }
86              
87             sub in {
88 58     58 1 2234 my ($self, $value) = @_;
89              
90 58 100       101 if (defined $value) {
91 8 100       12 unless (grep { $value eq $_ } @LOCATIONS) {
  40         69  
92 1         3 Raisin::log(warn => '`%s` should be one of: %s',
93             $self->name, join ', ', @LOCATIONS);
94 1         370 return;
95             }
96              
97 7         12 $self->{in} = $value;
98             }
99              
100 57         148 $self->{in};
101             }
102              
103             sub validate {
104 128     128 1 5823 my ($self, $ref_value, $quiet) = @_;
105              
106             # Required and empty
107             # Only optional parameters can have default value
108 128 100 100     219 if ($self->required && !defined($$ref_value)) {
109 3 50       25 Raisin::log(warn => '`%s` is required', $self->name) unless $quiet;
110 3         1401 return;
111             }
112              
113             # Optional and empty
114 125 100 100     652 if (!$self->required && !defined($$ref_value)) {
115 21         137 Raisin::log(info => '`%s` optional and empty', $self->name);
116 21         8151 return 1;
117             }
118              
119             # Type check
120 104         407 eval {
121 104 100 66     192 if ($self->type->has_coercion && $self->coerce) {
122 1         28 $$ref_value = $self->type->coerce($$ref_value);
123             }
124              
125 104 50       2552 if ($self->type->isa('Moose::Meta::TypeConstraint')) {
126 0         0 $self->type->assert_valid($$ref_value);
127             }
128             else {
129 104         1184 $$ref_value = $self->type->($$ref_value);
130             }
131             };
132 104 100       10695 if (my $e = $@) {
133 6 100       39 unless ($quiet) {
134 5         14 Raisin::log(warn => 'Param `%s` didn\'t pass constraint `%s` with value "%s"',
135             $self->name, $self->type->name, $$ref_value);
136             }
137 6         1801 return;
138             }
139              
140             # Nested
141 98 100 66     201 if ($self->type->name eq 'HashRef' && $self->enclosed) {
    100 100        
142 11         89 for my $p (@{ $self->enclosed }) {
  11         17  
143 19         42 my $v = $$ref_value;
144              
145 19 50       26 if ($self->type->name eq 'HashRef') {
146 19         92 $v = $v->{ $p->name };
147             }
148              
149 19 100       72 return unless $p->validate(\$v, $quiet);
150             }
151             }
152             # Regex
153             elsif ($self->regex && $$ref_value !~ $self->regex) {
154 3 50       74 unless ($quiet) {
155 3         7 Raisin::log(warn => 'Param `%s` didn\'t match regex `%s` with value "%s"',
156             $self->name, $self->regex, $$ref_value);
157             }
158 3         918 return;
159             }
160              
161 91         1016 1;
162             }
163              
164             1;
165              
166             __END__