Release 0.09006
[p5sagit/Class-Accessor-Grouped.git] / lib / Class / Accessor / Grouped.pm
CommitLineData
963a69a5 1package Class::Accessor::Grouped;
2use strict;
3use warnings;
a0bce8bc 4use Carp ();
a0bce8bc 5use Scalar::Util ();
8787799c 6use MRO::Compat;
1ee74bdd 7use Sub::Name ();
331e820d 8
ccc05aec 9our $VERSION = '0.09006';
15cf8e32 10$VERSION = eval $VERSION;
963a69a5 11
8019c4d8 12# when changing minimum version don't forget to adjust L</PERFROMANCE> as well
13our $__minimum_xsa_version = '1.06';
9f562456 14
8019c4d8 15our $USE_XS;
16# the unless defined is here so that we can override the value
17# before require/use, *regardless* of the state of $ENV{CAG_USE_XS}
18$USE_XS = $ENV{CAG_USE_XS}
19 unless defined $USE_XS;
af169484 20
8019c4d8 21my $xsa_loaded;
a2537c55 22
8019c4d8 23my $load_xsa = sub {
24 return if $xsa_loaded++;
25 require Class::XSAccessor;
26 Class::XSAccessor->VERSION($__minimum_xsa_version);
27};
28
29my $use_xs = sub {
30 if (defined $USE_XS) {
31 $load_xsa->() if ($USE_XS && ! $xsa_loaded);
32 return $USE_XS;
33 }
34
35 $USE_XS = 0;
36
37 # Class::XSAccessor is segfaulting on win32, in some
38 # esoteric heavily-threaded scenarios
39 # Win32 users can set $USE_XS/CAG_USE_XS to try to use it anyway
9f562456 40 if ($^O ne 'MSWin32') {
8019c4d8 41 local $@;
42 eval { $load_xsa->(); $USE_XS = 1 };
9f562456 43 }
a2537c55 44
8019c4d8 45 return $USE_XS;
46};
a2537c55 47
963a69a5 48=head1 NAME
49
1ad8d8c6 50Class::Accessor::Grouped - Lets you build groups of accessors
963a69a5 51
52=head1 SYNOPSIS
53
54=head1 DESCRIPTION
55
56This class lets you build groups of accessors that will call different
57getters and setters.
58
59=head1 METHODS
60
61=head2 mk_group_accessors
62
63=over 4
64
65=item Arguments: $group, @fieldspec
66
67Returns: none
68
69=back
70
71Creates a set of accessors in a given group.
72
73$group is the name of the accessor group for the generated accessors; they
74will call get_$group($field) on get and set_$group($field, $value) on set.
75
22fa6720 76If you want to mimic Class::Accessor's mk_accessors $group has to be 'simple'
77to tell Class::Accessor::Grouped to use its own get_simple and set_simple
78methods.
79
963a69a5 80@fieldspec is a list of field/accessor names; if a fieldspec is a scalar
81this is used as both field and accessor name, if a listref it is expected to
82be of the form [ $accessor, $field ].
83
84=cut
85
86sub mk_group_accessors {
87 my ($self, $group, @fields) = @_;
88
89 $self->_mk_group_accessors('make_group_accessor', $group, @fields);
90 return;
91}
92
93
94{
95 no strict 'refs';
96 no warnings 'redefine';
97
98 sub _mk_group_accessors {
99 my($self, $maker, $group, @fields) = @_;
a0bce8bc 100 my $class = Scalar::Util::blessed $self || $self;
963a69a5 101
102 # So we don't have to do lots of lookups inside the loop.
103 $maker = $self->can($maker) unless ref $maker;
9f562456 104
8019c4d8 105 foreach (@fields) {
106 if( $_ eq 'DESTROY' ) {
a0bce8bc 107 Carp::carp("Having a data accessor named DESTROY in ".
963a69a5 108 "'$class' is unwise.");
109 }
110
8019c4d8 111 my ($name, $field) = (ref $_)
112 ? (@$_)
113 : ($_, $_)
114 ;
9f562456 115
963a69a5 116 my $alias = "_${name}_accessor";
9f562456 117
8019c4d8 118 for my $meth ($name, $alias) {
119
120 # the maker may elect to not return anything, meaning it already
121 # installed the coderef for us
122 my $cref = $self->$maker($group, $field, $meth)
123 or next;
124
125 my $fq_meth = join('::', $class, $meth);
9f562456 126
8019c4d8 127 *$fq_meth = Sub::Name::subname($fq_meth, $cref);
128 #unless defined &{$class."\:\:$field"}
9540f4e4 129 }
963a69a5 130 }
131 }
132}
133
134=head2 mk_group_ro_accessors
135
136=over 4
137
138=item Arguments: $group, @fieldspec
139
140Returns: none
141
142=back
143
144Creates a set of read only accessors in a given group. Identical to
a557f8ad 145L</mk_group_accessors> but accessors will throw an error if passed a value
963a69a5 146rather than setting the value.
147
148=cut
149
150sub mk_group_ro_accessors {
151 my($self, $group, @fields) = @_;
152
153 $self->_mk_group_accessors('make_group_ro_accessor', $group, @fields);
154}
155
156=head2 mk_group_wo_accessors
157
158=over 4
159
160=item Arguments: $group, @fieldspec
161
162Returns: none
163
164=back
165
166Creates a set of write only accessors in a given group. Identical to
a557f8ad 167L</mk_group_accessors> but accessors will throw an error if not passed a
963a69a5 168value rather than getting the value.
169
170=cut
171
172sub mk_group_wo_accessors {
173 my($self, $group, @fields) = @_;
174
175 $self->_mk_group_accessors('make_group_wo_accessor', $group, @fields);
176}
177
178=head2 make_group_accessor
179
180=over 4
181
8019c4d8 182=item Arguments: $group, $field, $method
963a69a5 183
8019c4d8 184Returns: \&accessor_coderef ?
963a69a5 185
186=back
187
8019c4d8 188Called by mk_group_accessors for each entry in @fieldspec. Either returns
189a coderef which will be installed at C<&__PACKAGE__::$method>, or returns
190C<undef> if it elects to install the coderef on its own.
963a69a5 191
192=cut
193
194sub make_group_accessor {
8019c4d8 195 my ($class, $group, $field, $name) = @_;
196
197 if ( $group eq 'simple' && $use_xs->() ) {
198 Class::XSAccessor->import({
199 replace => 1,
200 class => $class,
201 accessors => {
202 $name => $field,
203 },
204 });
205 return;
206 }
963a69a5 207
208 my $set = "set_$group";
209 my $get = "get_$group";
210
6a4c729f 211 $field =~ s/'/\\'/g;
212
a0bce8bc 213 # eval for faster fastiness
43db6fd3 214 my $code = eval "sub {
a0bce8bc 215 if(\@_ > 1) {
216 return shift->$set('$field', \@_);
963a69a5 217 }
218 else {
a0bce8bc 219 return shift->$get('$field');
963a69a5 220 }
43db6fd3 221 };";
222 Carp::croak $@ if $@;
223
224 return $code;
963a69a5 225}
226
227=head2 make_group_ro_accessor
228
229=over 4
230
8019c4d8 231=item Arguments: $group, $field, $method
963a69a5 232
8019c4d8 233Returns: \&accessor_coderef ?
963a69a5 234
235=back
236
8019c4d8 237Called by mk_group_ro_accessors for each entry in @fieldspec. Either returns
238a coderef which will be installed at C<&__PACKAGE__::$method>, or returns
239C<undef> if it elects to install the coderef on its own.
963a69a5 240
241=cut
242
243sub make_group_ro_accessor {
8019c4d8 244 my($class, $group, $field, $name) = @_;
245
246 if ( $group eq 'simple' && $use_xs->() ) {
247 Class::XSAccessor->import({
248 replace => 1,
249 class => $class,
250 getters => {
251 $name => $field,
252 },
253 });
254 return;
255 }
963a69a5 256
257 my $get = "get_$group";
258
6a4c729f 259 $field =~ s/'/\\'/g;
260
43db6fd3 261 my $code = eval "sub {
a0bce8bc 262 if(\@_ > 1) {
263 my \$caller = caller;
264 Carp::croak(\"'\$caller' cannot alter the value of '$field' on \".
265 \"objects of class '$class'\");
963a69a5 266 }
267 else {
a0bce8bc 268 return shift->$get('$field');
963a69a5 269 }
43db6fd3 270 };";
271 Carp::croak $@ if $@;
272
273 return $code;
963a69a5 274}
275
276=head2 make_group_wo_accessor
277
278=over 4
279
8019c4d8 280=item Arguments: $group, $field, $method
963a69a5 281
8019c4d8 282Returns: \&accessor_coderef ?
963a69a5 283
284=back
285
8019c4d8 286Called by mk_group_wo_accessors for each entry in @fieldspec. Either returns
287a coderef which will be installed at C<&__PACKAGE__::$method>, or returns
288C<undef> if it elects to install the coderef on its own.
963a69a5 289
290=cut
291
292sub make_group_wo_accessor {
8019c4d8 293 my($class, $group, $field, $name) = @_;
294
295 if ( $group eq 'simple' && $use_xs->() ) {
296 Class::XSAccessor->import({
297 replace => 1,
298 class => $class,
299 setters => {
300 $name => $field,
301 },
302 });
303 return;
304 }
963a69a5 305
306 my $set = "set_$group";
307
6a4c729f 308 $field =~ s/'/\\'/g;
309
43db6fd3 310 my $code = eval "sub {
a0bce8bc 311 unless (\@_ > 1) {
312 my \$caller = caller;
313 Carp::croak(\"'\$caller' cannot access the value of '$field' on \".
314 \"objects of class '$class'\");
963a69a5 315 }
316 else {
a0bce8bc 317 return shift->$set('$field', \@_);
963a69a5 318 }
43db6fd3 319 };";
320 Carp::croak $@ if $@;
321
322 return $code;
963a69a5 323}
324
325=head2 get_simple
326
327=over 4
328
329=item Arguments: $field
330
331Returns: $value
332
333=back
334
335Simple getter for hash-based objects which returns the value for the field
336name passed as an argument.
337
338=cut
339
340sub get_simple {
a0bce8bc 341 return $_[0]->{$_[1]};
963a69a5 342}
343
344=head2 set_simple
345
346=over 4
347
348=item Arguments: $field, $new_value
349
350Returns: $new_value
351
352=back
353
354Simple setter for hash-based objects which sets and then returns the value
355for the field name passed as an argument.
356
357=cut
358
359sub set_simple {
a0bce8bc 360 return $_[0]->{$_[1]} = $_[2];
963a69a5 361}
362
e6f2a0fd 363
364=head2 get_inherited
365
366=over 4
367
368=item Arguments: $field
369
370Returns: $value
371
372=back
373
331e820d 374Simple getter for Classes and hash-based objects which returns the value for
375the field name passed as an argument. This behaves much like
376L<Class::Data::Accessor> where the field can be set in a base class,
377inherited and changed in subclasses, and inherited and changed for object
378instances.
e6f2a0fd 379
380=cut
381
382sub get_inherited {
a49c32d9 383 my $class;
e6f2a0fd 384
62cf9924 385 if ( ($class = ref $_[0]) && Scalar::Util::blessed $_[0]) {
386 if (Scalar::Util::reftype $_[0] eq 'HASH') {
387 return $_[0]->{$_[1]} if exists $_[0]->{$_[1]};
388 }
389 else {
390 Carp::croak('Cannot get inherited value on an object instance that is not hash-based');
391 }
392 }
393 else {
a0bce8bc 394 $class = $_[0];
62cf9924 395 }
e6f2a0fd 396
397 no strict 'refs';
fe63d727 398 no warnings qw/uninitialized/;
62cf9924 399
400 my $cag_slot = '::__cag_'. $_[1];
401 return ${$class.$cag_slot} if defined(${$class.$cag_slot});
e6f2a0fd 402
4f8ce9da 403 # we need to be smarter about recalculation, as @ISA (thus supers) can very well change in-flight
62cf9924 404 my $cur_gen = mro::get_pkg_gen ($class);
405 if ( $cur_gen != ${$class.'::__cag_pkg_gen__'} ) {
406 @{$class.'::__cag_supers__'} = $_[0]->get_super_paths;
407 ${$class.'::__cag_pkg_gen__'} = $cur_gen;
408 }
a49c32d9 409
62cf9924 410 for (@{$class.'::__cag_supers__'}) {
411 return ${$_.$cag_slot} if defined(${$_.$cag_slot});
e6f2a0fd 412 };
c46050d3 413
9c3d5179 414 return undef;
e6f2a0fd 415}
416
417=head2 set_inherited
418
419=over 4
420
421=item Arguments: $field, $new_value
422
423Returns: $new_value
424
425=back
426
331e820d 427Simple setter for Classes and hash-based objects which sets and then returns
428the value for the field name passed as an argument. When called on a hash-based
429object it will set the appropriate hash key value. When called on a class, it
430will set a class level variable.
e6f2a0fd 431
331e820d 432B<Note:>: This method will die if you try to set an object variable on a non
433hash-based object.
e6f2a0fd 434
435=cut
436
437sub set_inherited {
a0bce8bc 438 if (Scalar::Util::blessed $_[0]) {
439 if (Scalar::Util::reftype $_[0] eq 'HASH') {
440 return $_[0]->{$_[1]} = $_[2];
e6f2a0fd 441 } else {
a0bce8bc 442 Carp::croak('Cannot set inherited value on an object instance that is not hash-based');
e6f2a0fd 443 };
444 } else {
445 no strict 'refs';
446
a0bce8bc 447 return ${$_[0].'::__cag_'.$_[1]} = $_[2];
e6f2a0fd 448 };
449}
450
331e820d 451=head2 get_component_class
452
453=over 4
454
455=item Arguments: $field
456
457Returns: $value
458
459=back
460
461Gets the value of the specified component class.
462
463 __PACKAGE__->mk_group_accessors('component_class' => 'result_class');
97d76fb4 464
331e820d 465 $self->result_class->method();
97d76fb4 466
331e820d 467 ## same as
468 $self->get_component_class('result_class')->method();
469
470=cut
471
472sub get_component_class {
a0bce8bc 473 return $_[0]->get_inherited($_[1]);
331e820d 474};
475
476=head2 set_component_class
477
478=over 4
479
480=item Arguments: $field, $class
481
482Returns: $new_value
483
484=back
485
486Inherited accessor that automatically loads the specified class before setting
487it. This method will die if the specified class could not be loaded.
488
489 __PACKAGE__->mk_group_accessors('component_class' => 'result_class');
490 __PACKAGE__->result_class('MyClass');
97d76fb4 491
331e820d 492 $self->result_class->method();
493
494=cut
495
496sub set_component_class {
a0bce8bc 497 if ($_[2]) {
bce7bdf8 498 local $^W = 0;
01249db5 499 require Class::Inspector;
a0bce8bc 500 if (Class::Inspector->installed($_[2]) && !Class::Inspector->loaded($_[2])) {
501 eval "use $_[2]";
331e820d 502
a0bce8bc 503 Carp::croak("Could not load $_[1] '$_[2]': ", $@) if $@;
331e820d 504 };
505 };
506
a0bce8bc 507 return $_[0]->set_inherited($_[1], $_[2]);
331e820d 508};
509
a49c32d9 510=head2 get_super_paths
511
512Returns a list of 'parent' or 'super' class names that the current class inherited from.
513
514=cut
515
516sub get_super_paths {
62cf9924 517 return @{mro::get_linear_isa( ref($_[0]) || $_[0] )};
a49c32d9 518};
519
a2537c55 5201;
b9a69571 521
9d7d52da 522=head1 PERFORMANCE
15cf8e32 523
8019c4d8 524To provide total flexibility L<Class::Accessor::Grouped> calls methods
525internally while performing get/set actions, which makes it noticeably
526slower than similar modules. To compensate, this module will automatically
527use the insanely fast L<Class::XSAccessor> to generate the C<simple>-group
528accessors, if L<< Class::XSAccessor >= 1.06|Class::XSAccessor >> is
529available on your system.
530
531=head2 Benchmark
532
533This is the result of a set/get/set loop benchmark on perl 5.12.1 with
534thread support, showcasing most popular accessor builders: L<Moose>, L<Mouse>,
535L<CAF|Class::Accessor::Fast>, L<CAF_XS|Class::Accessor::Fast::XS>
536and L<XSA|Class::XSAccessor>:
537
538 Rate CAG moOse CAF HANDMADE CAF_XS moUse_XS CAG_XS XSA
539 CAG 1777/s -- -27% -29% -36% -62% -67% -72% -73%
540 moOse 2421/s 36% -- -4% -13% -48% -55% -61% -63%
541 CAF 2511/s 41% 4% -- -10% -47% -53% -60% -61%
542 HANDMADE 2791/s 57% 15% 11% -- -41% -48% -56% -57%
543 CAF_XS 4699/s 164% 94% 87% 68% -- -13% -25% -28%
544 moUse_XS 5375/s 203% 122% 114% 93% 14% -- -14% -18%
545 CAG_XS 6279/s 253% 159% 150% 125% 34% 17% -- -4%
546 XSA 6515/s 267% 169% 159% 133% 39% 21% 4% --
547
548Benchmark program is available in the root of the
549L<repository|http://search.cpan.org/dist/Class-Accessor-Grouped/>:
550
551=head2 Notes on Class::XSAccessor
552
553While L<Class::XSAccessor> works surprisingly well for the amount of black
554magic it tries to pull off, it's still black magic. At present (Sep 2010)
555the module is known to have problems on Windows under heavy thread-stress
556(e.g. Win32+Apache+mod_perl). Thus for the time being L<Class::XSAccessor>
557will not be used automatically if you are running under C<MSWin32>.
558
559You can force the use of L<Class::XSAccessor> before creating a particular
560C<simple> accessor by either manipulating the global variable
561C<$Class::Accessor::Grouped::USE_XS>, or you can do so before runtime via the
562C<CAG_USE_XS> environment variable.
963a69a5 563
564=head1 AUTHORS
565
566Matt S. Trout <mst@shadowcatsystems.co.uk>
ba6f7b1b 567
97972dcb 568Christopher H. Laco <claco@chrislaco.com>
963a69a5 569
8ef9b3ff 570=head1 CONTRIBUTORS
dfb86526 571
ba6f7b1b 572Caelum: Rafael Kitover <rkitover@cpan.org>
573
8ef9b3ff 574groditi: Guillermo Roditi <groditi@cpan.org>
ba6f7b1b 575
6a4c729f 576Jason Plum <jason.plum@bmmsi.com>
dfb86526 577
ba6f7b1b 578ribasushi: Peter Rabbitson <ribasushi@cpan.org>
579
580
4fe25633 581=head1 COPYRIGHT & LICENSE
963a69a5 582
af169484 583Copyright (c) 2006-2010 Matt S. Trout <mst@shadowcatsystems.co.uk>
963a69a5 584
4fe25633 585This program is free software; you can redistribute it and/or modify
586it under the same terms as perl itself.
963a69a5 587
4fe25633 588=cut