|
| 1 | +package Meowse::Meta::Class; |
| 2 | +use Meowse::Meta::Attribute; |
| 3 | +use Meowse::Meta::Method; |
| 4 | + |
| 5 | +use Exporter qw(import); |
| 6 | +our @EXPORT = ('_get_meta_by_class'); |
| 7 | + |
| 8 | +use Carp; |
| 9 | +use Data::Dumper; |
| 10 | +use Scalar::Util qw(blessed); |
| 11 | +use feature 'say'; |
| 12 | + |
| 13 | +my %METAS; |
| 14 | + |
| 15 | + |
| 16 | +sub initialize { |
| 17 | + my ($class, $pkg) = @_; |
| 18 | + return $METAS{$pkg} ||= $class->_create_meta(package => $pkg); |
| 19 | +} |
| 20 | +sub add_attribute { |
| 21 | + my $self = shift; |
| 22 | + my $name = shift; |
| 23 | + $name or croak "Name of attribute must be defined"; |
| 24 | + $self->{attributes}->{$name} = Meowse::Meta::Attribute->new($self, 'name' => $name, @_); |
| 25 | +} |
| 26 | +sub superclasses { |
| 27 | + my $self = shift; |
| 28 | + my $super_class = shift; |
| 29 | + my $super_meta = _get_meta_by_class($super_class); |
| 30 | + @{ $self->{superclasses} } = ($super_class) if ($super_meta); |
| 31 | +} |
| 32 | + |
| 33 | +sub _get_meta_by_class { |
| 34 | + my $super = shift; |
| 35 | + my $meta = $METAS{$super}; |
| 36 | + #carp "$super is not Meowse's class!" unless $meta; |
| 37 | + return $meta; |
| 38 | +} |
| 39 | +sub _create_meta { |
| 40 | + my ($class, %args) = @_; |
| 41 | + $args{attributes} = {}; |
| 42 | + $args{methods} = {}; |
| 43 | + $args{superclasses} = do { |
| 44 | + no strict 'refs'; |
| 45 | + \@{$args{package}.'::ISA'}; |
| 46 | + }; |
| 47 | + push @{$args{superclasses}}, 'Meowse::Object'; |
| 48 | + my $self = bless \%args, $class; |
| 49 | + return $self; |
| 50 | +} |
| 51 | + |
| 52 | +sub set_decorator { |
| 53 | + my ($self, $type, $name, $code) = @_; |
| 54 | + my $class = $self->{package}; |
| 55 | + #TODO CHECK |
| 56 | + my $orig = $class->can($name) or carp "$name was not found!"; |
| 57 | + if (!$self->{decorators}->{$name}) { |
| 58 | + my (@before, @after, @around); |
| 59 | + my $next = $orig; |
| 60 | + my $decorator = sub { |
| 61 | + if (@before) { |
| 62 | + $_->(@_) for(@before); |
| 63 | + } |
| 64 | + unless (@after) { |
| 65 | + return $next->(@_); |
| 66 | + } |
| 67 | + if (wantarray) { # list context |
| 68 | + my @val = $next->(@_); |
| 69 | + $_->(@_) for(@after); |
| 70 | + return @val; |
| 71 | + } elsif (defined wantarray) { # Scalar |
| 72 | + my $val = $next->(@_); |
| 73 | + $_->(@_) for(@after); |
| 74 | + return $val; |
| 75 | + } else { # void |
| 76 | + $next->(@_); |
| 77 | + $_->(@_) for(@after); |
| 78 | + return; |
| 79 | + } |
| 80 | + }; |
| 81 | + $self->{decorators}->{$name} = { |
| 82 | + before => \@before, |
| 83 | + after => \@after, |
| 84 | + around => \@around, |
| 85 | + next => \$next, |
| 86 | + }; |
| 87 | + $self->add_method($name, $name, $decorator); |
| 88 | + } |
| 89 | + if ($type eq 'before') { |
| 90 | + push @{ $self->{decorators}->{$name}->{before} }, $code; |
| 91 | + } elsif ($type eq 'after') { |
| 92 | + push @{ $self->{decorators}->{$name}->{after} }, $code; |
| 93 | + } else { # around |
| 94 | + push @{ $self->{decorators}->{$name}->{around} }, $code; |
| 95 | + my $next = ${ $self->{decorators}->{$name}->{next} }; |
| 96 | + ${ $self->{decorators}->{$name}->{next} } = sub { $code->($next, @_) }; |
| 97 | + } |
| 98 | + return; |
| 99 | +} |
| 100 | + |
| 101 | +sub add_method { |
| 102 | + my ($self, $name_method, $name_attr, $method) = @_; |
| 103 | + # $method either generator or code ref |
| 104 | + $self->{methods}->{$name_method} = Meowse::Meta::Method->new($name_attr, $method); |
| 105 | + *{$self->{package}.'::'.$name_method} = $self->{methods}->{$name_method}->code; |
| 106 | +} |
| 107 | + |
| 108 | +sub validate_attr { |
| 109 | + my ($self, %data) = @_; |
| 110 | + my $super_meta = _get_meta_by_class(@{ $self->{superclasses} }); |
| 111 | + my %attributes = (%{ $self->{attributes} }, %{ $super_meta->{attributes} }); # All attributes |
| 112 | + my @bad; |
| 113 | + for my $attr (keys %attributes) { |
| 114 | + if ($attributes{$attr}->is_required && !$data{$attr}) { |
| 115 | + push @bad, $attr; |
| 116 | + } |
| 117 | + } |
| 118 | + croak "Attributes [@bad] are required!" if (@bad); |
| 119 | +} |
| 120 | +1; |
0 commit comments