2 package InsideOutClass;
11 use Scalar::Util 'refaddr';
13 use base 'Class::MOP::Class';
15 sub construct_instance {
16 my ($class, %params) = @_;
17 # create a scalar ref to use as
18 # the inside-out instance
19 my $instance = \(my $var);
20 foreach my $attr (map { $_->{attribute} } $class->compute_all_applicable_attributes()) {
21 # if the attr has an init_arg, use that, otherwise,
22 # use the attributes name itself as the init_arg
23 my $init_arg = $attr->has_init_arg() ? $attr->init_arg() : $attr->name;
24 # try to fetch the init arg from the %params ...
26 $val = $params{$init_arg} if exists $params{$init_arg};
27 # if nothing was in the %params, we can use the
28 # attribute's default value (if it has one)
29 $val ||= $attr->default($instance) if $attr->has_default();
30 # now add this to the instance structure
31 $class->get_package_variable('%' . $attr->name)->{ refaddr($instance) } = $val;
36 package InsideOutClass::Attribute;
41 use Class::MOP 'meta';
43 our $VERSION = '0.02';
46 use Scalar::Util 'blessed', 'reftype', 'refaddr';
48 use base 'Class::MOP::Attribute';
51 # this is just a utility routine to
52 # handle the details of accessors
53 my $_inspect_accessor = sub {
54 my ($attr_name, $type, $accessor) = @_;
55 my %ACCESSOR_TEMPLATES = (
57 $' . $attr_name . '{ refaddr($_[0]) } = $_[1] if scalar(@_) == 2;
58 $' . $attr_name . '{ refaddr($_[0]) };
61 $' . $attr_name . '{ refaddr($_[0]) };
64 $' . $attr_name . '{ refaddr($_[0]) } = $_[1];
67 defined($' . $attr_name . '{ refaddr($_[0]) }) ? 1 : 0;
71 my $method = eval $ACCESSOR_TEMPLATES{$type};
72 confess "Could not create the $type for $attr_name CODE(\n" . $ACCESSOR_TEMPLATES{$type} . "\n) : $@" if $@;
73 return ($accessor => Class::MOP::Attribute::Accessor->wrap($method));
76 sub install_accessors {
77 my ($self, $class) = @_;
78 (blessed($class) && $class->isa('Class::MOP::Class'))
79 || confess "You must pass a Class::MOP::Class instance (or a subclass)";
81 # create the package variable to
82 # store the inside out attribute
83 $class->add_package_variable('%' . $self->name);
85 # now create the accessor/reader/writer/predicate methods
88 $_inspect_accessor->($class->name . '::' . $self->name, 'accessor' => $self->accessor())
89 ) if $self->has_accessor();
92 $_inspect_accessor->($class->name . '::' . $self->name, 'reader' => $self->reader())
93 ) if $self->has_reader();
96 $_inspect_accessor->($class->name . '::' . $self->name, 'writer' => $self->writer())
97 ) if $self->has_writer();
100 $_inspect_accessor->($class->name . '::' . $self->name, 'predicate' => $self->predicate())
101 ) if $self->has_predicate();
107 ## &remove_attribute is left as an exercise for the reader :)
117 InsideOutClass - A set of metaclasses which use the Inside-Out technique
123 sub meta { InsideOutClass->initialize($_[0]) }
125 __PACKAGE__->meta->add_attribute(
126 InsideOutClass::Attribute->new('foo' => (
134 bless $class->meta->construct_instance() => $class;
137 # now you can just use the class as normal
141 This is a set of example metaclasses which implement the Inside-Out
142 class technique. What follows is a brief explaination of the code
143 found in this module.
145 First step is to subclass B<Class::MOP::Class> and override the
146 C<construct_instance> method. The default C<construct_instance>
147 will create a HASH reference using the parameters and attribute
148 default values. Since inside-out objects don't use HASH refs, and
149 use package variables instead, we need to write code to handle
152 The next step is to create the subclass of B<Class::MOP::Attribute>
153 and override the C<install_accessors> method (you would also need to
154 override the C<remove_accessors> too, but we can safely ignore that
155 in our example). The C<install_accessor> method is called by the
156 C<add_attribute> method of B<Class::MOP::Class>, and will install
157 the accessors for your attribute. Since inside-out objects require
158 different types of accessors, we need to write the code to handle
159 this difference as well.
161 And that is pretty much all. Of course I am ignoring need for
162 inside-out objects to be C<DESTROY>-ed, and some other details as
163 well, but this is an example. A real implementation is left as an
164 exercise to the reader.
168 Stevan Little E<lt>stevan@iinteractive.comE<gt>
170 =head1 COPYRIGHT AND LICENSE
172 Copyright 2006 by Infinity Interactive, Inc.
174 L<http://www.iinteractive.com>
176 This library is free software; you can redistribute it and/or modify
177 it under the same terms as Perl itself.