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