Stop leaking extra methods into the inheritance chain
[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
dff52792 18our $VERSION = '0.10008';
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 );
6c6bc8c2 73
e4cb6320 74 require B;
75 # a perl 5.6 kludge
76 unless (B->can('perlstring')) {
77 require Data::Dumper;
78 my $d = Data::Dumper->new([])->Indent(0)->Purity(0)->Pad('')->Useqq(1)->Terse(1)->Freezer('')->Toaster('');
79 *B::perlstring = sub { $d->Values([shift])->Dump };
80 }
6d0e8ff2 81}
82
8079caeb 83# Yes this method is undocumented
85ccab9a 84# Yes it should be a private coderef like all the rest at the end of this file
8079caeb 85# No we can't do that (yet) because the DBIC-CDBI compat layer overrides it
86# %$*@!?&!&#*$!!!
6c6bc8c2 87
88my $illegal_accessors_warned;
8079caeb 89sub _mk_group_accessors {
ba8c183b 90 my($self, $maker, $group, @fields) = @_;
48dfec72 91 my $class = length (ref ($self) ) ? ref ($self) : $self;
eece2562 92
ba8c183b 93 no strict 'refs';
94 no warnings 'redefine';
eece2562 95
ba8c183b 96 # So we don't have to do lots of lookups inside the loop.
97 $maker = $self->can($maker) unless ref $maker;
eece2562 98
ba8c183b 99 for (@fields) {
eece2562 100
ba8c183b 101 my ($name, $field) = (ref $_) ? (@$_) : ($_, $_);
eece2562 102
6c6bc8c2 103 if ($name !~ /\A[A-Z_a-z][0-9A-Z_a-z]*\z/) {
104
105 if ($name =~ /\0/) {
106 Carp::croak(sprintf
107 "Illegal accessor name %s - nulls should never appear in stash keys",
e4cb6320 108 B::perlstring($name),
6c6bc8c2 109 );
110 }
111 elsif (! $ENV{CAG_ILLEGAL_ACCESSOR_NAME_OK} ) {
112 Carp::croak(
113 "Illegal accessor name '$name'. If you want CAG to attempt creating "
114 . 'it anyway (possible if Sub::Name is available) set '
115 . '$ENV{CAG_ILLEGAL_ACCESSOR_NAME_OK}'
116 );
117 }
118 elsif (__CAG_ENV__::NO_SUBNAME) {
119 Carp::croak(
120 "Unable to install accessor with illegal name '$name': "
121 . 'Sub::Name not available'
122 );
123 }
124 elsif (
125 # Because one of the former maintainers of DBIC::SL is a raging
126 # idiot, there is now a ton of DBIC code out there that attempts
127 # to create column accessors with illegal names. In the interest
128 # of not cluttering the logs of unsuspecting victims (unsuspecting
129 # because these accessors are unusuable anyway) we provide an
130 # explicit "do not warn at all" escape, until all such code is
131 # fixed (this will be a loooooong time >:(
132 $ENV{CAG_ILLEGAL_ACCESSOR_NAME_OK} ne 'DO_NOT_WARN'
133 and
134 ! $illegal_accessors_warned->{$class}++
135 ) {
136 Carp::carp(
137 "Installing illegal accessor '$name' into $class, see "
138 . 'documentation for more details'
139 );
140 }
141 }
510d7274 142
143 Carp::carp("Having a data accessor named '$name' in '$class' is unwise.")
144 if $name =~ /\A(?: DESTROY | AUTOLOAD | CLONE )\z/x;
33fe2299 145
ba8c183b 146 my $alias = "_${name}_accessor";
eece2562 147
48dfec72 148 for ($name, $alias) {
eece2562 149
ba8c183b 150 # the maker may elect to not return anything, meaning it already
151 # installed the coderef for us (e.g. lack of Sub::Name)
48dfec72 152 my $cref = $self->$maker($group, $field, $_)
ba8c183b 153 or next;
eece2562 154
48dfec72 155 my $fq_meth = "${class}::$_";
eece2562 156
ba8c183b 157 *$fq_meth = Sub::Name::subname($fq_meth, $cref);
158 #unless defined &{$class."\:\:$field"}
eece2562 159 }
ba8c183b 160 }
eece2562 161};
162
e6bdecbd 163# $gen_accessor coderef is setup at the end for clarity
85ccab9a 164my $gen_accessor;
eece2562 165
963a69a5 166=head1 NAME
167
1ad8d8c6 168Class::Accessor::Grouped - Lets you build groups of accessors
963a69a5 169
170=head1 SYNOPSIS
171
3b118c10 172 use base 'Class::Accessor::Grouped';
173
174 # make basic accessors for objects
175 __PACKAGE__->mk_group_accessors(simple => qw(id name email));
176
177 # make accessor that works for objects and classes
178 __PACKAGE__->mk_group_accessors(inherited => 'awesome_level');
179
533d4d96 180 # make an accessor which calls a custom pair of getters/setters
181 sub get_column { ... this will be called when you do $obj->name() ... }
182 sub set_column { ... this will be called when you do $obj->name('foo') ... }
183 __PACKAGE__->mk_group_accessors(column => 'name');
184
963a69a5 185=head1 DESCRIPTION
186
187This class lets you build groups of accessors that will call different
533d4d96 188getters and setters. The documentation of this module still requires a lot
189of work (B<< volunteers welcome >.> >>), but in the meantime you can refer to
190L<this post|http://lo-f.at/glahn/2009/08/WritingPowerfulAccessorsForPerlClasses.html>
191for more information.
963a69a5 192
6c6bc8c2 193=head2 Notes on accessor names
194
195In general method names in Perl are considered identifiers, and as such need to
196conform to the identifier specification of C<qr/\A[A-Z_a-z][0-9A-Z_a-z]*\z/>.
197While it is rather easy to invoke methods with non-standard names
198(C<< $obj->${\"anything goes"} >>), it is not possible to properly declare such
199methods without the use of L<Sub::Name>. Since this module must be able to
200function identically with and without its optional dependencies, starting with
201version C<0.10008> attempting to declare an accessor with a non-standard name
202is a fatal error (such operations would silently succeed since version
203C<0.08004>, as long as L<Sub::Name> is present, or otherwise would result in a
204syntax error during a string eval).
205
206Unfortunately in the years since C<0.08004> a rather large body of code
207accumulated in the wild that does attempt to declare accessors with funny
208names. One notable perpetrator is L<DBIx::Class::Schema::Loader>, which under
209certain conditions could create accessors of the C<column> group which start
210with numbers and/or some other punctuation (the proper way would be to declare
211columns with the C<accessor> attribute set to C<undef>).
212
213Therefore an escape mechanism is provided via the environment variable
214C<CAG_ILLEGAL_ACCESSOR_NAME_OK>. When set to a true value, one warning is
215issued B<per class> on attempts to declare an accessor with a non-conforming
216name, and as long as L<Sub::Name> is available all accessors will be properly
217created. Regardless of this setting, accessor names containing nulls C<"\0">
218are disallowed, due to various deficiencies in perl itself.
219
220If your code base has too many instances of illegal accessor declarations, and
221a fix is not feasible due to time constraints, it is possible to disable the
222warnings altogether by setting C<$ENV{CAG_ILLEGAL_ACCESSOR_NAME_OK}> to
223C<DO_NOT_WARN> (observe capitalization).
224
963a69a5 225=head1 METHODS
226
227=head2 mk_group_accessors
228
8e0a387b 229 __PACKAGE__->mk_group_accessors(simple => 'hair_length', [ hair_color => 'hc' ]);
18922520 230
963a69a5 231=over 4
232
233=item Arguments: $group, @fieldspec
234
235Returns: none
236
237=back
238
239Creates a set of accessors in a given group.
240
241$group is the name of the accessor group for the generated accessors; they
242will call get_$group($field) on get and set_$group($field, $value) on set.
243
22fa6720 244If you want to mimic Class::Accessor's mk_accessors $group has to be 'simple'
245to tell Class::Accessor::Grouped to use its own get_simple and set_simple
246methods.
247
963a69a5 248@fieldspec is a list of field/accessor names; if a fieldspec is a scalar
249this is used as both field and accessor name, if a listref it is expected to
250be of the form [ $accessor, $field ].
251
252=cut
253
254sub mk_group_accessors {
ba8c183b 255 my ($self, $group, @fields) = @_;
963a69a5 256
ba8c183b 257 $self->_mk_group_accessors('make_group_accessor', $group, @fields);
258 return;
963a69a5 259}
260
963a69a5 261=head2 mk_group_ro_accessors
262
8e0a387b 263 __PACKAGE__->mk_group_ro_accessors(simple => 'birthdate', [ social_security_number => 'ssn' ]);
18922520 264
963a69a5 265=over 4
266
267=item Arguments: $group, @fieldspec
268
269Returns: none
270
271=back
272
273Creates a set of read only accessors in a given group. Identical to
a557f8ad 274L</mk_group_accessors> but accessors will throw an error if passed a value
963a69a5 275rather than setting the value.
276
277=cut
278
279sub mk_group_ro_accessors {
ba8c183b 280 my($self, $group, @fields) = @_;
963a69a5 281
ba8c183b 282 $self->_mk_group_accessors('make_group_ro_accessor', $group, @fields);
fa4699e4 283 return;
963a69a5 284}
285
286=head2 mk_group_wo_accessors
287
8e0a387b 288 __PACKAGE__->mk_group_wo_accessors(simple => 'lie', [ subject => 'subj' ]);
18922520 289
963a69a5 290=over 4
291
292=item Arguments: $group, @fieldspec
293
294Returns: none
295
296=back
297
298Creates a set of write only accessors in a given group. Identical to
a557f8ad 299L</mk_group_accessors> but accessors will throw an error if not passed a
963a69a5 300value rather than getting the value.
301
302=cut
303
304sub mk_group_wo_accessors {
ba8c183b 305 my($self, $group, @fields) = @_;
963a69a5 306
ba8c183b 307 $self->_mk_group_accessors('make_group_wo_accessor', $group, @fields);
fa4699e4 308 return;
963a69a5 309}
310
963a69a5 311=head2 get_simple
312
313=over 4
314
315=item Arguments: $field
316
317Returns: $value
318
319=back
320
321Simple getter for hash-based objects which returns the value for the field
322name passed as an argument.
323
324=cut
325
326sub get_simple {
cf8d4321 327 $_[0]->{$_[1]};
963a69a5 328}
329
330=head2 set_simple
331
332=over 4
333
334=item Arguments: $field, $new_value
335
336Returns: $new_value
337
338=back
339
340Simple setter for hash-based objects which sets and then returns the value
341for the field name passed as an argument.
342
343=cut
344
345sub set_simple {
cf8d4321 346 $_[0]->{$_[1]} = $_[2];
963a69a5 347}
348
e6f2a0fd 349
350=head2 get_inherited
351
352=over 4
353
354=item Arguments: $field
355
356Returns: $value
357
358=back
359
331e820d 360Simple getter for Classes and hash-based objects which returns the value for
361the field name passed as an argument. This behaves much like
362L<Class::Data::Accessor> where the field can be set in a base class,
363inherited and changed in subclasses, and inherited and changed for object
364instances.
e6f2a0fd 365
366=cut
367
368sub get_inherited {
48dfec72 369 if ( length (ref ($_[0]) ) ) {
ba8c183b 370 if (Scalar::Util::reftype $_[0] eq 'HASH') {
371 return $_[0]->{$_[1]} if exists $_[0]->{$_[1]};
48dfec72 372 # everything in @_ is aliased, an assignment won't work
373 splice @_, 0, 1, ref($_[0]);
62cf9924 374 }
375 else {
ba8c183b 376 Carp::croak('Cannot get inherited value on an object instance that is not hash-based');
62cf9924 377 }
ba8c183b 378 }
e6f2a0fd 379
48dfec72 380 # if we got this far there is nothing in the instance
381 # OR this is a class call
382 # in any case $_[0] contains the class name (see splice above)
ba8c183b 383 no strict 'refs';
384 no warnings 'uninitialized';
62cf9924 385
ba8c183b 386 my $cag_slot = '::__cag_'. $_[1];
48dfec72 387 return ${$_[0].$cag_slot} if defined(${$_[0].$cag_slot});
e6f2a0fd 388
a3a81175 389 do { return ${$_.$cag_slot} if defined(${$_.$cag_slot}) }
390 for $_[0]->get_super_paths;
c46050d3 391
ba8c183b 392 return undef;
e6f2a0fd 393}
394
395=head2 set_inherited
396
397=over 4
398
399=item Arguments: $field, $new_value
400
401Returns: $new_value
402
403=back
404
331e820d 405Simple setter for Classes and hash-based objects which sets and then returns
406the value for the field name passed as an argument. When called on a hash-based
407object it will set the appropriate hash key value. When called on a class, it
408will set a class level variable.
e6f2a0fd 409
331e820d 410B<Note:>: This method will die if you try to set an object variable on a non
411hash-based object.
e6f2a0fd 412
413=cut
414
415sub set_inherited {
48dfec72 416 if (length (ref ($_[0]) ) ) {
ba8c183b 417 if (Scalar::Util::reftype $_[0] eq 'HASH') {
418 return $_[0]->{$_[1]} = $_[2];
e6f2a0fd 419 } else {
ba8c183b 420 Carp::croak('Cannot set inherited value on an object instance that is not hash-based');
e6f2a0fd 421 };
cf8d4321 422 }
ba8c183b 423
cf8d4321 424 no strict 'refs';
425 ${$_[0].'::__cag_'.$_[1]} = $_[2];
e6f2a0fd 426}
427
331e820d 428=head2 get_component_class
429
430=over 4
431
432=item Arguments: $field
433
434Returns: $value
435
436=back
437
438Gets the value of the specified component class.
439
ba8c183b 440 __PACKAGE__->mk_group_accessors('component_class' => 'result_class');
97d76fb4 441
ba8c183b 442 $self->result_class->method();
97d76fb4 443
ba8c183b 444 ## same as
445 $self->get_component_class('result_class')->method();
331e820d 446
447=cut
448
449sub get_component_class {
cf8d4321 450 $_[0]->get_inherited($_[1]);
331e820d 451};
452
453=head2 set_component_class
454
455=over 4
456
457=item Arguments: $field, $class
458
459Returns: $new_value
460
461=back
462
463Inherited accessor that automatically loads the specified class before setting
464it. This method will die if the specified class could not be loaded.
465
ba8c183b 466 __PACKAGE__->mk_group_accessors('component_class' => 'result_class');
467 __PACKAGE__->result_class('MyClass');
97d76fb4 468
ba8c183b 469 $self->result_class->method();
331e820d 470
471=cut
472
473sub set_component_class {
6d0e8ff2 474 if (defined $_[2] and length $_[2]) {
475 # disable warnings, and prevent $_ being eaten away by a behind-the-scenes
476 # module loading
477 local ($^W, $_);
478
479 if (__CAG_ENV__::UNSTABLE_DOLLARAT) {
480 my $err;
481 {
482 local $@;
483 eval { Module::Runtime::use_package_optimistically($_[2]) }
484 or $err = $@;
485 }
486 Carp::croak("Could not load $_[1] '$_[2]': $err") if defined $err;
ba8c183b 487
6d0e8ff2 488 }
489 else {
490 eval { Module::Runtime::use_package_optimistically($_[2]) }
491 or Carp::croak("Could not load $_[1] '$_[2]': $@");
492 }
ba8c183b 493 };
331e820d 494
cf8d4321 495 $_[0]->set_inherited($_[1], $_[2]);
331e820d 496};
497
8e0a387b 498=head1 INTERNAL METHODS
499
500These methods are documented for clarity, but are never meant to be called
501directly, and are not really meant for overriding either.
502
a49c32d9 503=head2 get_super_paths
504
8e0a387b 505Returns a list of 'parent' or 'super' class names that the current class
506inherited from. This is what drives the traversal done by L</get_inherited>.
a49c32d9 507
508=cut
509
510sub get_super_paths {
cc8ab1f2 511 # get_linear_isa returns the class itself as the 1st element
512 # use @_ as a pre-allocated scratch array
513 (undef, @_) = @{mro::get_linear_isa( length( ref($_[0]) ) ? ref($_[0]) : $_[0] )};
514 @_;
a49c32d9 515};
516
8e0a387b 517=head2 make_group_accessor
518
519 __PACKAGE__->make_group_accessor('simple', 'hair_length', 'hair_length');
520 __PACKAGE__->make_group_accessor('simple', 'hc', 'hair_color');
521
522=over 4
523
524=item Arguments: $group, $field, $accessor
525
526Returns: \&accessor_coderef ?
527
528=back
529
530Called by mk_group_accessors for each entry in @fieldspec. Either returns
531a coderef which will be installed at C<&__PACKAGE__::$accessor>, or returns
532C<undef> if it elects to install the coderef on its own.
533
534=cut
535
536sub make_group_accessor { $gen_accessor->('rw', @_) }
537
538=head2 make_group_ro_accessor
539
540 __PACKAGE__->make_group_ro_accessor('simple', 'birthdate', 'birthdate');
541 __PACKAGE__->make_group_ro_accessor('simple', 'ssn', 'social_security_number');
542
543=over 4
544
545=item Arguments: $group, $field, $accessor
546
547Returns: \&accessor_coderef ?
548
549=back
550
551Called by mk_group_ro_accessors for each entry in @fieldspec. Either returns
552a coderef which will be installed at C<&__PACKAGE__::$accessor>, or returns
553C<undef> if it elects to install the coderef on its own.
554
555=cut
556
557sub make_group_ro_accessor { $gen_accessor->('ro', @_) }
558
559=head2 make_group_wo_accessor
560
561 __PACKAGE__->make_group_wo_accessor('simple', 'lie', 'lie');
562 __PACKAGE__->make_group_wo_accessor('simple', 'subj', 'subject');
563
564=over 4
565
566=item Arguments: $group, $field, $accessor
567
568Returns: \&accessor_coderef ?
569
570=back
571
572Called by mk_group_wo_accessors for each entry in @fieldspec. Either returns
573a coderef which will be installed at C<&__PACKAGE__::$accessor>, or returns
574C<undef> if it elects to install the coderef on its own.
575
576=cut
577
578sub make_group_wo_accessor { $gen_accessor->('wo', @_) }
579
580
9d7d52da 581=head1 PERFORMANCE
15cf8e32 582
8019c4d8 583To provide total flexibility L<Class::Accessor::Grouped> calls methods
584internally while performing get/set actions, which makes it noticeably
585slower than similar modules. To compensate, this module will automatically
586use the insanely fast L<Class::XSAccessor> to generate the C<simple>-group
b36cd259 587accessors if this module is available on your system.
8019c4d8 588
589=head2 Benchmark
590
e89e4f1a 591This is the benchmark of 200 get/get/set/get/set cycles on perl 5.16.2 with
592thread support, showcasing how this modules L<simple (CAG_S)|/get_simple>,
593L<inherited (CAG_INH)|/get_inherited> and L<inherited with parent-class data
594(CAG_INHP)|/get_inherited> accessors stack up against most popular accessor
595builders: L<Moose>, L<Moo>, L<Mo>, L<Mouse> (both pure-perl and XS variant),
596L<Object::Tiny::RW (OTRW)|Object::Tiny::RW>,
597L<Class::Accessor (CA)|Class::Accessor>,
598L<Class::Accessor::Lite (CAL)|Class::Accessor::Lite>,
599L<Class::Accessor::Fast (CAF)|Class::Accessor::Fast>,
600L<Class::Accessor::Fast::XS (CAF_XS)|Class::Accessor::Fast::XS>
601and L<Class::XSAccessor (XSA)|Class::XSAccessor>
602
603 Rate CAG_INHP CAG_INH CA CAG_S CAF moOse OTRW CAL mo moUse HANDMADE moo CAF_XS moUse_XS XSA
604
605 CAG_INHP 287.021+-0.02/s -- -0.3% -10.0% -37.1% -53.1% -53.6% -53.7% -54.1% -56.9% -59.0% -59.6% -59.8% -78.7% -81.9% -83.5%
606
607 CAG_INH 288.025+-0.031/s 0.3% -- -9.7% -36.9% -52.9% -53.5% -53.5% -53.9% -56.7% -58.8% -59.5% -59.7% -78.6% -81.9% -83.5%
608
609 CA 318.967+-0.047/s 11.1% 10.7% -- -30.1% -47.9% -48.5% -48.5% -49.0% -52.1% -54.4% -55.1% -55.3% -76.3% -79.9% -81.7%
610
611 CAG_S 456.107+-0.054/s 58.9% 58.4% 43.0% -- -25.4% -26.3% -26.4% -27.0% -31.5% -34.8% -35.8% -36.1% -66.1% -71.3% -73.9%
612
613 CAF 611.745+-0.099/s 113.1% 112.4% 91.8% 34.1% -- -1.2% -1.2% -2.1% -8.1% -12.6% -14.0% -14.3% -54.5% -61.5% -64.9%
614
615 moOse 619.051+-0.059/s 115.7% 114.9% 94.1% 35.7% 1.2% -- -0.1% -1.0% -7.0% -11.6% -12.9% -13.3% -54.0% -61.0% -64.5%
616
617 OTRW 619.475+-0.1/s 115.8% 115.1% 94.2% 35.8% 1.3% 0.1% -- -0.9% -6.9% -11.5% -12.9% -13.2% -54.0% -61.0% -64.5%
618
619 CAL 625.106+-0.085/s 117.8% 117.0% 96.0% 37.1% 2.2% 1.0% 0.9% -- -6.1% -10.7% -12.1% -12.5% -53.5% -60.6% -64.2%
620
621 mo 665.44+-0.12/s 131.8% 131.0% 108.6% 45.9% 8.8% 7.5% 7.4% 6.5% -- -4.9% -6.4% -6.8% -50.5% -58.1% -61.9%
622
623 moUse 699.9+-0.15/s 143.9% 143.0% 119.4% 53.5% 14.4% 13.1% 13.0% 12.0% 5.2% -- -1.6% -2.0% -48.0% -55.9% -59.9%
624
625 HANDMADE 710.98+-0.16/s 147.7% 146.8% 122.9% 55.9% 16.2% 14.9% 14.8% 13.7% 6.8% 1.6% -- -0.4% -47.2% -55.2% -59.2%
626
627 moo 714.04+-0.13/s 148.8% 147.9% 123.9% 56.6% 16.7% 15.3% 15.3% 14.2% 7.3% 2.0% 0.4% -- -46.9% -55.0% -59.1%
628
629 CAF_XS 1345.55+-0.051/s 368.8% 367.2% 321.8% 195.0% 120.0% 117.4% 117.2% 115.3% 102.2% 92.2% 89.3% 88.4% -- -15.3% -22.9%
630
631 moUse_XS 1588+-0.036/s 453.3% 451.3% 397.9% 248.2% 159.6% 156.5% 156.3% 154.0% 138.6% 126.9% 123.4% 122.4% 18.0% -- -9.0%
632
633 XSA 1744.67+-0.052/s 507.9% 505.7% 447.0% 282.5% 185.2% 181.8% 181.6% 179.1% 162.2% 149.3% 145.4% 144.3% 29.7% 9.9% --
634
635Benchmarking program is available in the root of the
8019c4d8 636L<repository|http://search.cpan.org/dist/Class-Accessor-Grouped/>:
637
638=head2 Notes on Class::XSAccessor
639
bd83e674 640You can force (or disable) the use of L<Class::XSAccessor> before creating a
641particular C<simple> accessor by either manipulating the global variable
642C<$Class::Accessor::Grouped::USE_XS> to true or false (preferably with
643L<localization|perlfunc/local>, or you can do so before runtime via the
644C<CAG_USE_XS> environment variable.
645
646Since L<Class::XSAccessor> has no knowledge of L</get_simple> and
647L</set_simple> this module does its best to detect if you are overriding
648one of these methods and will fall back to using the perl version of the
649accessor in order to maintain consistency. However be aware that if you
650enable use of C<Class::XSAccessor> (automatically or explicitly), create
651an object, invoke a simple accessor on that object, and B<then> manipulate
652the symbol table to install a C<get/set_simple> override - you get to keep
653all the pieces.
654
963a69a5 655=head1 AUTHORS
656
657Matt S. Trout <mst@shadowcatsystems.co.uk>
ba6f7b1b 658
97972dcb 659Christopher H. Laco <claco@chrislaco.com>
963a69a5 660
8ef9b3ff 661=head1 CONTRIBUTORS
dfb86526 662
ba6f7b1b 663Caelum: Rafael Kitover <rkitover@cpan.org>
664
3b118c10 665frew: Arthur Axel "fREW" Schmidt <frioux@gmail.com>
666
8ef9b3ff 667groditi: Guillermo Roditi <groditi@cpan.org>
ba6f7b1b 668
6a4c729f 669Jason Plum <jason.plum@bmmsi.com>
dfb86526 670
ba6f7b1b 671ribasushi: Peter Rabbitson <ribasushi@cpan.org>
672
673
4fe25633 674=head1 COPYRIGHT & LICENSE
963a69a5 675
af169484 676Copyright (c) 2006-2010 Matt S. Trout <mst@shadowcatsystems.co.uk>
963a69a5 677
4fe25633 678This program is free software; you can redistribute it and/or modify
679it under the same terms as perl itself.
963a69a5 680
4fe25633 681=cut
85ccab9a 682
683########################################################################
684########################################################################
685########################################################################
686#
687# Here be many angry dragons
688# (all code is in private coderefs since everything inherits CAG)
689#
690########################################################################
691########################################################################
692
85ccab9a 693# Autodetect unless flag supplied
85ccab9a 694my $xsa_autodetected;
695if (! defined $USE_XS) {
3f6054c4 696 $USE_XS = __CAG_ENV__::NO_CXSA ? 0 : 1;
85ccab9a 697 $xsa_autodetected++;
698}
699
4d70ba11 700
85ccab9a 701my $maker_templates = {
702 rw => {
e6bdecbd 703 cxsa_call => 'accessors',
704 pp_generator => sub {
4d70ba11 705 # my ($group, $fieldname) = @_;
e4cb6320 706 my $quoted_fieldname = B::perlstring($_[1]);
4d70ba11 707 sprintf <<'EOS', ($_[0], $quoted_fieldname) x 2;
708
709@_ > 1
710 ? shift->set_%s(%s, @_)
711 : shift->get_%s(%s)
712EOS
713
85ccab9a 714 },
715 },
716 ro => {
e6bdecbd 717 cxsa_call => 'getters',
718 pp_generator => sub {
4d70ba11 719 # my ($group, $fieldname) = @_;
e4cb6320 720 my $quoted_fieldname = B::perlstring($_[1]);
da609a46 721 sprintf <<'EOS', $_[0], $quoted_fieldname;
4d70ba11 722
723@_ > 1
724 ? do {
da609a46 725 my ($meth) = (caller(0))[3] =~ /([^\:]+)$/;
4d70ba11 726 my $class = length( ref($_[0]) ) ? ref($_[0]) : $_[0];
da609a46 727 Carp::croak(
728 "'$meth' cannot alter its value (read-only attribute of class $class)"
4d70ba11 729 );
730 }
731 : shift->get_%s(%s)
732EOS
733
85ccab9a 734 },
735 },
736 wo => {
e6bdecbd 737 cxsa_call => 'setters',
738 pp_generator => sub {
4d70ba11 739 # my ($group, $fieldname) = @_;
e4cb6320 740 my $quoted_fieldname = B::perlstring($_[1]);
da609a46 741 sprintf <<'EOS', $_[0], $quoted_fieldname;
4d70ba11 742
743@_ > 1
744 ? shift->set_%s(%s, @_)
745 : do {
da609a46 746 my ($meth) = (caller(0))[3] =~ /([^\:]+)$/;
4d70ba11 747 my $class = length( ref($_[0]) ) ? ref($_[0]) : $_[0];
da609a46 748 Carp::croak(
749 "'$meth' cannot access its value (write-only attribute of class $class)"
4d70ba11 750 );
751 }
752EOS
753
85ccab9a 754 },
755 },
756};
757
00e42e87 758my $cag_eval = sub {
759 #my ($src, $no_warnings, $err_msg) = @_;
760
761 my $src = sprintf "{ %s warnings; use strict; no strict 'refs'; %s }",
762 $_[1] ? 'no' : 'use',
763 $_[0],
764 ;
765
766 my (@rv, $err);
767 {
768 local $@ if __CAG_ENV__::UNSTABLE_DOLLARAT;
769 wantarray
770 ? @rv = eval $src
771 : $rv[0] = eval $src
772 ;
773 $err = $@ if $@ ne '';
774 }
775
776 Carp::croak(join ': ', ($_[2] || 'String-eval failed'), "$err\n$src\n" )
777 if defined $err;
778
779 wantarray ? @rv : $rv[0];
780};
85ccab9a 781
782my ($accessor_maker_cache, $no_xsa_warned_classes);
783
784# can't use pkg_gen to track this stuff, as it doesn't
785# detect superclass mucking
786my $original_simple_getter = __PACKAGE__->can ('get_simple');
787my $original_simple_setter = __PACKAGE__->can ('set_simple');
788
789# Note!!! Unusual signature
790$gen_accessor = sub {
791 my ($type, $class, $group, $field, $methname) = @_;
48dfec72 792 $class = ref $class if length ref $class;
85ccab9a 793
794 # When installing an XSA simple accessor, we need to make sure we are not
795 # short-circuiting a (compile or runtime) get_simple/set_simple override.
796 # What we do here is install a lazy first-access check, which will decide
797 # the ultimate coderef being placed in the accessor slot
f7cf6867 798 #
799 # Also note that the *original* class will always retain this shim, as
800 # different branches inheriting from it may have different overrides.
801 # Thus the final method (properly labeled and all) is installed in the
802 # calling-package's namespace
85ccab9a 803 if ($USE_XS and $group eq 'simple') {
3f6054c4 804 die sprintf( "Class::XSAccessor requested but not available:\n%s\n", __CAG_ENV__::NO_CXSA )
805 if __CAG_ENV__::NO_CXSA;
f7cf6867 806
de167379 807 my ($expected_cref, $cached_implementation);
808 my $ret = $expected_cref = sub {
48dfec72 809 my $current_class = length (ref ($_[0] ) ) ? ref ($_[0]) : $_[0];
f7cf6867 810
de167379 811 # $cached_implementation will be set only if the shim got
812 # 'around'ed, in which case it is handy to avoid re-running
813 # this block over and over again
814 my $resolved_implementation = $cached_implementation->{$current_class} || do {
815 if (
ad0ed975 816 ($current_class->can('get_simple')||0) == $original_simple_getter
de167379 817 &&
ad0ed975 818 ($current_class->can('set_simple')||0) == $original_simple_setter
de167379 819 ) {
820 # nothing has changed, might as well use the XS crefs
821 #
822 # note that by the time this code executes, we already have
823 # *objects* (since XSA works on 'simple' only by definition).
824 # If someone is mucking with the symbol table *after* there
825 # are some objects already - look! many, shiny pieces! :)
826 #
827 # The weird breeder thingy is because XSA does not have an
828 # interface returning *just* a coderef, without installing it
829 # anywhere :(
830 Class::XSAccessor->import(
831 replace => 1,
832 class => '__CAG__XSA__BREEDER__',
e6bdecbd 833 $maker_templates->{$type}{cxsa_call} => {
de167379 834 $methname => $field,
835 },
836 );
837 __CAG__XSA__BREEDER__->can($methname);
838 }
839 else {
840 if (! $xsa_autodetected and ! $no_xsa_warned_classes->{$current_class}++) {
841 # not using Carp since the line where this happens doesn't mean much
842 warn 'Explicitly requested use of Class::XSAccessor disabled for objects of class '
843 . "'$current_class' inheriting from '$class' due to an overriden get_simple and/or "
844 . "set_simple\n";
845 }
846
847 do {
848 # that's faster than local
849 $USE_XS = 0;
850 my $c = $gen_accessor->($type, $class, 'simple', $field, $methname);
851 $USE_XS = 1;
852 $c;
853 };
854 }
855 };
856
857 # if after this shim was created someone wrapped it with an 'around',
858 # we can not blindly reinstall the method slot - we will destroy the
859 # wrapper. Silently chain execution further...
ad0ed975 860 if ( !$expected_cref or $expected_cref != ($current_class->can($methname)||0) ) {
de167379 861
862 # there is no point in re-determining it on every subsequent call,
863 # just store for future reference
864 $cached_implementation->{$current_class} ||= $resolved_implementation;
865
866 # older perls segfault if the cref behind the goto throws
867 # http://rt.perl.org/rt3/Public/Bug/Display.html?id=35878
3f6054c4 868 return $resolved_implementation->(@_) if __CAG_ENV__::BROKEN_GOTO;
de167379 869
870 goto $resolved_implementation;
871 }
872
3f6054c4 873 if (__CAG_ENV__::TRACK_UNDEFER_FAIL) {
2d392af1 874 my $deferred_calls_seen = do {
875 no strict 'refs';
876 \%{"${current_class}::__cag_deferred_xs_shim_invocations"}
877 };
e6f2993f 878 my @cframe = caller(0);
2d392af1 879 if (my $already_seen = $deferred_calls_seen->{$cframe[3]}) {
e6f2993f 880 Carp::carp (
881 "Deferred version of method $cframe[3] invoked more than once (originally "
2d392af1 882 . "invoked at $already_seen). This is a strong indication your code has "
883 . 'cached the original ->can derived method coderef, and is using it instead '
e6bdecbd 884 . 'of the proper method re-lookup, causing minor performance regressions'
e6f2993f 885 );
886 }
887 else {
2d392af1 888 $deferred_calls_seen->{$cframe[3]} = "$cframe[1] line $cframe[2]";
e6f2993f 889 }
890 }
891
de167379 892 # install the resolved implementation into the code slot so we do not
893 # come here anymore (hopefully)
894 # since XSAccessor was available - so is Sub::Name
895 {
e6f2993f 896 no strict 'refs';
897 no warnings 'redefine';
85ccab9a 898
f7cf6867 899 my $fq_name = "${current_class}::${methname}";
de167379 900 *$fq_name = Sub::Name::subname($fq_name, $resolved_implementation);
901
902 # need to update what the shim expects too *in case* its
903 # ->can was cached for some moronic reason
904 $expected_cref = $resolved_implementation;
905 Scalar::Util::weaken($expected_cref);
f7cf6867 906 }
85ccab9a 907
f7cf6867 908 # older perls segfault if the cref behind the goto throws
909 # http://rt.perl.org/rt3/Public/Bug/Display.html?id=35878
3f6054c4 910 return $resolved_implementation->(@_) if __CAG_ENV__::BROKEN_GOTO;
85ccab9a 911
de167379 912 goto $resolved_implementation;
f7cf6867 913 };
de167379 914
915 Scalar::Util::weaken($expected_cref); # to break the self-reference
916 $ret;
85ccab9a 917 }
918
919 # no Sub::Name - just install the coderefs directly (compiling every time)
3f6054c4 920 elsif (__CAG_ENV__::NO_SUBNAME) {
eda06cc6 921 my $src = $accessor_maker_cache->{source}{$type}{$group}{$field} ||=
e6bdecbd 922 $maker_templates->{$type}{pp_generator}->($group, $field);
eda06cc6 923
00e42e87 924 $cag_eval->(
925 "no warnings 'redefine'; sub ${class}::${methname} { $src }; 1",
926 );
eda06cc6 927
e6f2993f 928 undef; # so that no further attempt will be made to install anything
85ccab9a 929 }
930
931 # a coderef generator with a variable pad (returns a fresh cref on every invocation)
85ccab9a 932 else {
e6f2993f 933 ($accessor_maker_cache->{pp}{$type}{$group}{$field} ||= do {
eda06cc6 934 my $src = $accessor_maker_cache->{source}{$type}{$group}{$field} ||=
e6bdecbd 935 $maker_templates->{$type}{pp_generator}->($group, $field);
eda06cc6 936
00e42e87 937 $cag_eval->( "sub { my \$dummy; sub { \$dummy if 0; $src } }" );
85ccab9a 938 })->()
939 }
940};
941
9421;