Improve the documentation a *tiny* bit (needs much more work)
[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 ();
6d0e8ff2 6use Module::Runtime ();
df845078 7
8BEGIN {
6d0e8ff2 9 # use M::R to work around the 5.8 require bugs
df845078 10 if ($] < 5.009_005) {
6d0e8ff2 11 Module::Runtime::require_module('MRO::Compat');
df845078 12 }
13 else {
14 require mro;
15 }
16}
331e820d 17
841a75f5 18our $VERSION = '0.10007';
d93670a5 19$VERSION = eval $VERSION if $VERSION =~ /_/; # numify for warning-free dev releases
963a69a5 20
80ebe3b6 21# when changing minimum version don't forget to adjust Makefile.PL as well
85ccab9a 22our $__minimum_xsa_version;
79f0ccb0 23BEGIN { $__minimum_xsa_version = '1.15' }
9f562456 24
8019c4d8 25our $USE_XS;
26# the unless defined is here so that we can override the value
27# before require/use, *regardless* of the state of $ENV{CAG_USE_XS}
28$USE_XS = $ENV{CAG_USE_XS}
ba8c183b 29 unless defined $USE_XS;
af169484 30
6d0e8ff2 31BEGIN {
727cd2c0 32 package # hide from PAUSE
33 __CAG_ENV__;
6d0e8ff2 34
35 die "Huh?! No minimum C::XSA version?!\n"
36 unless $__minimum_xsa_version;
37
38 local $@;
39 require constant;
40
41 # individual (one const at a time) imports so we are 5.6.2 compatible
42 # if we can - why not ;)
43 constant->import( NO_SUBNAME => eval {
44 Module::Runtime::require_module('Sub::Name')
45 } ? 0 : "$@" );
46
79f0ccb0 47 my $found_cxsa;
7a1ba8bd 48 constant->import( NO_CXSA => ( NO_SUBNAME() || ( eval {
79f0ccb0 49 Module::Runtime::require_module('Class::XSAccessor');
50 $found_cxsa = Class::XSAccessor->VERSION;
51 Class::XSAccessor->VERSION($__minimum_xsa_version);
7a1ba8bd 52 } ? 0 : "$@" ) ) );
6d0e8ff2 53
79f0ccb0 54 if (NO_CXSA() and $found_cxsa and !$ENV{CAG_OLD_XS_NOWARN}) {
55 warn(
56 'The installed version of Class::XSAccessor is too old '
57 . "(v$found_cxsa < v$__minimum_xsa_version). Please upgrade "
58 . "to instantly quadruple the performance of 'simple' accessors. "
59 . 'Set $ENV{CAG_OLD_XS_NOWARN} if you wish to disable this '
60 . "warning.\n"
61 );
62 }
63
6d0e8ff2 64 constant->import( BROKEN_GOTO => ($] < '5.008009') ? 1 : 0 );
65
66 constant->import( UNSTABLE_DOLLARAT => ($] < '5.013002') ? 1 : 0 );
67
68 constant->import( TRACK_UNDEFER_FAIL => (
69 $INC{'Test/Builder.pm'} || $INC{'Test/Builder2.pm'}
70 and
71 $0 =~ m|^ x?t / .+ \.t $|x
72 ) ? 1 : 0 );
73}
74
8079caeb 75# Yes this method is undocumented
85ccab9a 76# Yes it should be a private coderef like all the rest at the end of this file
8079caeb 77# No we can't do that (yet) because the DBIC-CDBI compat layer overrides it
78# %$*@!?&!&#*$!!!
79sub _mk_group_accessors {
ba8c183b 80 my($self, $maker, $group, @fields) = @_;
48dfec72 81 my $class = length (ref ($self) ) ? ref ($self) : $self;
eece2562 82
ba8c183b 83 no strict 'refs';
84 no warnings 'redefine';
eece2562 85
ba8c183b 86 # So we don't have to do lots of lookups inside the loop.
87 $maker = $self->can($maker) unless ref $maker;
eece2562 88
ba8c183b 89 for (@fields) {
eece2562 90
ba8c183b 91 my ($name, $field) = (ref $_) ? (@$_) : ($_, $_);
eece2562 92
510d7274 93 Carp::croak("Illegal accessor name '$name'")
94 unless $name =~ /\A[A-Z_a-z][0-9A-Z_a-z]*\z/;
95
96 Carp::carp("Having a data accessor named '$name' in '$class' is unwise.")
97 if $name =~ /\A(?: DESTROY | AUTOLOAD | CLONE )\z/x;
33fe2299 98
ba8c183b 99 my $alias = "_${name}_accessor";
eece2562 100
48dfec72 101 for ($name, $alias) {
eece2562 102
ba8c183b 103 # the maker may elect to not return anything, meaning it already
104 # installed the coderef for us (e.g. lack of Sub::Name)
48dfec72 105 my $cref = $self->$maker($group, $field, $_)
ba8c183b 106 or next;
eece2562 107
48dfec72 108 my $fq_meth = "${class}::$_";
eece2562 109
ba8c183b 110 *$fq_meth = Sub::Name::subname($fq_meth, $cref);
111 #unless defined &{$class."\:\:$field"}
eece2562 112 }
ba8c183b 113 }
eece2562 114};
115
e6bdecbd 116# $gen_accessor coderef is setup at the end for clarity
85ccab9a 117my $gen_accessor;
eece2562 118
963a69a5 119=head1 NAME
120
1ad8d8c6 121Class::Accessor::Grouped - Lets you build groups of accessors
963a69a5 122
123=head1 SYNOPSIS
124
3b118c10 125 use base 'Class::Accessor::Grouped';
126
127 # make basic accessors for objects
128 __PACKAGE__->mk_group_accessors(simple => qw(id name email));
129
130 # make accessor that works for objects and classes
131 __PACKAGE__->mk_group_accessors(inherited => 'awesome_level');
132
533d4d96 133 # make an accessor which calls a custom pair of getters/setters
134 sub get_column { ... this will be called when you do $obj->name() ... }
135 sub set_column { ... this will be called when you do $obj->name('foo') ... }
136 __PACKAGE__->mk_group_accessors(column => 'name');
137
963a69a5 138=head1 DESCRIPTION
139
140This class lets you build groups of accessors that will call different
533d4d96 141getters and setters. The documentation of this module still requires a lot
142of work (B<< volunteers welcome >.> >>), but in the meantime you can refer to
143L<this post|http://lo-f.at/glahn/2009/08/WritingPowerfulAccessorsForPerlClasses.html>
144for more information.
963a69a5 145
146=head1 METHODS
147
148=head2 mk_group_accessors
149
8e0a387b 150 __PACKAGE__->mk_group_accessors(simple => 'hair_length', [ hair_color => 'hc' ]);
18922520 151
963a69a5 152=over 4
153
154=item Arguments: $group, @fieldspec
155
156Returns: none
157
158=back
159
160Creates a set of accessors in a given group.
161
162$group is the name of the accessor group for the generated accessors; they
163will call get_$group($field) on get and set_$group($field, $value) on set.
164
22fa6720 165If you want to mimic Class::Accessor's mk_accessors $group has to be 'simple'
166to tell Class::Accessor::Grouped to use its own get_simple and set_simple
167methods.
168
963a69a5 169@fieldspec is a list of field/accessor names; if a fieldspec is a scalar
170this is used as both field and accessor name, if a listref it is expected to
171be of the form [ $accessor, $field ].
172
173=cut
174
175sub mk_group_accessors {
ba8c183b 176 my ($self, $group, @fields) = @_;
963a69a5 177
ba8c183b 178 $self->_mk_group_accessors('make_group_accessor', $group, @fields);
179 return;
963a69a5 180}
181
963a69a5 182=head2 mk_group_ro_accessors
183
8e0a387b 184 __PACKAGE__->mk_group_ro_accessors(simple => 'birthdate', [ social_security_number => 'ssn' ]);
18922520 185
963a69a5 186=over 4
187
188=item Arguments: $group, @fieldspec
189
190Returns: none
191
192=back
193
194Creates a set of read only accessors in a given group. Identical to
a557f8ad 195L</mk_group_accessors> but accessors will throw an error if passed a value
963a69a5 196rather than setting the value.
197
198=cut
199
200sub mk_group_ro_accessors {
ba8c183b 201 my($self, $group, @fields) = @_;
963a69a5 202
ba8c183b 203 $self->_mk_group_accessors('make_group_ro_accessor', $group, @fields);
fa4699e4 204 return;
963a69a5 205}
206
207=head2 mk_group_wo_accessors
208
8e0a387b 209 __PACKAGE__->mk_group_wo_accessors(simple => 'lie', [ subject => 'subj' ]);
18922520 210
963a69a5 211=over 4
212
213=item Arguments: $group, @fieldspec
214
215Returns: none
216
217=back
218
219Creates a set of write only accessors in a given group. Identical to
a557f8ad 220L</mk_group_accessors> but accessors will throw an error if not passed a
963a69a5 221value rather than getting the value.
222
223=cut
224
225sub mk_group_wo_accessors {
ba8c183b 226 my($self, $group, @fields) = @_;
963a69a5 227
ba8c183b 228 $self->_mk_group_accessors('make_group_wo_accessor', $group, @fields);
fa4699e4 229 return;
963a69a5 230}
231
963a69a5 232=head2 get_simple
233
234=over 4
235
236=item Arguments: $field
237
238Returns: $value
239
240=back
241
242Simple getter for hash-based objects which returns the value for the field
243name passed as an argument.
244
245=cut
246
247sub get_simple {
cf8d4321 248 $_[0]->{$_[1]};
963a69a5 249}
250
251=head2 set_simple
252
253=over 4
254
255=item Arguments: $field, $new_value
256
257Returns: $new_value
258
259=back
260
261Simple setter for hash-based objects which sets and then returns the value
262for the field name passed as an argument.
263
264=cut
265
266sub set_simple {
cf8d4321 267 $_[0]->{$_[1]} = $_[2];
963a69a5 268}
269
e6f2a0fd 270
271=head2 get_inherited
272
273=over 4
274
275=item Arguments: $field
276
277Returns: $value
278
279=back
280
331e820d 281Simple getter for Classes and hash-based objects which returns the value for
282the field name passed as an argument. This behaves much like
283L<Class::Data::Accessor> where the field can be set in a base class,
284inherited and changed in subclasses, and inherited and changed for object
285instances.
e6f2a0fd 286
287=cut
288
289sub get_inherited {
48dfec72 290 if ( length (ref ($_[0]) ) ) {
ba8c183b 291 if (Scalar::Util::reftype $_[0] eq 'HASH') {
292 return $_[0]->{$_[1]} if exists $_[0]->{$_[1]};
48dfec72 293 # everything in @_ is aliased, an assignment won't work
294 splice @_, 0, 1, ref($_[0]);
62cf9924 295 }
296 else {
ba8c183b 297 Carp::croak('Cannot get inherited value on an object instance that is not hash-based');
62cf9924 298 }
ba8c183b 299 }
e6f2a0fd 300
48dfec72 301 # if we got this far there is nothing in the instance
302 # OR this is a class call
303 # in any case $_[0] contains the class name (see splice above)
ba8c183b 304 no strict 'refs';
305 no warnings 'uninitialized';
62cf9924 306
ba8c183b 307 my $cag_slot = '::__cag_'. $_[1];
48dfec72 308 return ${$_[0].$cag_slot} if defined(${$_[0].$cag_slot});
e6f2a0fd 309
a3a81175 310 do { return ${$_.$cag_slot} if defined(${$_.$cag_slot}) }
311 for $_[0]->get_super_paths;
c46050d3 312
ba8c183b 313 return undef;
e6f2a0fd 314}
315
316=head2 set_inherited
317
318=over 4
319
320=item Arguments: $field, $new_value
321
322Returns: $new_value
323
324=back
325
331e820d 326Simple setter for Classes and hash-based objects which sets and then returns
327the value for the field name passed as an argument. When called on a hash-based
328object it will set the appropriate hash key value. When called on a class, it
329will set a class level variable.
e6f2a0fd 330
331e820d 331B<Note:>: This method will die if you try to set an object variable on a non
332hash-based object.
e6f2a0fd 333
334=cut
335
336sub set_inherited {
48dfec72 337 if (length (ref ($_[0]) ) ) {
ba8c183b 338 if (Scalar::Util::reftype $_[0] eq 'HASH') {
339 return $_[0]->{$_[1]} = $_[2];
e6f2a0fd 340 } else {
ba8c183b 341 Carp::croak('Cannot set inherited value on an object instance that is not hash-based');
e6f2a0fd 342 };
cf8d4321 343 }
ba8c183b 344
cf8d4321 345 no strict 'refs';
346 ${$_[0].'::__cag_'.$_[1]} = $_[2];
e6f2a0fd 347}
348
331e820d 349=head2 get_component_class
350
351=over 4
352
353=item Arguments: $field
354
355Returns: $value
356
357=back
358
359Gets the value of the specified component class.
360
ba8c183b 361 __PACKAGE__->mk_group_accessors('component_class' => 'result_class');
97d76fb4 362
ba8c183b 363 $self->result_class->method();
97d76fb4 364
ba8c183b 365 ## same as
366 $self->get_component_class('result_class')->method();
331e820d 367
368=cut
369
370sub get_component_class {
cf8d4321 371 $_[0]->get_inherited($_[1]);
331e820d 372};
373
374=head2 set_component_class
375
376=over 4
377
378=item Arguments: $field, $class
379
380Returns: $new_value
381
382=back
383
384Inherited accessor that automatically loads the specified class before setting
385it. This method will die if the specified class could not be loaded.
386
ba8c183b 387 __PACKAGE__->mk_group_accessors('component_class' => 'result_class');
388 __PACKAGE__->result_class('MyClass');
97d76fb4 389
ba8c183b 390 $self->result_class->method();
331e820d 391
392=cut
393
394sub set_component_class {
6d0e8ff2 395 if (defined $_[2] and length $_[2]) {
396 # disable warnings, and prevent $_ being eaten away by a behind-the-scenes
397 # module loading
398 local ($^W, $_);
399
400 if (__CAG_ENV__::UNSTABLE_DOLLARAT) {
401 my $err;
402 {
403 local $@;
404 eval { Module::Runtime::use_package_optimistically($_[2]) }
405 or $err = $@;
406 }
407 Carp::croak("Could not load $_[1] '$_[2]': $err") if defined $err;
ba8c183b 408
6d0e8ff2 409 }
410 else {
411 eval { Module::Runtime::use_package_optimistically($_[2]) }
412 or Carp::croak("Could not load $_[1] '$_[2]': $@");
413 }
ba8c183b 414 };
331e820d 415
cf8d4321 416 $_[0]->set_inherited($_[1], $_[2]);
331e820d 417};
418
8e0a387b 419=head1 INTERNAL METHODS
420
421These methods are documented for clarity, but are never meant to be called
422directly, and are not really meant for overriding either.
423
a49c32d9 424=head2 get_super_paths
425
8e0a387b 426Returns a list of 'parent' or 'super' class names that the current class
427inherited from. This is what drives the traversal done by L</get_inherited>.
a49c32d9 428
429=cut
430
431sub get_super_paths {
cc8ab1f2 432 # get_linear_isa returns the class itself as the 1st element
433 # use @_ as a pre-allocated scratch array
434 (undef, @_) = @{mro::get_linear_isa( length( ref($_[0]) ) ? ref($_[0]) : $_[0] )};
435 @_;
a49c32d9 436};
437
8e0a387b 438=head2 make_group_accessor
439
440 __PACKAGE__->make_group_accessor('simple', 'hair_length', 'hair_length');
441 __PACKAGE__->make_group_accessor('simple', 'hc', 'hair_color');
442
443=over 4
444
445=item Arguments: $group, $field, $accessor
446
447Returns: \&accessor_coderef ?
448
449=back
450
451Called by mk_group_accessors for each entry in @fieldspec. Either returns
452a coderef which will be installed at C<&__PACKAGE__::$accessor>, or returns
453C<undef> if it elects to install the coderef on its own.
454
455=cut
456
457sub make_group_accessor { $gen_accessor->('rw', @_) }
458
459=head2 make_group_ro_accessor
460
461 __PACKAGE__->make_group_ro_accessor('simple', 'birthdate', 'birthdate');
462 __PACKAGE__->make_group_ro_accessor('simple', 'ssn', 'social_security_number');
463
464=over 4
465
466=item Arguments: $group, $field, $accessor
467
468Returns: \&accessor_coderef ?
469
470=back
471
472Called by mk_group_ro_accessors for each entry in @fieldspec. Either returns
473a coderef which will be installed at C<&__PACKAGE__::$accessor>, or returns
474C<undef> if it elects to install the coderef on its own.
475
476=cut
477
478sub make_group_ro_accessor { $gen_accessor->('ro', @_) }
479
480=head2 make_group_wo_accessor
481
482 __PACKAGE__->make_group_wo_accessor('simple', 'lie', 'lie');
483 __PACKAGE__->make_group_wo_accessor('simple', 'subj', 'subject');
484
485=over 4
486
487=item Arguments: $group, $field, $accessor
488
489Returns: \&accessor_coderef ?
490
491=back
492
493Called by mk_group_wo_accessors for each entry in @fieldspec. Either returns
494a coderef which will be installed at C<&__PACKAGE__::$accessor>, or returns
495C<undef> if it elects to install the coderef on its own.
496
497=cut
498
499sub make_group_wo_accessor { $gen_accessor->('wo', @_) }
500
501
9d7d52da 502=head1 PERFORMANCE
15cf8e32 503
8019c4d8 504To provide total flexibility L<Class::Accessor::Grouped> calls methods
505internally while performing get/set actions, which makes it noticeably
506slower than similar modules. To compensate, this module will automatically
507use the insanely fast L<Class::XSAccessor> to generate the C<simple>-group
b36cd259 508accessors if this module is available on your system.
8019c4d8 509
510=head2 Benchmark
511
512This is the result of a set/get/set loop benchmark on perl 5.12.1 with
513thread support, showcasing most popular accessor builders: L<Moose>, L<Mouse>,
af71d687 514L<Moo>, L<CAF|Class::Accessor::Fast>, L<CAF_XS|Class::Accessor::Fast::XS>,
515L<XSA|Class::XSAccessor>, and L<CAF_XSA|Class::XSAccessor::Compat>:
516
517 Rate CAG moOse CAF moUse moo HANDMADE CAF_XS moUse_XS moo_XS CAF_XSA XSA CAG_XS
518 CAG 169/s -- -21% -24% -32% -32% -34% -59% -63% -67% -67% -67% -67%
519 moOse 215/s 27% -- -3% -13% -13% -15% -48% -53% -58% -58% -58% -58%
520 CAF 222/s 31% 3% -- -10% -10% -13% -46% -52% -57% -57% -57% -57%
521 moUse 248/s 46% 15% 11% -- -0% -3% -40% -46% -52% -52% -52% -52%
522 moo 248/s 46% 15% 11% 0% -- -3% -40% -46% -52% -52% -52% -52%
523 HANDMADE 255/s 50% 18% 14% 3% 3% -- -38% -45% -50% -51% -51% -51%
524 CAF_XS 411/s 143% 91% 85% 66% 66% 61% -- -11% -20% -20% -21% -21%
525 moUse_XS 461/s 172% 114% 107% 86% 86% 81% 12% -- -10% -11% -11% -11%
526 moo_XS 514/s 204% 139% 131% 107% 107% 102% 25% 12% -- -0% -1% -1%
527 CAF_XSA 516/s 205% 140% 132% 108% 108% 103% 26% 12% 0% -- -0% -0%
528 XSA 519/s 206% 141% 133% 109% 109% 104% 26% 13% 1% 0% -- -0%
529 CAG_XS 519/s 206% 141% 133% 109% 109% 104% 26% 13% 1% 0% 0% --
8019c4d8 530
531Benchmark program is available in the root of the
532L<repository|http://search.cpan.org/dist/Class-Accessor-Grouped/>:
533
534=head2 Notes on Class::XSAccessor
535
bd83e674 536You can force (or disable) the use of L<Class::XSAccessor> before creating a
537particular C<simple> accessor by either manipulating the global variable
538C<$Class::Accessor::Grouped::USE_XS> to true or false (preferably with
539L<localization|perlfunc/local>, or you can do so before runtime via the
540C<CAG_USE_XS> environment variable.
541
542Since L<Class::XSAccessor> has no knowledge of L</get_simple> and
543L</set_simple> this module does its best to detect if you are overriding
544one of these methods and will fall back to using the perl version of the
545accessor in order to maintain consistency. However be aware that if you
546enable use of C<Class::XSAccessor> (automatically or explicitly), create
547an object, invoke a simple accessor on that object, and B<then> manipulate
548the symbol table to install a C<get/set_simple> override - you get to keep
549all the pieces.
550
963a69a5 551=head1 AUTHORS
552
553Matt S. Trout <mst@shadowcatsystems.co.uk>
ba6f7b1b 554
97972dcb 555Christopher H. Laco <claco@chrislaco.com>
963a69a5 556
8ef9b3ff 557=head1 CONTRIBUTORS
dfb86526 558
ba6f7b1b 559Caelum: Rafael Kitover <rkitover@cpan.org>
560
3b118c10 561frew: Arthur Axel "fREW" Schmidt <frioux@gmail.com>
562
8ef9b3ff 563groditi: Guillermo Roditi <groditi@cpan.org>
ba6f7b1b 564
6a4c729f 565Jason Plum <jason.plum@bmmsi.com>
dfb86526 566
ba6f7b1b 567ribasushi: Peter Rabbitson <ribasushi@cpan.org>
568
569
4fe25633 570=head1 COPYRIGHT & LICENSE
963a69a5 571
af169484 572Copyright (c) 2006-2010 Matt S. Trout <mst@shadowcatsystems.co.uk>
963a69a5 573
4fe25633 574This program is free software; you can redistribute it and/or modify
575it under the same terms as perl itself.
963a69a5 576
4fe25633 577=cut
85ccab9a 578
579########################################################################
580########################################################################
581########################################################################
582#
583# Here be many angry dragons
584# (all code is in private coderefs since everything inherits CAG)
585#
586########################################################################
587########################################################################
588
85ccab9a 589# Autodetect unless flag supplied
85ccab9a 590my $xsa_autodetected;
591if (! defined $USE_XS) {
3f6054c4 592 $USE_XS = __CAG_ENV__::NO_CXSA ? 0 : 1;
85ccab9a 593 $xsa_autodetected++;
594}
595
4d70ba11 596my $perlstring;
597if ($] < '5.008') {
598 require Data::Dumper;
599 my $d = Data::Dumper->new([])->Indent(0)->Purity(0)->Pad('')->Useqq(1)->Terse(1)->Freezer('')->Toaster('');
600 $perlstring = sub { $d->Values([shift])->Dump };
601}
602else {
603 require B;
604 $perlstring = \&B::perlstring;
605}
606
607
85ccab9a 608my $maker_templates = {
609 rw => {
e6bdecbd 610 cxsa_call => 'accessors',
611 pp_generator => sub {
4d70ba11 612 # my ($group, $fieldname) = @_;
613 my $quoted_fieldname = $perlstring->($_[1]);
614 sprintf <<'EOS', ($_[0], $quoted_fieldname) x 2;
615
616@_ > 1
617 ? shift->set_%s(%s, @_)
618 : shift->get_%s(%s)
619EOS
620
85ccab9a 621 },
622 },
623 ro => {
e6bdecbd 624 cxsa_call => 'getters',
625 pp_generator => sub {
4d70ba11 626 # my ($group, $fieldname) = @_;
627 my $quoted_fieldname = $perlstring->($_[1]);
da609a46 628 sprintf <<'EOS', $_[0], $quoted_fieldname;
4d70ba11 629
630@_ > 1
631 ? do {
da609a46 632 my ($meth) = (caller(0))[3] =~ /([^\:]+)$/;
4d70ba11 633 my $class = length( ref($_[0]) ) ? ref($_[0]) : $_[0];
da609a46 634 Carp::croak(
635 "'$meth' cannot alter its value (read-only attribute of class $class)"
4d70ba11 636 );
637 }
638 : shift->get_%s(%s)
639EOS
640
85ccab9a 641 },
642 },
643 wo => {
e6bdecbd 644 cxsa_call => 'setters',
645 pp_generator => sub {
4d70ba11 646 # my ($group, $fieldname) = @_;
647 my $quoted_fieldname = $perlstring->($_[1]);
da609a46 648 sprintf <<'EOS', $_[0], $quoted_fieldname;
4d70ba11 649
650@_ > 1
651 ? shift->set_%s(%s, @_)
652 : do {
da609a46 653 my ($meth) = (caller(0))[3] =~ /([^\:]+)$/;
4d70ba11 654 my $class = length( ref($_[0]) ) ? ref($_[0]) : $_[0];
da609a46 655 Carp::croak(
656 "'$meth' cannot access its value (write-only attribute of class $class)"
4d70ba11 657 );
658 }
659EOS
660
85ccab9a 661 },
662 },
663};
664
00e42e87 665my $cag_eval = sub {
666 #my ($src, $no_warnings, $err_msg) = @_;
667
668 my $src = sprintf "{ %s warnings; use strict; no strict 'refs'; %s }",
669 $_[1] ? 'no' : 'use',
670 $_[0],
671 ;
672
673 my (@rv, $err);
674 {
675 local $@ if __CAG_ENV__::UNSTABLE_DOLLARAT;
676 wantarray
677 ? @rv = eval $src
678 : $rv[0] = eval $src
679 ;
680 $err = $@ if $@ ne '';
681 }
682
683 Carp::croak(join ': ', ($_[2] || 'String-eval failed'), "$err\n$src\n" )
684 if defined $err;
685
686 wantarray ? @rv : $rv[0];
687};
85ccab9a 688
689my ($accessor_maker_cache, $no_xsa_warned_classes);
690
691# can't use pkg_gen to track this stuff, as it doesn't
692# detect superclass mucking
693my $original_simple_getter = __PACKAGE__->can ('get_simple');
694my $original_simple_setter = __PACKAGE__->can ('set_simple');
695
696# Note!!! Unusual signature
697$gen_accessor = sub {
698 my ($type, $class, $group, $field, $methname) = @_;
48dfec72 699 $class = ref $class if length ref $class;
85ccab9a 700
701 # When installing an XSA simple accessor, we need to make sure we are not
702 # short-circuiting a (compile or runtime) get_simple/set_simple override.
703 # What we do here is install a lazy first-access check, which will decide
704 # the ultimate coderef being placed in the accessor slot
f7cf6867 705 #
706 # Also note that the *original* class will always retain this shim, as
707 # different branches inheriting from it may have different overrides.
708 # Thus the final method (properly labeled and all) is installed in the
709 # calling-package's namespace
85ccab9a 710 if ($USE_XS and $group eq 'simple') {
3f6054c4 711 die sprintf( "Class::XSAccessor requested but not available:\n%s\n", __CAG_ENV__::NO_CXSA )
712 if __CAG_ENV__::NO_CXSA;
f7cf6867 713
de167379 714 my ($expected_cref, $cached_implementation);
715 my $ret = $expected_cref = sub {
48dfec72 716 my $current_class = length (ref ($_[0] ) ) ? ref ($_[0]) : $_[0];
f7cf6867 717
de167379 718 # $cached_implementation will be set only if the shim got
719 # 'around'ed, in which case it is handy to avoid re-running
720 # this block over and over again
721 my $resolved_implementation = $cached_implementation->{$current_class} || do {
722 if (
ad0ed975 723 ($current_class->can('get_simple')||0) == $original_simple_getter
de167379 724 &&
ad0ed975 725 ($current_class->can('set_simple')||0) == $original_simple_setter
de167379 726 ) {
727 # nothing has changed, might as well use the XS crefs
728 #
729 # note that by the time this code executes, we already have
730 # *objects* (since XSA works on 'simple' only by definition).
731 # If someone is mucking with the symbol table *after* there
732 # are some objects already - look! many, shiny pieces! :)
733 #
734 # The weird breeder thingy is because XSA does not have an
735 # interface returning *just* a coderef, without installing it
736 # anywhere :(
737 Class::XSAccessor->import(
738 replace => 1,
739 class => '__CAG__XSA__BREEDER__',
e6bdecbd 740 $maker_templates->{$type}{cxsa_call} => {
de167379 741 $methname => $field,
742 },
743 );
744 __CAG__XSA__BREEDER__->can($methname);
745 }
746 else {
747 if (! $xsa_autodetected and ! $no_xsa_warned_classes->{$current_class}++) {
748 # not using Carp since the line where this happens doesn't mean much
749 warn 'Explicitly requested use of Class::XSAccessor disabled for objects of class '
750 . "'$current_class' inheriting from '$class' due to an overriden get_simple and/or "
751 . "set_simple\n";
752 }
753
754 do {
755 # that's faster than local
756 $USE_XS = 0;
757 my $c = $gen_accessor->($type, $class, 'simple', $field, $methname);
758 $USE_XS = 1;
759 $c;
760 };
761 }
762 };
763
764 # if after this shim was created someone wrapped it with an 'around',
765 # we can not blindly reinstall the method slot - we will destroy the
766 # wrapper. Silently chain execution further...
ad0ed975 767 if ( !$expected_cref or $expected_cref != ($current_class->can($methname)||0) ) {
de167379 768
769 # there is no point in re-determining it on every subsequent call,
770 # just store for future reference
771 $cached_implementation->{$current_class} ||= $resolved_implementation;
772
773 # older perls segfault if the cref behind the goto throws
774 # http://rt.perl.org/rt3/Public/Bug/Display.html?id=35878
3f6054c4 775 return $resolved_implementation->(@_) if __CAG_ENV__::BROKEN_GOTO;
de167379 776
777 goto $resolved_implementation;
778 }
779
3f6054c4 780 if (__CAG_ENV__::TRACK_UNDEFER_FAIL) {
2d392af1 781 my $deferred_calls_seen = do {
782 no strict 'refs';
783 \%{"${current_class}::__cag_deferred_xs_shim_invocations"}
784 };
e6f2993f 785 my @cframe = caller(0);
2d392af1 786 if (my $already_seen = $deferred_calls_seen->{$cframe[3]}) {
e6f2993f 787 Carp::carp (
788 "Deferred version of method $cframe[3] invoked more than once (originally "
2d392af1 789 . "invoked at $already_seen). This is a strong indication your code has "
790 . 'cached the original ->can derived method coderef, and is using it instead '
e6bdecbd 791 . 'of the proper method re-lookup, causing minor performance regressions'
e6f2993f 792 );
793 }
794 else {
2d392af1 795 $deferred_calls_seen->{$cframe[3]} = "$cframe[1] line $cframe[2]";
e6f2993f 796 }
797 }
798
de167379 799 # install the resolved implementation into the code slot so we do not
800 # come here anymore (hopefully)
801 # since XSAccessor was available - so is Sub::Name
802 {
e6f2993f 803 no strict 'refs';
804 no warnings 'redefine';
85ccab9a 805
f7cf6867 806 my $fq_name = "${current_class}::${methname}";
de167379 807 *$fq_name = Sub::Name::subname($fq_name, $resolved_implementation);
808
809 # need to update what the shim expects too *in case* its
810 # ->can was cached for some moronic reason
811 $expected_cref = $resolved_implementation;
812 Scalar::Util::weaken($expected_cref);
f7cf6867 813 }
85ccab9a 814
f7cf6867 815 # older perls segfault if the cref behind the goto throws
816 # http://rt.perl.org/rt3/Public/Bug/Display.html?id=35878
3f6054c4 817 return $resolved_implementation->(@_) if __CAG_ENV__::BROKEN_GOTO;
85ccab9a 818
de167379 819 goto $resolved_implementation;
f7cf6867 820 };
de167379 821
822 Scalar::Util::weaken($expected_cref); # to break the self-reference
823 $ret;
85ccab9a 824 }
825
826 # no Sub::Name - just install the coderefs directly (compiling every time)
3f6054c4 827 elsif (__CAG_ENV__::NO_SUBNAME) {
eda06cc6 828 my $src = $accessor_maker_cache->{source}{$type}{$group}{$field} ||=
e6bdecbd 829 $maker_templates->{$type}{pp_generator}->($group, $field);
eda06cc6 830
00e42e87 831 $cag_eval->(
832 "no warnings 'redefine'; sub ${class}::${methname} { $src }; 1",
833 );
eda06cc6 834
e6f2993f 835 undef; # so that no further attempt will be made to install anything
85ccab9a 836 }
837
838 # a coderef generator with a variable pad (returns a fresh cref on every invocation)
85ccab9a 839 else {
e6f2993f 840 ($accessor_maker_cache->{pp}{$type}{$group}{$field} ||= do {
eda06cc6 841 my $src = $accessor_maker_cache->{source}{$type}{$group}{$field} ||=
e6bdecbd 842 $maker_templates->{$type}{pp_generator}->($group, $field);
eda06cc6 843
00e42e87 844 $cag_eval->( "sub { my \$dummy; sub { \$dummy if 0; $src } }" );
85ccab9a 845 })->()
846 }
847};
848
8491;