File Coverage

blib/lib/mop/class.pm
Criterion Covered Total %
statement 85 88 96.5
branch 14 16 87.5
condition 8 11 72.7
subroutine 16 17 94.1
pod 9 9 100.0
total 132 141 93.6


line stmt bran cond sub pod time code
1             package mop::class;
2              
3 143     143   1914 use v5.16;
  143         462  
  143         5474  
4 143     143   741 use warnings;
  143         325  
  143         4256  
5              
6 143     143   880 use mop::internals::util;
  143         297  
  143         6676  
7              
8             our $VERSION = '0.03';
9             our $AUTHORITY = 'cpan:STEVAN';
10              
11 143     143   161470 use parent 'mop::role';
  143         47345  
  143         890  
12              
13             mop::internals::util::init_attribute_storage(my %superclass);
14             mop::internals::util::init_attribute_storage(my %is_abstract);
15             mop::internals::util::init_attribute_storage(my %instance_generator);
16              
17 1504   100 1504 1 1822 sub superclass { ${ $superclass{ $_[0] } // \undef } }
  1504         9330  
18 1650   50 1650 1 1907 sub is_abstract { ${ $is_abstract{ $_[0] } // \undef } }
  1650         8793  
19 1956   50 1956 1 2089 sub instance_generator { ${ $instance_generator{ $_[0] } // \undef } }
  1956         14381  
20              
21 14     14 1 64 sub make_class_abstract { $is_abstract{ $_[0] } = \1 }
22 384     384 1 1717 sub set_instance_generator { $instance_generator{ $_[0] } = \$_[1] }
23              
24             # temporary, for bootstrapping
25             sub new {
26 5     5   8 my $class = shift;
27 5         21 my %args = @_;
28              
29 5         27 my $self = $class->SUPER::new( @_ );
30              
31 5   50     35 $is_abstract{ $self } = \($args{'is_abstract'} // 0);
32 5         14 $superclass{ $self } = \($args{'superclass'});
33 5     0   30 $instance_generator{ $self } = \(sub { \(my $anon) });
  0         0  
34              
35 5         16 $self;
36             }
37              
38             sub BUILD {
39 387     387 1 741 my $self = shift;
40              
41 387         1492 mop::internals::util::install_meta($self);
42              
43 386 100       832 if (my @nometa = grep { !mop::meta($_) } @{ $self->roles }) {
  73         244  
  386         1475  
44 1         16 die "No metaclass found for these roles: @nometa";
45             }
46              
47 385 100 100     1413 if ($self->superclass && (my $meta = mop::meta($self->superclass))) {
48 377         1162 $self->set_instance_generator($meta->instance_generator);
49              
50             # merge required methods with superclass
51             $self->add_required_method($_)
52 377         2299 for $meta->required_methods;
53              
54 377         1587 mop::apply_metaclass($self, $meta);
55             }
56             else {
57 8 100       34 mop::internals::util::mark_nonmop_class($self->superclass)
58             if $self->superclass;
59             }
60             }
61              
62             sub new_fresh_instance {
63 1579     1579 1 2040 my $self = shift;
64 1579         3503 my $instance = bless $self->instance_generator->(), $self->name;
65 1579         5251 mop::internals::util::register_object($instance);
66 1579         3234 return $instance;
67             }
68              
69             sub new_instance {
70 1581     1581 1 3456 my $self = shift;
71 1581         4155 my (%args) = @_;
72              
73 1581 100       4040 die 'Cannot instantiate abstract class (' . $self->name . ')'
74             if $self->is_abstract;
75              
76 1576         4028 my $instance = $self->new_fresh_instance;
77              
78             my %attributes = map {
79 3287 50       7716 if (my $m = mop::meta($_)) {
  1576         4320  
80 3287         4653 %{ $m->attribute_map }
  3287         8399  
81             }
82             else {
83             ()
84 0         0 }
85 1576         2281 } reverse @{ mro::get_linear_isa($self->name) };
86              
87 1576         7372 foreach my $attr (values %attributes) {
88 9823 100       26489 if ( exists $args{ $attr->key_name }) {
89 3876         10954 $attr->store_data_in_slot_for( $instance, $args{ $attr->key_name } )
90             } else {
91 5947         22106 $attr->store_default_in_slot_for( $instance );
92             }
93             }
94              
95 1573         5877 mop::internals::util::buildall($instance, \%args);
96              
97 1567         40172 return $instance;
98             }
99              
100             sub clone_instance {
101 117     117 1 189 my $self = shift;
102 117         248 my ($instance, %args) = @_;
103              
104             my $attributes = {
105             map {
106 240 50       573 if (my $m = mop::meta($_)) {
  117         390  
107 240         286 %{ $m->attribute_map }
  240         615  
108             }
109 117         173 } reverse @{ mro::get_linear_isa($self->name) }
110             };
111              
112 669         1128 %args = (
113             (map {
114 669         1202 my $attr = $attributes->{$_};
115 669 100       1649 $attr->has_data_in_slot_for($instance)
116             ? ($attr->key_name => $attr->fetch_data_in_slot_for($instance))
117             : ()
118             } grep {
119 117         481 !exists $args{ $_ }
120             } keys %$attributes),
121             %args,
122             );
123              
124 117         657 my $clone = $self->new_instance(%args);
125              
126 117         851 return $clone;
127             }
128              
129             sub __INIT_METACLASS__ {
130 143     143   818 my $METACLASS = mop::class->new(
131             name => 'mop::class',
132             version => $VERSION,
133             authority => $AUTHORITY,
134             superclass => 'mop::object',
135             );
136              
137 143         838 $METACLASS->add_attribute(mop::attribute->new(
138             name => '$!superclass',
139             storage => \%superclass,
140             ));
141 143         787 $METACLASS->add_attribute(mop::attribute->new(
142             name => '$!is_abstract',
143             storage => \%is_abstract,
144             default => 0,
145             ));
146             $METACLASS->add_attribute(mop::attribute->new(
147             name => '$!instance_generator',
148             storage => \%instance_generator,
149 375     375   2673 default => sub { sub { \(my $anon) } },
  0         0  
150 143         1295 ));
151              
152 143         896 $METACLASS->add_method( mop::method->new( name => 'BUILD', body => \&BUILD ) );
153              
154 143         819 $METACLASS->add_method( mop::method->new( name => 'superclass', body => \&superclass ) );
155              
156 143         793 $METACLASS->add_method( mop::method->new( name => 'is_abstract', body => \&is_abstract ) );
157 143         791 $METACLASS->add_method( mop::method->new( name => 'make_class_abstract', body => \&make_class_abstract ) );
158              
159 143         762 $METACLASS->add_method( mop::method->new( name => 'instance_generator', body => \&instance_generator ) );
160 143         967 $METACLASS->add_method( mop::method->new( name => 'set_instance_generator', body => \&set_instance_generator ) );
161 143         838 $METACLASS->add_method( mop::method->new( name => 'new_fresh_instance', body => \&new_fresh_instance ) );
162              
163 143         735 $METACLASS->add_method( mop::method->new( name => 'new_instance', body => \&new_instance ) );
164 143         772 $METACLASS->add_method( mop::method->new( name => 'clone_instance', body => \&clone_instance ) );
165              
166 143         1302 $METACLASS;
167             }
168              
169             1;
170              
171             __END__