

package # hide the package from PAUSE
    InsideOutClass::Attribute;

use strict;
use warnings;

our $VERSION = '0.05';

use Carp         'confess';
use Scalar::Util 'refaddr';

use base 'Class::MOP::Attribute';

sub initialize_instance_slot {
    my ($self, $class, $instance, $params) = @_;
    # if the attr has an init_arg, use that, otherwise,
    # use the attributes name itself as the init_arg
    my $init_arg = $self->init_arg();
    # try to fetch the init arg from the %params ...
    my $val;        
    $val = $params->{$init_arg} if exists $params->{$init_arg};
    # if nothing was in the %params, we can use the 
    # attribute's default value (if it has one)
    if (!defined $val && $self->has_default) {
        $val = $self->default($instance); 
    }
    # now add this to the instance structure
    $class->get_package_variable('%' . $self->name)->{ refaddr($instance) } = $val;    
}

sub generate_accessor_method {
    my ($self, $attr_name) = @_;
    $attr_name = ($self->associated_class->name . '::' . $attr_name);
    eval 'sub {
        $' . $attr_name . '{ refaddr($_[0]) } = $_[1] if scalar(@_) == 2;
        $' . $attr_name . '{ refaddr($_[0]) };
    }';
}

sub generate_reader_method {
    my ($self, $attr_name) = @_;     
    eval 'sub {
        confess "Cannot assign a value to a read-only accessor" if @_ > 1;
        $' . ($self->associated_class->name . '::' . $attr_name) . '{ refaddr($_[0]) };
    }';   
}

sub generate_writer_method {
    my ($self, $attr_name) = @_; 
    eval 'sub {
        $' . ($self->associated_class->name . '::' . $attr_name) . '{ refaddr($_[0]) } = $_[1];
    }';
}

sub generate_predicate_method {
    my ($self, $attr_name) = @_; 
    eval 'sub {
        defined($' . ($self->associated_class->name . '::' . $attr_name) . '{ refaddr($_[0]) }) ? 1 : 0;
    }';
}

## &remove_attribute is left as an exercise for the reader :)

1;

__END__

=pod

=head1 NAME

InsideOutClass - A set of example metaclasses which implement the Inside-Out technique

=head1 SYNOPSIS

  package Foo;
  
  use metaclass 'Class::MOP::Class' => (
     # tell our metaclass to use the 
     # InsideOut attribute metclass 
     # to construct all it's attributes
    ':attribute_metaclass' => 'InsideOutClass::Attribute'
  );
  
  __PACKAGE__->meta->add_attribute('foo' => (
      reader => 'get_foo',
      writer => 'set_foo'
  ));    
  
  sub new  {
      my $class = shift;
      $class->meta->new_object(@_);
  } 

  # now you can just use the class as normal

=head1 DESCRIPTION

This is a set of example metaclasses which implement the Inside-Out 
class technique. What follows is a brief explaination of the code 
found in this module.

We must create a subclass of B<Class::MOP::Attribute> and override 
the instance initialization and method generation code. This requires 
overloading C<initialize_instance_slot>, C<generate_accessor_method>, 
C<generate_reader_method>, C<generate_writer_method> and 
C<generate_predicate_method>. All other aspects are taken care of with 
the existing B<Class::MOP::Attribute> infastructure.

And that is pretty much all. Of course I am ignoring need for 
inside-out objects to be C<DESTROY>-ed, and some other details as 
well, but this is an example. A real implementation is left as an 
exercise to the reader.

=head1 AUTHOR

Stevan Little E<lt>stevan@iinteractive.comE<gt>

=head1 COPYRIGHT AND LICENSE

Copyright 2006 by Infinity Interactive, Inc.

L<http://www.iinteractive.com>

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself. 

=cut
