Factor out a concatenation in grabbing the instance.
[gitmo/MooseX-ClassAttribute.git] / lib / MooseX / ClassAttribute.pm
CommitLineData
4dee0fd3 1package MooseX::ClassAttribute;
2
4dee0fd3 3use strict;
54a288bd 4use warnings;
4dee0fd3 5
fab23ffc 6our $VERSION = '0.04';
0f24a39d 7our $AUTHORITY = 'cpan:DROLSKY';
8
54a288bd 9our @EXPORT = 'class_has'; ## no critic ProhibitAutomaticExportation
d48c186f 10use base 'Exporter';
0f24a39d 11
54a288bd 12use B qw( svref_2object );
d8ac31ad 13use Moose::Meta::Class;
54a288bd 14use Sub::Name;
a124b299 15
0f24a39d 16
54a288bd 17sub class_has ## no critic RequireArgUnpacking
0f24a39d 18{
54a288bd 19 my $caller = caller();
20
8d655404 21 process_class_attribute( $caller, @_ );
22
23 return;
24}
25
26sub process_class_attribute ## no critic RequireArgUnpacking
27{
28 my $caller = shift;
29
54a288bd 30 my $caller_meta = $caller->meta();
31
32 my @parents = $caller_meta->superclasses();
33
34 my $container_pkg = _make_container_class( $caller, @parents );
9cabc97e 35 my $container_meta = $container_pkg->meta();
a124b299 36
b8f68e61 37 $container_meta->add_attribute(@_);
a124b299 38
54a288bd 39 for my $meth ( grep { $_ ne 'instance' } $container_meta->get_method_list() )
40 {
41 next if $caller_meta->has_method($meth);
a124b299 42
54a288bd 43 my $sub = sub { shift;
44 my $instance = $container_pkg->instance();
45 return $instance->$meth(@_); };
46
47 $caller_meta->add_method( $meth => $sub );
48 }
a124b299 49
0f24a39d 50 return;
51}
52
54a288bd 53{
54 # This should probably be an attribute of the metaclass, but that
55 # would require extending Moose::Meta::Class, which would conflict
56 # with anything else that wanted to do so as well (we need
57 # metaclass roles or something).
58 my %Name;
59
60 sub _make_container_class ## no critic RequireArgUnpacking
61 {
62 my $caller = shift;
63
64 return $Name{$caller} if $Name{$caller};
65
66 my @parents = map { container_class($_) || () } @_;
67
b4d972cd 68 push @parents, 'Moose::Object'
69 unless grep { $_->isa('Moose::Object') } @parents;
54a288bd 70
b4d972cd 71 my $container_pkg = 'MooseX::ClassAttribute::Container::' . $caller;
e1bb5601 72 my $instance_holder = $container_pkg . '::Self';
0f24a39d 73
b4d972cd 74 my $instance_meth = sub {
75 no strict 'refs'; ## no critic ProhibitNoStrict
e1bb5601 76 return $$instance_holder ||= shift->new(@_);
b4d972cd 77 };
78
79 my $class =
80 Moose::Meta::Class->create
81 ( $container_pkg =>
82 superclasses => \@parents,
83 methods => { instance => $instance_meth },
84 );
0f24a39d 85
54a288bd 86 return $Name{$caller} = $container_pkg;
87 }
88
89 sub container_class
90 {
91 my $pkg = shift || caller();
92
93 return $Name{$pkg};
94 }
95}
96
97# This is basically copied from Moose.pm
8d655404 98sub unimport ## no critic RequireFinalReturn, RequireArgUnpacking
54a288bd 99{
4c911e18 100 my $caller = Moose::_get_caller(@_);
54a288bd 101
102 no strict 'refs'; ## no critic ProhibitNoStrict
103 foreach my $name (@EXPORT)
104 {
105 if ( defined &{ $caller . '::' . $name } )
106 {
107 my $keyword = \&{ $caller . '::' . $name };
108
109 my $pkg_name =
110 eval { svref_2object($keyword)->GV()->STASH()->NAME() };
111
112 next if $@;
113 next if $pkg_name ne __PACKAGE__;
114
115 delete ${ $caller . '::' }{$name};
116 }
117 }
118}
4dee0fd3 119
120
1211;
122
123__END__
124
125=pod
126
127=head1 NAME
128
54a288bd 129MooseX::ClassAttribute - Declare class attributes Moose-style
4dee0fd3 130
4dee0fd3 131=head1 SYNOPSIS
132
54a288bd 133 package My::Class;
4dee0fd3 134
54a288bd 135 use Moose;
4dee0fd3 136 use MooseX::ClassAttribute;
137
54a288bd 138 class_has 'Cache' =>
139 ( is => 'rw',
140 isa => 'HashRef',
141 default => sub { {} },
142 );
143
144 __PACKAGE__->meta()->make_immutable();
ac5d97b7 145 MooseX::ClassAttribute::container_class()->meta()->make_immutable();
54a288bd 146
147 no Moose;
148 no MooseX::ClassAttribute;
149
150 # then later ...
151
152 My::Class->Cache()->{thing} = ...;
153
154
155=head1 DESCRIPTION
156
157This module allows you to declare class attributes in exactly the same
158way as you declare object attributes, except using C<class_has()>
159instead of C<has()>. It is also possible to make these attributes
160immutable (and faster) just as you can with normal Moose attributes.
161
162You can use any feature of Moose's attribute declarations, including
163overriding a parent's attributes, delegation (C<handles>), and
164attribute metaclasses, and it should just work.
165
7dc1418a 166The accessors methods for class attribute may be called on the class
167directly, or on objects of that class. Passing a class attribute to
168the constructor will not set it.
169
54a288bd 170=head1 FUNCTIONS
171
172This class exports one function when you use it, C<class_has()>. This
173works exactly like Moose's C<has()>, but it declares class attributes.
174
170db2d9 175One little nit is that if you include C<no Moose> in your class, you
54a288bd 176won't remove the C<class_has()> function. To do that you must include
177C<no MooseX::ClassAttribute> as well.
178
8d655404 179If you want to use this module to create class attributes in I<other>
180classes, you can call the C<process_class_attribute()> function like
181this:
182
183 MooseX::ClassAttribute::process_class_attribute( $package, ... );
184
185The first argument is the package which will have the class attribute,
186and the remaining arguments are the same as those passed to
187C<class_has()>.
188
54a288bd 189=head2 Implementation and Immutability
190
191Underneath the hood, this class creates one new class for each class
192which has class attributes and sets up delegating methods in the class
193for which you're creating class attributes. You don't need to worry
194about this too much, except when it comes to making a class immutable.
4dee0fd3 195
54a288bd 196Since the class attributes are not really stored in your class, you
ac5d97b7 197need to make the container class immutable as well as your own ...
4dee0fd3 198
54a288bd 199 __PACKAGE__->meta()->make_immutable();
ac5d97b7 200 MooseX::ClassAttribute::container_class()->meta()->make_immutable();
4dee0fd3 201
54a288bd 202I<This may change in the future!>
4dee0fd3 203
204=head1 AUTHOR
205
206Dave Rolsky, C<< <autarch@urth.org> >>
207
208=head1 BUGS
209
54a288bd 210Please report any bugs or feature requests to
211C<bug-moosex-classattribute@rt.cpan.org>, or through the web interface
212at L<http://rt.cpan.org>. I will be notified, and then you'll
213automatically be notified of progress on your bug as I make changes.
4dee0fd3 214
215=head1 COPYRIGHT & LICENSE
216
217Copyright 2007 Dave Rolsky, All Rights Reserved.
218
219This program is free software; you can redistribute it and/or modify
220it under the same terms as Perl itself.
221
222=cut