0b860312b9d8e546ae43ef44e4892e2a71f03020
[gitmo/Class-MOP.git] / examples / ClassEncapsulatedAttributes.pod
1
2 package # hide the package from PAUSE
3     ClassEncapsulatedAttributes;
4
5 use strict;
6 use warnings;
7
8 our $VERSION = '0.04';
9
10 use base 'Class::MOP::Class';
11
12 sub initialize { 
13     (shift)->SUPER::initialize(@_, 
14         # use the custom attribute metaclass here 
15         ':attribute_metaclass' => 'ClassEncapsulatedAttributes::Attribute' 
16     );
17 }
18
19 sub construct_instance {
20     my ($class, %params) = @_;
21     my $instance = {};
22     foreach my $current_class ($class->class_precedence_list()) {
23         $instance->{$current_class} = {} 
24             unless exists $instance->{$current_class};
25         my $meta = $current_class->meta;
26         foreach my $attr_name ($meta->get_attribute_list()) {
27             my $attr = $meta->get_attribute($attr_name);
28             # if the attr has an init_arg, use that, otherwise,
29             # use the attributes name itself as the init_arg
30             my $init_arg = $attr->init_arg();
31             # try to fetch the init arg from the %params ...
32             my $val;        
33             $val = $params{$current_class}->{$init_arg} 
34                 if exists $params{$current_class} && 
35                    exists ${$params{$current_class}}{$init_arg};
36             # if nothing was in the %params, we can use the 
37             # attribute's default value (if it has one)
38             if (!defined $val && $attr->has_default) {
39                 $val = $attr->default($instance); 
40             }
41             # now add this to the instance structure
42             $instance->{$current_class}->{$attr_name} = $val;
43         }
44     }  
45     return $instance;
46 }
47
48 package # hide the package from PAUSE
49     ClassEncapsulatedAttributes::Attribute;
50
51 use strict;
52 use warnings;
53
54 our $VERSION = '0.02';
55
56 use base 'Class::MOP::Attribute';
57
58 sub generate_accessor_method {
59     my ($self, $attr_name) = @_;
60     my $class_name = $self->associated_class->name;
61     eval qq{sub {
62         \$_[0]->{'$class_name'}->{'$attr_name'} = \$_[1] if scalar(\@_) == 2;
63         \$_[0]->{'$class_name'}->{'$attr_name'};
64     }};
65 }
66
67 sub generate_reader_method {
68     my ($self, $attr_name) = @_; 
69     my $class_name = $self->associated_class->name;
70     eval qq{sub {
71         Carp::confess "Cannot assign a value to a read-only accessor" if \@_ > 1;
72         \$_[0]->{'$class_name'}->{'$attr_name'};
73     }};   
74 }
75
76 sub generate_writer_method {
77     my ($self, $attr_name) = @_; 
78     my $class_name = $self->associated_class->name;    
79     eval qq{sub {
80         \$_[0]->{'$class_name'}->{'$attr_name'} = \$_[1];
81     }};
82 }
83
84 sub generate_predicate_method {
85     my ($self, $attr_name) = @_; 
86     my $class_name = $self->associated_class->name;    
87     eval qq{sub {
88         defined \$_[0]->{'$class_name'}->{'$attr_name'} ? 1 : 0;
89     }};
90 }
91
92 ## &remove_attribute is left as an exercise for the reader :)
93
94 1;
95
96 __END__
97
98 =pod
99
100 =head1 NAME
101
102 ClassEncapsulatedAttributes - A set of example metaclasses with class encapsulated attributes
103
104 =head1 SYNOPSIS
105
106   package Foo;
107   
108   use metaclass 'ClassEncapsulatedAttributes';
109   
110   Foo->meta->add_attribute('foo' => (
111       accessor  => 'Foo_foo',
112       default   => 'init in FOO'
113   ));
114   
115   sub new  {
116       my $class = shift;
117       $class->meta->new_object(@_);
118   }
119   
120   package Bar;
121   our @ISA = ('Foo');
122   
123   # duplicate the attribute name here
124   Bar->meta->add_attribute('foo' => (
125       accessor  => 'Bar_foo',
126       default   => 'init in BAR'            
127   ));      
128   
129   # ... later in other code ...
130   
131   my $bar = Bar->new();
132   prints $bar->Bar_foo(); # init in BAR
133   prints $bar->Foo_foo(); # init in FOO  
134   
135   # and ...
136   
137   my $bar = Bar->new(
138       'Foo' => { 'foo' => 'Foo::foo' },
139       'Bar' => { 'foo' => 'Bar::foo' }        
140   );  
141   
142   prints $bar->Bar_foo(); # Foo::foo
143   prints $bar->Foo_foo(); # Bar::foo  
144   
145 =head1 DESCRIPTION
146
147 This is an example metaclass which encapsulates a class's 
148 attributes on a per-class basis. This means that there is no
149 possibility of name clashes with inherited attributes. This 
150 is similar to how C++ handles its data members. 
151
152 =head1 ACKNOWLEDGEMENTS
153
154 Thanks to Yuval "nothingmuch" Kogman for the idea for this example.
155
156 =head1 AUTHOR
157
158 Stevan Little E<lt>stevan@iinteractive.comE<gt>
159
160 =head1 COPYRIGHT AND LICENSE
161
162 Copyright 2006 by Infinity Interactive, Inc.
163
164 L<http://www.iinteractive.com>
165
166 This library is free software; you can redistribute it and/or modify
167 it under the same terms as Perl itself. 
168
169 =cut