summaryrefslogtreecommitdiff
path: root/t/cmop/lib/ClassEncapsulatedAttributes.pm
diff options
context:
space:
mode:
Diffstat (limited to 't/cmop/lib/ClassEncapsulatedAttributes.pm')
-rw-r--r--t/cmop/lib/ClassEncapsulatedAttributes.pm150
1 files changed, 150 insertions, 0 deletions
diff --git a/t/cmop/lib/ClassEncapsulatedAttributes.pm b/t/cmop/lib/ClassEncapsulatedAttributes.pm
new file mode 100644
index 0000000..5fb3a24
--- /dev/null
+++ b/t/cmop/lib/ClassEncapsulatedAttributes.pm
@@ -0,0 +1,150 @@
+package # hide the package from PAUSE
+ ClassEncapsulatedAttributes;
+
+use strict;
+use warnings;
+
+our $VERSION = '0.06';
+
+use parent 'Class::MOP::Class';
+
+sub initialize {
+ (shift)->SUPER::initialize(@_,
+ # use the custom attribute metaclass here
+ 'attribute_metaclass' => 'ClassEncapsulatedAttributes::Attribute',
+ );
+}
+
+sub construct_instance {
+ my ($class, %params) = @_;
+
+ my $meta_instance = $class->get_meta_instance;
+ my $instance = $meta_instance->create_instance();
+
+ # initialize *ALL* attributes, including masked ones (as opposed to applicable)
+ foreach my $current_class ($class->class_precedence_list()) {
+ my $meta = $current_class->meta;
+ foreach my $attr_name ($meta->get_attribute_list()) {
+ my $attr = $meta->get_attribute($attr_name);
+ $attr->initialize_instance_slot($meta_instance, $instance, \%params);
+ }
+ }
+
+ return $instance;
+}
+
+package # hide the package from PAUSE
+ ClassEncapsulatedAttributes::Attribute;
+
+use strict;
+use warnings;
+
+our $VERSION = '0.04';
+
+use parent 'Class::MOP::Attribute';
+
+# alter the way parameters are specified
+sub initialize_instance_slot {
+ my ($self, $meta_instance, $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 $class = $self->associated_class;
+ my $val;
+ $val = $params->{$class->name}->{$init_arg}
+ if exists $params->{$class->name} &&
+ exists ${$params->{$class->name}}{$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
+ $meta_instance->set_slot_value($instance, $self->name, $val);
+}
+
+sub name {
+ my $self = shift;
+ return ($self->associated_class->name . '::' . $self->SUPER::name)
+}
+
+1;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+ClassEncapsulatedAttributes - A set of example metaclasses with class encapsulated attributes
+
+=head1 SYNOPSIS
+
+ package Foo;
+
+ use metaclass 'ClassEncapsulatedAttributes';
+
+ Foo->meta->add_attribute('foo' => (
+ accessor => 'Foo_foo',
+ default => 'init in FOO'
+ ));
+
+ sub new {
+ my $class = shift;
+ $class->meta->new_object(@_);
+ }
+
+ package Bar;
+ our @ISA = ('Foo');
+
+ # duplicate the attribute name here
+ Bar->meta->add_attribute('foo' => (
+ accessor => 'Bar_foo',
+ default => 'init in BAR'
+ ));
+
+ # ... later in other code ...
+
+ my $bar = Bar->new();
+ prints $bar->Bar_foo(); # init in BAR
+ prints $bar->Foo_foo(); # init in FOO
+
+ # and ...
+
+ my $bar = Bar->new(
+ 'Foo' => { 'foo' => 'Foo::foo' },
+ 'Bar' => { 'foo' => 'Bar::foo' }
+ );
+
+ prints $bar->Bar_foo(); # Foo::foo
+ prints $bar->Foo_foo(); # Bar::foo
+
+=head1 DESCRIPTION
+
+This is an example metaclass which encapsulates a class's
+attributes on a per-class basis. This means that there is no
+possibility of name clashes with inherited attributes. This
+is similar to how C++ handles its data members.
+
+=head1 ACKNOWLEDGEMENTS
+
+Thanks to Yuval "nothingmuch" Kogman for the idea for this example.
+
+=head1 AUTHORS
+
+Stevan Little E<lt>stevan@iinteractive.comE<gt>
+
+Yuval Kogman E<lt>nothingmuch@woobling.comE<gt>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright 2006-2008 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