X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FClass%2FMOP%2FPackage.pm;h=e5dbd4af3fb7d8c40a814b75e79dbbb802d1013c;hb=c4260b45e76ce008e4c51987b243f2b0ae4313bb;hp=0f9849da382ddda84ef71d6057cb91c3afa47d26;hpb=1a09d9cce4930577a39060a03029a32cd51d41c7;p=gitmo%2FClass-MOP.git diff --git a/lib/Class/MOP/Package.pm b/lib/Class/MOP/Package.pm index 0f9849d..e5dbd4a 100644 --- a/lib/Class/MOP/Package.pm +++ b/lib/Class/MOP/Package.pm @@ -7,7 +7,10 @@ use warnings; use Scalar::Util 'blessed'; use Carp 'confess'; -our $VERSION = '0.01'; +our $VERSION = '0.04'; +our $AUTHORITY = 'cpan:STEVAN'; + +use base 'Class::MOP::Object'; # introspection @@ -19,8 +22,22 @@ sub meta { # creation ... sub initialize { - my ($class, $package) = @_; - bless { '$:package' => $package } => $class; + my $class = shift; + my $package_name = shift; + # we hand-construct the class + # until we can bootstrap it + no strict 'refs'; + return bless { + '$:package' => $package_name, + # NOTE: + # because of issues with the Perl API + # to the typeglob in some versions, we + # need to just always grab a new + # reference to the hash in the accessor. + # Ideally we could just store a ref and + # it would Just Work, but oh well :\ + '%:namespace' => \undef, + } => $class; } # Attributes @@ -29,9 +46,20 @@ sub initialize { # all these attribute readers will be bootstrapped # away in the Class::MOP bootstrap section -sub name { $_[0]->{'$:package'} } +sub name { $_[0]->{'$:package'} } +sub namespace { + # NOTE: + # because of issues with the Perl API + # to the typeglob in some versions, we + # need to just always grab a new + # reference to the hash here. Ideally + # we could just store a ref and it would + # Just Work, but oh well :\ + no strict 'refs'; + \%{$_[0]->name . '::'} +} -# Class attributes +# utility methods { my %SIGIL_MAP = ( @@ -40,94 +68,127 @@ sub name { $_[0]->{'$:package'} } '%' => 'HASH', '&' => 'CODE', ); - - sub add_package_symbol { - my ($self, $variable, $initial_value) = @_; + sub _deconstruct_variable_name { + my ($self, $variable) = @_; + (defined $variable) || confess "You must pass a variable name"; - + my ($sigil, $name) = ($variable =~ /^(.)(.*)$/); - + (defined $sigil) || confess "The variable name must include a sigil"; - + (exists $SIGIL_MAP{$sigil}) - || confess "I do not recognize that sigil '$sigil'"; - - no strict 'refs'; - no warnings 'misc'; - *{$self->name . '::' . $name} = $initial_value; + || confess "I do not recognize that sigil '$sigil'"; + + return ($name, $sigil, $SIGIL_MAP{$sigil}); } +} - sub has_package_symbol { - my ($self, $variable) = @_; - (defined $variable) - || confess "You must pass a variable name"; +# Class attributes - my ($sigil, $name) = ($variable =~ /^(.)(.*)$/); - - (defined $sigil) - || confess "The variable name must include a sigil"; +# ... these functions have to touch the symbol table itself,.. yuk + +sub add_package_symbol { + my ($self, $variable, $initial_value) = @_; + + my ($name, $sigil, $type) = $self->_deconstruct_variable_name($variable); + + no strict 'refs'; + no warnings 'redefine', 'misc'; + *{$self->name . '::' . $name} = ref $initial_value ? $initial_value : \$initial_value; +} + +sub remove_package_glob { + my ($self, $name) = @_; + no strict 'refs'; + delete ${$self->name . '::'}{$name}; +} + +# ... these functions deal with stuff on the namespace level + +sub has_package_symbol { + my ($self, $variable) = @_; + + my ($name, $sigil, $type) = $self->_deconstruct_variable_name($variable); - (exists $SIGIL_MAP{$sigil}) - || confess "I do not recognize that sigil '$sigil'"; + return 0 unless exists $self->namespace->{$name}; - no strict 'refs'; - defined *{$self->name . '::' . $name}{$SIGIL_MAP{$sigil}} ? 1 : 0; + # FIXME: + # For some really stupid reason + # a typeglob will have a default + # value of \undef in the SCALAR + # slot, so we need to work around + # this. Which of course means that + # if you put \undef in your scalar + # then this is broken. + if ($type eq 'SCALAR') { + my $val = *{$self->namespace->{$name}}{$type}; + defined(${$val}) ? 1 : 0; } + else { + defined(*{$self->namespace->{$name}}{$type}) ? 1 : 0; + } +} - sub get_package_symbol { - my ($self, $variable) = @_; - (defined $variable) - || confess "You must pass a variable name"; - - my ($sigil, $name) = ($variable =~ /^(.)(.*)$/); - - (defined $sigil) - || confess "The variable name must include a sigil"; - - (exists $SIGIL_MAP{$sigil}) - || confess "I do not recognize that sigil '$sigil'"; - - no strict 'refs'; - return *{$self->name . '::' . $name}{$SIGIL_MAP{$sigil}}; +sub get_package_symbol { + my ($self, $variable) = @_; - } + my ($name, $sigil, $type) = $self->_deconstruct_variable_name($variable); - sub remove_package_symbol { - my ($self, $variable) = @_; - - (defined $variable) - || confess "You must pass a variable name"; + $self->add_package_symbol($variable) + unless exists $self->namespace->{$name}; + return *{$self->namespace->{$name}}{$type}; +} + +sub remove_package_symbol { + my ($self, $variable) = @_; + + my ($name, $sigil, $type) = $self->_deconstruct_variable_name($variable); + + # FIXME: + # no doubt this is grossly inefficient and + # could be done much easier and faster in XS + + my ($scalar, $array, $hash, $code); + if ($type eq 'SCALAR') { + $array = $self->get_package_symbol('@' . $name) if $self->has_package_symbol('@' . $name); + $hash = $self->get_package_symbol('%' . $name) if $self->has_package_symbol('%' . $name); + $code = $self->get_package_symbol('&' . $name) if $self->has_package_symbol('&' . $name); + } + elsif ($type eq 'ARRAY') { + $scalar = $self->get_package_symbol('$' . $name) if $self->has_package_symbol('$' . $name); + $hash = $self->get_package_symbol('%' . $name) if $self->has_package_symbol('%' . $name); + $code = $self->get_package_symbol('&' . $name) if $self->has_package_symbol('&' . $name); + } + elsif ($type eq 'HASH') { + $scalar = $self->get_package_symbol('$' . $name) if $self->has_package_symbol('$' . $name); + $array = $self->get_package_symbol('@' . $name) if $self->has_package_symbol('@' . $name); + $code = $self->get_package_symbol('&' . $name) if $self->has_package_symbol('&' . $name); + } + elsif ($type eq 'CODE') { + $scalar = $self->get_package_symbol('$' . $name) if $self->has_package_symbol('$' . $name); + $array = $self->get_package_symbol('@' . $name) if $self->has_package_symbol('@' . $name); + $hash = $self->get_package_symbol('%' . $name) if $self->has_package_symbol('%' . $name); + } + else { + confess "This should never ever ever happen"; + } - my ($sigil, $name) = ($variable =~ /^(.)(.*)$/); + $self->remove_package_glob($name); - (defined $sigil) - || confess "The variable name must include a sigil"; - - (exists $SIGIL_MAP{$sigil}) - || confess "I do not recognize that sigil '$sigil'"; - - no strict 'refs'; - if ($SIGIL_MAP{$sigil} eq 'SCALAR') { - undef ${$self->name . '::' . $name}; - } - elsif ($SIGIL_MAP{$sigil} eq 'ARRAY') { - undef @{$self->name . '::' . $name}; - } - elsif ($SIGIL_MAP{$sigil} eq 'HASH') { - undef %{$self->name . '::' . $name}; - } - elsif ($SIGIL_MAP{$sigil} eq 'CODE') { - undef &{$self->name . '::' . $name}; - } - else { - confess "This should never ever ever happen"; - } - } + $self->add_package_symbol(('$' . $name) => $scalar) if defined $scalar; + $self->add_package_symbol(('@' . $name) => $array) if defined $array; + $self->add_package_symbol(('%' . $name) => $hash) if defined $hash; + $self->add_package_symbol(('&' . $name) => $code) if defined $code; +} +sub list_all_package_symbols { + my ($self) = @_; + return keys %{$self->namespace}; } 1; @@ -150,17 +211,50 @@ Class::MOP::Package - Package Meta Object =item B -=item B +=item B =item B -=item B +This is a read-only attribute which returns the package name for the +given instance. + +=item B + +This returns a HASH reference to the symbol table. The keys of the +HASH are the symbol names, and the values are typeglob references. + +=item B + +Given a C<$variable_name>, which must contain a leading sigil, this +method will create that variable within the package which houses the +class. It also takes an optional C<$initial_value>, which must be a +reference of the same type as the sigil of the C<$variable_name> +implies. + +=item B + +This will return a reference to the package variable in +C<$variable_name>. + +=item B + +Returns true (C<1>) if there is a package variable defined for +C<$variable_name>, and false (C<0>) otherwise. + +=item B + +This will attempt to remove the package variable at C<$variable_name>. + +=item B -=item B +This will attempt to remove the entire typeglob associated with +C<$glob_name> from the package. -=item B +=item B -=item B +This will list all the glob names associated with the current package. +By inspecting the globs returned you can discern all the variables in +the package. =back