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