File Coverage

blib/lib/Salvation/TC/Meta/Type.pm
Criterion Covered Total %
statement 77 93 82.8
branch 17 28 60.7
condition 11 26 42.3
subroutine 21 26 80.7
pod 18 18 100.0
total 144 191 75.3


line stmt bran cond sub pod time code
1             package Salvation::TC::Meta::Type;
2              
3             =head1 NAME
4              
5             Salvation::TC::Meta::Type - Класс для простых типов
6              
7             =cut
8              
9 4     4   16 use strict;
  4         5  
  4         134  
10 4     4   17 use warnings;
  4         4  
  4         94  
11 4     4   16 use boolean;
  4         4  
  4         26  
12              
13 4     4   305 use base 'Salvation::TC::Meta';
  4         4  
  4         1334  
14              
15 4     4   19 use Scalar::Util 'blessed';
  4         4  
  4         3120  
16              
17             =head1 METHODS
18              
19             =cut
20              
21             =head2 new()
22              
23             =cut
24              
25             sub new {
26              
27 181     181 1 472 my ( $proto, %args ) = @_;
28              
29 181 50       343 die( 'Type metaclass must have validator' ) unless( defined $args{ 'validator' } );
30 181 50       374 die( 'Validator must be a CodeRef' ) if( ref( $args{ 'validator' } ) ne 'CODE' );
31              
32 181         276 $args{ 'coercion_map' } = []; # Salvation::TC::Meta::Type => CodeRef
33              
34 181 100       387 if( exists $args{ 'parent' } ) {
35              
36 9 50 33     86 unless(
      33        
37             defined $args{ 'parent' } && blessed $args{ 'parent' }
38             && $args{ 'parent' } -> isa( 'Salvation::TC::Meta::Type' )
39             ) {
40              
41 0         0 die( 'Parent type must be a Salvation::TC::Meta::Type' );
42             }
43              
44 9         11 my $self_validator = $args{ 'validator' };
45 9         18 my $parent_validator = $args{ 'parent' } -> validator();
46              
47             $args{ 'validator' } = sub {
48 27 50   27   53 $parent_validator -> ( $_[ 0 ] ) && $self_validator -> ( $_[ 0 ] )
49 9         34 };
50             }
51              
52 181 100       309 if( exists $args{ 'base' } ) {
53              
54 77 50 33     576 unless(
      33        
55             defined $args{ 'base' } && blessed $args{ 'base' }
56             && $args{ 'base' } -> isa( 'Salvation::TC::Meta::Type' )
57             ) {
58              
59 0         0 die( 'Base type must be a Salvation::TC::Meta::Type' );
60             }
61             }
62              
63 181         466 foreach my $spec (
64             [ 'signed_type_generator', 'Signed type generator' ],
65             [ 'length_type_generator', 'Length type generator' ],
66             ) {
67 362         378 my ( $key, $name ) = @$spec;
68              
69 362 50 100     1590 if( exists $args{ $key } && defined $args{ $key } && ( ref( $args{ $key } ) ne 'CODE' ) ) {
      66        
70              
71 0         0 die( "${name} must be a CodeRef" );
72             }
73             }
74              
75 181         773 return $proto -> SUPER::new( %args );
76             }
77              
78             =head2 validator()
79              
80             =cut
81              
82             sub validator {
83              
84 411     411 1 393 my ( $self ) = @_;
85              
86 411         918 return $self -> { 'validator' };
87             }
88              
89             =head2 check( Any $value )
90              
91             =cut
92              
93             sub check {
94              
95 355     355 1 394 my ( $self, $value ) = @_;
96              
97 355         580 return $self -> validator() -> ( $value );
98             }
99              
100             =head2 signature()
101              
102             =cut
103              
104             sub signature {
105              
106 1     1 1 1 my ( $self ) = @_;
107              
108 1         7 return $self -> { 'signature' };
109             }
110              
111             =head2 options()
112              
113             =cut
114              
115             sub options {
116              
117 1     1 1 1 my ( $self ) = @_;
118              
119 1   50     13 return $self -> { 'options' } //= {};
120             }
121              
122             =head2 has_signature()
123              
124             =cut
125              
126             sub has_signature {
127              
128 24     24 1 21 my ( $self ) = @_;
129              
130 24         105 return exists $self -> { 'signature' };
131             }
132              
133             =head2 is_signature_strict()
134              
135             =cut
136              
137             sub is_signature_strict {
138              
139 0     0 1 0 my ( $self ) = @_;
140              
141 0   0     0 return ( $self -> has_signature() && !! ( $self -> options() -> { 'strict' } // 0 ) );
142             }
143              
144             =head2 coercion_map()
145              
146             =cut
147              
148             sub coercion_map {
149              
150 11     11 1 11 my ( $self ) = @_;
151              
152 11         19 return $self -> { 'coercion_map' };
153             }
154              
155             =head2 parent()
156              
157             =cut
158              
159             sub parent {
160              
161 0     0 1 0 my ( $self ) = @_;
162              
163 0         0 return $self -> { 'parent' };
164             }
165              
166             =head2 base()
167              
168             =cut
169              
170             sub base {
171              
172 0     0 1 0 my ( $self ) = @_;
173              
174 0         0 return $self -> { 'base' };
175             }
176              
177             =head2 has_base()
178              
179             =cut
180              
181             sub has_base {
182              
183 0     0 1 0 my ( $self ) = @_;
184              
185 0         0 return exists $self -> { 'base' };
186             }
187              
188             =head2 has_parent()
189              
190             =cut
191              
192             sub has_parent {
193              
194 0     0 1 0 my ( $self ) = @_;
195              
196 0         0 return exists $self -> { 'parent' };
197             }
198              
199             =head2 signed_type_generator()
200              
201             =cut
202              
203             sub signed_type_generator {
204              
205 45     45 1 46 my ( $self ) = @_;
206              
207 45         97 return $self -> { 'signed_type_generator' };
208             }
209              
210             =head2 length_type_generator()
211              
212             =cut
213              
214             sub length_type_generator {
215              
216 77     77 1 75 my ( $self ) = @_;
217              
218 77         198 return $self -> { 'length_type_generator' };
219             }
220              
221             =head2 sign( ArrayRef signature, HashRef options )
222              
223             Генерирует валидатор для текущего типа на основе подписи.
224              
225             =cut
226              
227             sub sign {
228              
229 11     11 1 12 my ( $self, $signature, $options ) = @_;
230              
231 11         21 my $signed_type_generator = $self -> signed_type_generator();
232              
233 11 50       21 unless( defined $signed_type_generator ) {
234              
235 0         0 die( sprintf( 'Type %s cannot be signed', $self -> name() ) )
236             }
237              
238 11         26 my $signed_validator = $signed_type_generator -> ( $signature, $options );
239              
240             return sub {
241              
242 22 50   22   41 $self -> check( $_[ 0 ] ) && $signed_validator -> ( $_[ 0 ] )
243 11         42 };
244             }
245              
246             =head2 length_checker( Int $min, Maybe[Int] $max )
247              
248             Генерирует валидатор для текущего типа на основе спецификации длины.
249              
250             =cut
251              
252             sub length_checker {
253              
254 19     19 1 25 my ( $self, $min, $max ) = @_;
255              
256 19         37 my $length_type_generator = $self -> length_type_generator();
257              
258 19 50       56 unless( defined $length_type_generator ) {
259              
260 0         0 die( sprintf( 'Length of type %s could not be checked', $self -> name() ) );
261             }
262              
263 19         45 my $length_validator = $length_type_generator -> ( $min, $max );
264              
265             return sub {
266              
267 32 50   32   56 $self -> check( $_[ 0 ] ) && $length_validator -> ( $_[ 0 ] )
268 19         72 };
269             }
270              
271             =head2 add_coercion( Salvation::TC::Meta::Type $from, CodeRef $how )
272              
273             =cut
274              
275             sub add_coercion {
276              
277 5     5 1 8 my ( $self, $from, $how ) = @_;
278              
279 5         3 push( @{ $self -> { 'coercion_map' } }, [ $from => $how ] );
  5         12  
280              
281 5         7 return;
282             }
283              
284             =head2 coerce( Any $value )
285              
286             =cut
287              
288             sub coerce {
289              
290 11     11 1 12 my ( $self, $value ) = @_;
291              
292 11         8 foreach my $rule ( @{ $self -> coercion_map() } ) {
  11         23  
293              
294             {
295 10         9 local $SIG{ '__DIE__' } = 'DEFAULT';
  10         25  
296              
297 10         11 eval { $rule -> [ 0 ] -> check( $value ) };
  10         19  
298             }
299              
300 10 100       41 if( $@ ) {
301              
302 3 50 33     17 if( blessed( $@ ) && $@ -> isa( 'Salvation::TC::Exception::WrongType' ) ) {
303              
304 3         7 next;
305              
306             } else {
307              
308 0         0 die( $@ );
309             }
310             };
311              
312 7         7 local $_ = $value; # Ради соответствия API Moose
313              
314 7         15 $value = $rule -> [ 1 ] -> ();
315              
316 7         26 last;
317             }
318              
319 11         40 return $value; # Moose возвращает либо старое, либо приведённое значение
320             }
321              
322             1;
323              
324             __END__