package Class::Meta::Method; =head1 NAME Class::Meta::Method - Class::Meta class method introspection =head1 SYNOPSIS # Assuming MyApp::Thingy was generated by Class::Meta. my $class = MyApp::Thingy->my_class; my $thingy = MyApp::Thingy->new; print "\nMethods:\n"; for my $meth ($class->methods) { print " o ", $meth->name, $/; $meth->call($thingy); } =head1 DESCRIPTION This class provides an interface to the C objects that describe methods. It supports a simple description of the method, a label, and its visibility (private, protected, trusted, or public). Class::Meta::Method objects are created by Class::Meta; they are never instantiated directly in client code. To access the method objects for a Class::Meta-generated class, simply call its C method to retrieve its Class::Meta::Class object, and then call the C method on the Class::Meta::Class object. =cut ############################################################################## # Dependencies # ############################################################################## use strict; ############################################################################## # Package Globals # ############################################################################## our $VERSION = '0.65'; =head1 INTERFACE =head2 Constructors =head3 new A protected method for constructing a Class::Meta::Method object. Do not call this method directly; Call the L|Class::Meta/"add_method"> method on a Class::Meta object, instead. =cut sub new { my $pkg = shift; my $class = shift; # Check to make sure that only Class::Meta or a subclass is constructing a # Class::Meta::Method object. my $caller = caller; Class::Meta->handle_error("Package '$caller' cannot create $pkg " . "objects") unless UNIVERSAL::isa($caller, 'Class::Meta') || UNIVERSAL::isa($caller, __PACKAGE__); # Make sure we can get all the arguments. $class->handle_error("Odd number of parameters in call to new() " . "when named parameters were expected") if @_ % 2; my %p = @_; # Validate the name. $class->handle_error("Parameter 'name' is required in call to " . "new()") unless $p{name}; $class->handle_error("Method '$p{name}' is not a valid method " . "name -- only alphanumeric and '_' characters allowed") if $p{name} =~ /\W/; # Make sure the name hasn't already been used for another method # or constructor. $class->handle_error("Method '$p{name}' already exists in class " . "'$class->{package}'") if exists $class->{meths}{$p{name}} || exists $class->{ctors}{$p{name}}; # Check the visibility. if (exists $p{view}) { $p{view} = Class::Meta::_str_to_const($p{view}); $class->handle_error("Not a valid view parameter: '$p{view}'") unless $p{view} == Class::Meta::PUBLIC || $p{view} == Class::Meta::PROTECTED || $p{view} == Class::Meta::TRUSTED || $p{view} == Class::Meta::PRIVATE; } else { # Make it public by default. $p{view} = Class::Meta::PUBLIC; } # Check the context. if (exists $p{context}) { $p{context} = Class::Meta::_str_to_const($p{context}); $class->handle_error("Not a valid context parameter: " . "'$p{context}'") unless $p{context} == Class::Meta::OBJECT || $p{context} == Class::Meta::CLASS; } else { # Make it public by default. $p{context} = Class::Meta::OBJECT; } # Validate or create the method caller if necessary. if ($p{caller}) { my $ref = ref $p{caller}; $class->handle_error( 'Parameter caller must be a code reference' ) unless $ref && $ref eq 'CODE' } else { $p{caller} = eval "sub { shift->$p{name}(\@_) }" if $p{view} > Class::Meta::PRIVATE; } if ($p{code}) { my $ref = ref $p{code}; $class->handle_error( 'Parameter code must be a code reference' ) unless $ref && $ref eq 'CODE'; } # Create and cache the method object. $p{package} = $class->{package}; $class->{meths}{$p{name}} = bless \%p, ref $pkg || $pkg; # Index its view. push @{ $class->{all_meth_ord} }, $p{name}; if ($p{view} > Class::Meta::PRIVATE) { push @{$class->{prot_meth_ord}}, $p{name} unless $p{view} == Class::Meta::TRUSTED; if ($p{view} > Class::Meta::PROTECTED) { push @{$class->{trst_meth_ord}}, $p{name}; push @{$class->{meth_ord}}, $p{name} if $p{view} == Class::Meta::PUBLIC; } } # Store a reference to the class object. $p{class} = $class; # Let 'em have it. return $class->{meths}{$p{name}}; } ############################################################################## # Instance Methods # ############################################################################## =head2 Instance Methods =head3 name my $name = $meth->name; Returns the method name. =head3 package my $package = $meth->package; Returns the method package. =head3 desc my $desc = $meth->desc; Returns the description of the method. =head3 label my $desc = $meth->label; Returns label for the method. =head3 view my $view = $meth->view; Returns the view of the method, reflecting its visibility. The possible values are defined by the following constants: =over 4 =item Class::Meta::PUBLIC =item Class::Meta::PRIVATE =item Class::Meta::TRUSTED =item Class::Meta::PROTECTED =back =head3 context my $context = $meth->context; Returns the context of the method, essentially whether it is a class or object method. The possible values are defined by the following constants: =over 4 =item Class::Meta::CLASS =item Class::Meta::OBJECT =back =head3 args A description of the arguments to the method. This can be anything you like, but I recommend something like a string for a single argument, an array reference for a list of arguments, or a hash reference for parameter arguments. =head3 returns A description of the return value or values of the method. =head3 class my $class = $meth->class; Returns the Class::Meta::Class object that this method is associated with. Note that this object will always represent the class in which the method is defined, and I any of its subclasses. =cut sub name { $_[0]->{name} } sub package { $_[0]->{package} } sub desc { $_[0]->{desc} } sub label { $_[0]->{label} } sub view { $_[0]->{view} } sub context { $_[0]->{context} } sub args { $_[0]->{args} } sub returns { $_[0]->{returns} } sub class { $_[0]->{class} } =head3 call my $ret = $meth->call($obj, @args); Calls the method on the C<$obj> object, passing in any arguments. Note that it uses a C to execute the method, so the call to C itself will not appear in a call stack trace. =cut sub call { my $self = shift; my $code = $self->{caller} or $self->class->handle_error("Cannot call method '", $self->name, "'"); goto &$code; } ############################################################################## =head3 build $meth->build($class); This is a protected method, designed to be called only by the Class::Meta class or a subclass of Class::Meta. It takes a single argument, the Class::Meta::Class object for the class in which the method was defined. Once it checks to make sure that it is only called by Class::Meta or a subclass of Class::Meta or of Class::Meta::Method, C installs the method if it was specified via the C parameter to C. Although you should never call this method directly, subclasses of Class::Meta::Method may need to override it in order to add behavior. =cut sub build { my ($self, $class) = @_; # Check to make sure that only Class::Meta or a subclass is building # methods. my $caller = caller; $self->class->handle_error( "Package '$caller' cannot call " . ref($self) . "->build" ) unless UNIVERSAL::isa($caller, 'Class::Meta') || UNIVERSAL::isa($caller, __PACKAGE__); # Install the method if we've got it. if (my $code = delete $self->{code}) { my $pack = $self->package; my $name = $self->{name}; if ($self->{view} < Class::Meta::PUBLIC ) { # Add a constraint to the code ref. my $real_meth = $code; if ($self->{view} == Class::Meta::PROTECTED) { $code = sub { $self->class->handle_error( "$name is a protected method of $pack" ) unless UNIVERSAL::isa(scalar caller, $pack); goto &$real_meth; }; } elsif ($self->{view} == Class::Meta::PRIVATE) { $code = sub { $self->class->handle_error( "$name is a private method of $pack" ) unless caller eq $pack; goto &$real_meth; }; } elsif ($self->{view} == Class::Meta::TRUSTED) { my $trusted = $self->class->trusted; $code = sub { my $caller = caller; goto &$real_meth if $caller eq $pack; for my $pkg ( @{ $trusted } ) { goto &$real_meth if UNIVERSAL::isa($caller, $pkg); } $self->class->handle_error( "$name is a trusted method of $pack" ); }; } } no strict 'refs'; *{"$pack\::$name"} = $code; } return $self; } 1; __END__ =head1 SUPPORT This module is stored in an open L. Feel free to fork and contribute! Please file bug reports via L or by sending mail to L. =head1 AUTHOR David E. Wheeler =head1 SEE ALSO Other classes of interest within the Class::Meta distribution include: =over 4 =item L =item L =item L =item L =back =head1 COPYRIGHT AND LICENSE Copyright (c) 2002-2011, David E. Wheeler. Some Rights Reserved. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut