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