File Coverage

blib/lib/Typed.pm
Criterion Covered Total %
statement 84 98 85.7
branch 13 18 72.2
condition 6 11 54.5
subroutine 17 20 85.0
pod 0 6 0.0
total 120 153 78.4


line stmt bran cond sub pod time code
1             package Typed;
2              
3 2     2   69795 use strict;
  2         5  
  2         85  
4 2     2   12 use warnings FATAL => 'all';
  2         4  
  2         194  
5 2     2   12 use feature qw(:5.10);
  2         9  
  2         341  
6              
7 2     2   19 use Carp qw();
  2         4  
  2         51  
8 2     2   11 use Scalar::Util qw(blessed);
  2         3  
  2         273  
9              
10 2     2   2413 use Types::Standard "-types";
  2         212425  
  2         32  
11 2     2   13140 use Type::Utils qw();
  2         10292  
  2         64  
12 2     2   18 use Exporter::Tiny;
  2         5  
  2         13  
13              
14 2     2   2161 use parent qw(Exporter::Tiny);
  2         650  
  2         13  
15              
16             our @EXPORT = qw(has new as from subtype);
17             our @TINY_UTILS = qw(message where inline_as declare coerce);
18              
19             our $VERSION = '0.11';
20              
21             sub import {
22 2     2   39 shift->SUPER::import({ into => scalar(caller(0)) }, @EXPORT );
23 2         893 Type::Utils->import({ into => scalar(caller(0)) }, @TINY_UTILS );
24             }
25              
26             sub new {
27 1     1 0 65 my $self = shift;
28            
29 1   33     9 my $class = ref($self) || $self;
30 1         3 my $blessed = bless({}, $class);
31              
32 1         2 my $meta_pkg = __PACKAGE__;
33 2     2   410 my $meta = do { no strict 'refs'; \%{"${meta_pkg}::meta"}; };
  2         4  
  2         1336  
  1         2  
  1         1  
  1         3  
34              
35 1         3 my %user_vals = @_;
36 1         4 foreach my $k (keys %user_vals) {
37 0         0 $blessed->$k($user_vals{$k});
38             }
39              
40 1         9 my $build = $blessed->can("BUILD");
41 1 50       4 if ($build) {
42 0         0 $build->($blessed);
43             }
44              
45 1         5 return($blessed);
46             }
47              
48             # Yes, we use a global cache for metadata
49             our %meta = (
50             );
51              
52             sub process_has {
53 5     5 0 7 my $self = shift;
54 5         7 my $name = shift;
55 5         8 my $package = shift;
56              
57 5         11 my $is = $meta{$package}{$name}{is};
58 5   66     28 my $writable = $is && "rw" eq $is;
59 5         12 my $opts = $meta{$package}{$name};
60              
61 5         6 my $default;
62            
63 5 100       12 if ($$opts{default}) {
64             $default = sub {
65 1     1   4 $$opts{default};
66 1         5 };
67             }
68              
69             my $attribute = sub {
70 24     24   7744 state $type = $meta{type}{$package}{$name};
71 24         42 state $cache = \$_[0]->{$name};
72 24         36 state $writable = $writable;
73              
74 24 100       59 if ($default) {
75 1         9 $_[0]->{$name} = $default->();
76 1         2 $default = undef;
77             }
78              
79             # Do we set the value
80 24 100       59 if (1 == $#_) {
81 16 100       34 if ($writable) {
82 15         59 my $msg = $type->validate($_[1]);
83 15 100       1181 Carp::croak($msg) if $msg;
84              
85 10         19 $$cache = $_[1];
86             }
87             else {
88 1         125 Carp::croak("Attempt to modify read-only attribute: $name");
89             }
90             }
91              
92 18 100       136 return("CODE" eq ref($$cache) ? $$cache->() : $$cache);
93 5         36 };
94              
95 5         34 return($attribute);
96             }
97              
98             sub has {
99 5     5 0 30 my $name = shift;
100 5         20 my %opts = @_;
101 5         12 my $package = caller;
102              
103 5         104 $meta{$package}{$name} = \%opts;
104            
105 5   100     172 my $isa = $opts{isa} || "Str";
106 5         12 $meta{$package}{$name}{isa} = $isa;
107              
108 5   33     32 my $type = Types::Standard->get_type($isa) || $meta{subtype}{$package}{$isa};
109 5         180 $meta{type}{$package}{$name} = $type;
110              
111 5         18 my $attribute = __PACKAGE__->process_has($name, $package);
112              
113 2     2   11 { no strict 'refs'; *{"${package}::$name"} = $attribute; };
  2         3  
  2         909  
  5         8  
  5         6  
  5         40  
114             }
115              
116             sub as (@) {
117 0 0   0 0   unless (blessed($_[0])) {
118 0           my $type = shift(@_);
119 0           unshift(@_, __PACKAGE__->$type);
120             }
121              
122 0           Type::Utils::as(@_);
123             }
124              
125             sub from (@)
126             {
127 0 0   0 0   unless (blessed($_[0])) {
128 0           my $type = shift(@_);
129 0           unshift(@_, __PACKAGE__->$type);
130             }
131              
132 0           Type::Utils::from(@_);
133             }
134              
135             sub subtype
136             {
137 0     0 0   my $subtype = Type::Utils::subtype(@_);
138 0           my $package = caller;
139 0           my $name = $_[0];
140 0           $meta{subtype}{$package}{$name} = $subtype;
141             }
142              
143             1;
144              
145             __END__