Release 0.10014
[p5sagit/Class-Accessor-Grouped.git] / lib / Class / Accessor / Grouped.pm
1 package Class::Accessor::Grouped;
2 use strict;
3 use warnings;
4 use Carp ();
5 use Scalar::Util ();
6 use Module::Runtime ();
7
8 BEGIN {
9   # use M::R to work around the 5.8 require bugs
10   if ($] < 5.009_005) {
11     Module::Runtime::require_module('MRO::Compat');
12   }
13   else {
14     require mro;
15   }
16 }
17
18 our $VERSION = '0.10014';
19 $VERSION =~ tr/_//d; # numify for warning-free dev releases
20
21 # when changing minimum version don't forget to adjust Makefile.PL as well
22 our $__minimum_xsa_version;
23 BEGIN { $__minimum_xsa_version = '1.19' }
24
25 our $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}
29   unless defined $USE_XS;
30
31 BEGIN {
32   package # hide from PAUSE
33     __CAG_ENV__;
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
47   my $found_cxsa;
48   constant->import( NO_CXSA => ( NO_SUBNAME() || ( eval {
49     Module::Runtime::require_module('Class::XSAccessor');
50     $found_cxsa = Class::XSAccessor->VERSION;
51     Class::XSAccessor->VERSION($__minimum_xsa_version);
52   } ? 0 : "$@" ) ) );
53
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
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   sub perlstring ($) { q{"}. quotemeta( shift ). q{"} };
75 }
76
77 # Yes this method is undocumented
78 # Yes it should be a private coderef like all the rest at the end of this file
79 # No we can't do that (yet) because the DBIC-CDBI compat layer overrides it
80 # %$*@!?&!&#*$!!!
81
82 my $illegal_accessors_warned;
83 sub _mk_group_accessors {
84   my($self, $maker, $group, @fields) = @_;
85   my $class = length (ref ($self) ) ? ref ($self) : $self;
86
87   no strict 'refs';
88   no warnings 'redefine';
89
90   # So we don't have to do lots of lookups inside the loop.
91   $maker = $self->can($maker) unless ref $maker;
92
93   for (@fields) {
94
95     my ($name, $field) = (ref $_) ? (@$_) : ($_, $_);
96
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",
102           __CAG_ENV__::perlstring($name),
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
123         # because these accessors are unusable anyway) we provide an
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     }
136
137     Carp::carp("Having a data accessor named '$name' in '$class' is unwise.")
138       if $name =~ /\A(?: DESTROY | AUTOLOAD | CLONE )\z/x;
139
140     my $alias = "_${name}_accessor";
141
142     for ($name, $alias) {
143
144       # the maker may elect to not return anything, meaning it already
145       # installed the coderef for us (e.g. lack of Sub::Name)
146       my $cref = $self->$maker($group, $field, $_)
147         or next;
148
149       my $fq_meth = "${class}::$_";
150
151       *$fq_meth = Sub::Name::subname($fq_meth, $cref);
152         #unless defined &{$class."\:\:$field"}
153     }
154   }
155 };
156
157 # $gen_accessor coderef is setup at the end for clarity
158 my $gen_accessor;
159
160 =head1 NAME
161
162 Class::Accessor::Grouped - Lets you build groups of accessors
163
164 =head1 SYNOPSIS
165
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
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
179 =head1 DESCRIPTION
180
181 This class lets you build groups of accessors that will call different
182 getters and setters. The documentation of this module still requires a lot
183 of work (B<< volunteers welcome >.> >>), but in the meantime you can refer to
184 L<this post|http://lo-f.at/glahn/2009/08/WritingPowerfulAccessorsForPerlClasses.html>
185 for more information.
186
187 =head2 Notes on accessor names
188
189 In general method names in Perl are considered identifiers, and as such need to
190 conform to the identifier specification of C<qr/\A[A-Z_a-z][0-9A-Z_a-z]*\z/>.
191 While it is rather easy to invoke methods with non-standard names
192 (C<< $obj->${\"anything goes"} >>), it is not possible to properly declare such
193 methods without the use of L<Sub::Name>. Since this module must be able to
194 function identically with and without its optional dependencies, starting with
195 version C<0.10008> attempting to declare an accessor with a non-standard name
196 is a fatal error (such operations would silently succeed since version
197 C<0.08004>, as long as L<Sub::Name> is present, or otherwise would result in a
198 syntax error during a string eval).
199
200 Unfortunately in the years since C<0.08004> a rather large body of code
201 accumulated in the wild that does attempt to declare accessors with funny
202 names. One notable perpetrator is L<DBIx::Class::Schema::Loader>, which under
203 certain conditions could create accessors of the C<column> group which start
204 with numbers and/or some other punctuation (the proper way would be to declare
205 columns with the C<accessor> attribute set to C<undef>).
206
207 Therefore an escape mechanism is provided via the environment variable
208 C<CAG_ILLEGAL_ACCESSOR_NAME_OK>. When set to a true value, one warning is
209 issued B<per class> on attempts to declare an accessor with a non-conforming
210 name, and as long as L<Sub::Name> is available all accessors will be properly
211 created. Regardless of this setting, accessor names containing nulls C<"\0">
212 are disallowed, due to various deficiencies in perl itself.
213
214 If your code base has too many instances of illegal accessor declarations, and
215 a fix is not feasible due to time constraints, it is possible to disable the
216 warnings altogether by setting C<$ENV{CAG_ILLEGAL_ACCESSOR_NAME_OK}> to
217 C<DO_NOT_WARN> (observe capitalization).
218
219 =head1 METHODS
220
221 =head2 mk_group_accessors
222
223  __PACKAGE__->mk_group_accessors(simple => 'hair_length', [ hair_color => 'hc' ]);
224
225 =over 4
226
227 =item Arguments: $group, @fieldspec
228
229 Returns: none
230
231 =back
232
233 Creates a set of accessors in a given group.
234
235 $group is the name of the accessor group for the generated accessors; they
236 will call get_$group($field) on get and set_$group($field, $value) on set.
237
238 If you want to mimic Class::Accessor's mk_accessors $group has to be 'simple'
239 to tell Class::Accessor::Grouped to use its own get_simple and set_simple
240 methods.
241
242 @fieldspec is a list of field/accessor names; if a fieldspec is a scalar
243 this is used as both field and accessor name, if a listref it is expected to
244 be of the form [ $accessor, $field ].
245
246 =cut
247
248 sub mk_group_accessors {
249   my ($self, $group, @fields) = @_;
250
251   $self->_mk_group_accessors('make_group_accessor', $group, @fields);
252   return;
253 }
254
255 =head2 mk_group_ro_accessors
256
257  __PACKAGE__->mk_group_ro_accessors(simple => 'birthdate', [ social_security_number => 'ssn' ]);
258
259 =over 4
260
261 =item Arguments: $group, @fieldspec
262
263 Returns: none
264
265 =back
266
267 Creates a set of read only accessors in a given group. Identical to
268 L</mk_group_accessors> but accessors will throw an error if passed a value
269 rather than setting the value.
270
271 =cut
272
273 sub mk_group_ro_accessors {
274   my($self, $group, @fields) = @_;
275
276   $self->_mk_group_accessors('make_group_ro_accessor', $group, @fields);
277   return;
278 }
279
280 =head2 mk_group_wo_accessors
281
282  __PACKAGE__->mk_group_wo_accessors(simple => 'lie', [ subject => 'subj' ]);
283
284 =over 4
285
286 =item Arguments: $group, @fieldspec
287
288 Returns: none
289
290 =back
291
292 Creates a set of write only accessors in a given group. Identical to
293 L</mk_group_accessors> but accessors will throw an error if not passed a
294 value rather than getting the value.
295
296 =cut
297
298 sub mk_group_wo_accessors {
299   my($self, $group, @fields) = @_;
300
301   $self->_mk_group_accessors('make_group_wo_accessor', $group, @fields);
302   return;
303 }
304
305 =head2 get_simple
306
307 =over 4
308
309 =item Arguments: $field
310
311 Returns: $value
312
313 =back
314
315 Simple getter for hash-based objects which returns the value for the field
316 name passed as an argument.
317
318 =cut
319
320 sub get_simple {
321   $_[0]->{$_[1]};
322 }
323
324 =head2 set_simple
325
326 =over 4
327
328 =item Arguments: $field, $new_value
329
330 Returns: $new_value
331
332 =back
333
334 Simple setter for hash-based objects which sets and then returns the value
335 for the field name passed as an argument.
336
337 =cut
338
339 sub set_simple {
340   $_[0]->{$_[1]} = $_[2];
341 }
342
343
344 =head2 get_inherited
345
346 =over 4
347
348 =item Arguments: $field
349
350 Returns: $value
351
352 =back
353
354 Simple getter for Classes and hash-based objects which returns the value for
355 the field name passed as an argument. This behaves much like
356 L<Class::Data::Accessor> where the field can be set in a base class,
357 inherited and changed in subclasses, and inherited and changed for object
358 instances.
359
360 =cut
361
362 sub get_inherited {
363   if ( length (ref ($_[0]) ) ) {
364     if (Scalar::Util::reftype $_[0] eq 'HASH') {
365       return $_[0]->{$_[1]} if exists $_[0]->{$_[1]};
366       # everything in @_ is aliased, an assignment won't work
367       splice @_, 0, 1, ref($_[0]);
368     }
369     else {
370       Carp::croak('Cannot get inherited value on an object instance that is not hash-based');
371     }
372   }
373
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)
377   no strict 'refs';
378   no warnings 'uninitialized';
379
380   my $cag_slot = '::__cag_'. $_[1];
381   return ${$_[0].$cag_slot} if defined(${$_[0].$cag_slot});
382
383   do { return ${$_.$cag_slot} if defined(${$_.$cag_slot}) }
384     for $_[0]->get_super_paths;
385
386   return undef;
387 }
388
389 =head2 set_inherited
390
391 =over 4
392
393 =item Arguments: $field, $new_value
394
395 Returns: $new_value
396
397 =back
398
399 Simple setter for Classes and hash-based objects which sets and then returns
400 the value for the field name passed as an argument. When called on a hash-based
401 object it will set the appropriate hash key value. When called on a class, it
402 will set a class level variable.
403
404 B<Note:>: This method will die if you try to set an object variable on a non
405 hash-based object.
406
407 =cut
408
409 sub set_inherited {
410   if (length (ref ($_[0]) ) ) {
411     if (Scalar::Util::reftype $_[0] eq 'HASH') {
412       return $_[0]->{$_[1]} = $_[2];
413     } else {
414       Carp::croak('Cannot set inherited value on an object instance that is not hash-based');
415     };
416   }
417
418   no strict 'refs';
419   ${$_[0].'::__cag_'.$_[1]} = $_[2];
420 }
421
422 =head2 get_component_class
423
424 =over 4
425
426 =item Arguments: $field
427
428 Returns: $value
429
430 =back
431
432 Gets the value of the specified component class.
433
434  __PACKAGE__->mk_group_accessors('component_class' => 'result_class');
435
436  $self->result_class->method();
437
438  ## same as
439  $self->get_component_class('result_class')->method();
440
441 =cut
442
443 sub get_component_class {
444   $_[0]->get_inherited($_[1]);
445 };
446
447 =head2 set_component_class
448
449 =over 4
450
451 =item Arguments: $field, $class
452
453 Returns: $new_value
454
455 =back
456
457 Inherited accessor that automatically loads the specified class before setting
458 it. This method will die if the specified class could not be loaded.
459
460  __PACKAGE__->mk_group_accessors('component_class' => 'result_class');
461  __PACKAGE__->result_class('MyClass');
462
463  $self->result_class->method();
464
465 =cut
466
467 sub set_component_class {
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;
481
482     }
483     else {
484       eval { Module::Runtime::use_package_optimistically($_[2]) }
485         or Carp::croak("Could not load $_[1] '$_[2]': $@");
486     }
487   };
488
489   $_[0]->set_inherited($_[1], $_[2]);
490 };
491
492 =head1 INTERNAL METHODS
493
494 These methods are documented for clarity, but are never meant to be called
495 directly, and are not really meant for overriding either.
496
497 =head2 get_super_paths
498
499 Returns a list of 'parent' or 'super' class names that the current class
500 inherited from. This is what drives the traversal done by L</get_inherited>.
501
502 =cut
503
504 sub get_super_paths {
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   @_;
509 };
510
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
520 Returns: \&accessor_coderef ?
521
522 =back
523
524 Called by mk_group_accessors for each entry in @fieldspec. Either returns
525 a coderef which will be installed at C<&__PACKAGE__::$accessor>, or returns
526 C<undef> if it elects to install the coderef on its own.
527
528 =cut
529
530 sub 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
541 Returns: \&accessor_coderef ?
542
543 =back
544
545 Called by mk_group_ro_accessors for each entry in @fieldspec. Either returns
546 a coderef which will be installed at C<&__PACKAGE__::$accessor>, or returns
547 C<undef> if it elects to install the coderef on its own.
548
549 =cut
550
551 sub 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
562 Returns: \&accessor_coderef ?
563
564 =back
565
566 Called by mk_group_wo_accessors for each entry in @fieldspec. Either returns
567 a coderef which will be installed at C<&__PACKAGE__::$accessor>, or returns
568 C<undef> if it elects to install the coderef on its own.
569
570 =cut
571
572 sub make_group_wo_accessor { $gen_accessor->('wo', @_) }
573
574
575 =head1 PERFORMANCE
576
577 To provide total flexibility L<Class::Accessor::Grouped> calls methods
578 internally while performing get/set actions, which makes it noticeably
579 slower than similar modules. To compensate, this module will automatically
580 use the insanely fast L<Class::XSAccessor> to generate the C<simple>-group
581 accessors if this module is available on your system.
582
583 =head2 Benchmark
584
585 This is the benchmark of 200 get/get/set/get/set cycles on perl 5.16.2 with
586 thread support, showcasing how this modules L<simple (CAG_S)|/get_simple>,
587 L<inherited (CAG_INH)|/get_inherited> and L<inherited with parent-class data
588 (CAG_INHP)|/get_inherited> accessors stack up against most popular accessor 
589 builders:  L<Moose>, L<Moo>, L<Mo>, L<Mouse> (both pure-perl and XS variant),
590 L<Object::Tiny::RW (OTRW)|Object::Tiny::RW>,
591 L<Class::Accessor (CA)|Class::Accessor>,
592 L<Class::Accessor::Lite (CAL)|Class::Accessor::Lite>,
593 L<Class::Accessor::Fast (CAF)|Class::Accessor::Fast>,
594 L<Class::Accessor::Fast::XS (CAF_XS)|Class::Accessor::Fast::XS>
595 and 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
629 Benchmarking program is available in the root of the
630 L<repository|http://search.cpan.org/dist/Class-Accessor-Grouped/>:
631
632 =head2 Notes on Class::XSAccessor
633
634 You can force (or disable) the use of L<Class::XSAccessor> before creating a
635 particular C<simple> accessor by either manipulating the global variable
636 C<$Class::Accessor::Grouped::USE_XS> to true or false (preferably with
637 L<localization|perlfunc/local>, or you can do so before runtime via the
638 C<CAG_USE_XS> environment variable.
639
640 Since L<Class::XSAccessor> has no knowledge of L</get_simple> and
641 L</set_simple> this module does its best to detect if you are overriding
642 one of these methods and will fall back to using the perl version of the
643 accessor in order to maintain consistency. However be aware that if you
644 enable use of C<Class::XSAccessor> (automatically or explicitly), create
645 an object, invoke a simple accessor on that object, and B<then> manipulate
646 the symbol table to install a C<get/set_simple> override - you get to keep
647 all the pieces.
648
649 =head1 AUTHORS
650
651 Matt S. Trout <mst@shadowcatsystems.co.uk>
652
653 Christopher H. Laco <claco@chrislaco.com>
654
655 =head1 CONTRIBUTORS
656
657 Caelum: Rafael Kitover <rkitover@cpan.org>
658
659 frew: Arthur Axel "fREW" Schmidt <frioux@gmail.com>
660
661 groditi: Guillermo Roditi <groditi@cpan.org>
662
663 Jason Plum <jason.plum@bmmsi.com>
664
665 ribasushi: Peter Rabbitson <ribasushi@cpan.org>
666
667
668 =head1 COPYRIGHT & LICENSE
669
670 Copyright (c) 2006-2010 Matt S. Trout <mst@shadowcatsystems.co.uk>
671
672 This program is free software; you can redistribute it and/or modify
673 it under the same terms as perl itself.
674
675 =cut
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
687 # Autodetect unless flag supplied
688 my $xsa_autodetected;
689 if (! defined $USE_XS) {
690   $USE_XS = __CAG_ENV__::NO_CXSA ? 0 : 1;
691   $xsa_autodetected++;
692 }
693
694
695 my $maker_templates = {
696   rw => {
697     cxsa_call => 'accessors',
698     pp_generator => sub {
699       # my ($group, $fieldname) = @_;
700       my $quoted_fieldname = __CAG_ENV__::perlstring($_[1]);
701       sprintf <<'EOS', ($_[0], $quoted_fieldname) x 2;
702
703 @_ > 1
704   ? shift->set_%s(%s, @_)
705   : shift->get_%s(%s)
706 EOS
707
708     },
709   },
710   ro => {
711     cxsa_call => 'getters',
712     pp_generator => sub {
713       # my ($group, $fieldname) = @_;
714       my $quoted_fieldname = __CAG_ENV__::perlstring($_[1]);
715       sprintf  <<'EOS', $_[0], $quoted_fieldname;
716
717 @_ > 1
718   ? do {
719     my ($meth) = (caller(0))[3] =~ /([^\:]+)$/;
720     my $class = length( ref($_[0]) ) ? ref($_[0]) : $_[0];
721     Carp::croak(
722       "'$meth' cannot alter its value (read-only attribute of class $class)"
723     );
724   }
725   : shift->get_%s(%s)
726 EOS
727
728     },
729   },
730   wo => {
731     cxsa_call => 'setters',
732     pp_generator => sub {
733       # my ($group, $fieldname) = @_;
734       my $quoted_fieldname = __CAG_ENV__::perlstring($_[1]);
735       sprintf  <<'EOS', $_[0], $quoted_fieldname;
736
737 @_ > 1
738   ? shift->set_%s(%s, @_)
739   : do {
740     my ($meth) = (caller(0))[3] =~ /([^\:]+)$/;
741     my $class = length( ref($_[0]) ) ? ref($_[0]) : $_[0];
742     Carp::croak(
743       "'$meth' cannot access its value (write-only attribute of class $class)"
744     );
745   }
746 EOS
747
748     },
749   },
750 };
751
752 my $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 };
775
776 my ($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
780 my $original_simple_getter = __PACKAGE__->can ('get_simple');
781 my $original_simple_setter = __PACKAGE__->can ('set_simple');
782
783 my ($resolved_methods, $cag_produced_crefs);
784
785 sub CLONE {
786   my @crefs = grep { defined $_ } values %{$cag_produced_crefs||{}};
787   $cag_produced_crefs = @crefs
788     ? { map { $_ => $_ } @crefs }
789     : undef
790   ;
791 }
792
793 # Note!!! Unusual signature
794 $gen_accessor = sub {
795   my ($type, $class, $group, $field, $methname) = @_;
796   $class = ref $class if length ref $class;
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
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
807   if ($USE_XS and $group eq 'simple') {
808     die sprintf( "Class::XSAccessor requested but not available:\n%s\n", __CAG_ENV__::NO_CXSA )
809       if __CAG_ENV__::NO_CXSA;
810
811     my $ret = sub {
812       my $current_class = length (ref ($_[0] ) ) ? ref ($_[0]) : $_[0];
813
814       my $resolved_implementation = $resolved_methods->{$current_class}{$methname} ||= do {
815         if (
816           ($current_class->can('get_simple')||0) == $original_simple_getter
817             &&
818           ($current_class->can('set_simple')||0) == $original_simple_setter
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__',
833             $maker_templates->{$type}{cxsa_call} => {
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...
860       if ( ! $cag_produced_crefs->{ $current_class->can($methname) || 0 } ) {
861
862         # older perls segfault if the cref behind the goto throws
863         # http://rt.perl.org/rt3/Public/Bug/Display.html?id=35878
864         return $resolved_implementation->(@_) if __CAG_ENV__::BROKEN_GOTO;
865
866         goto $resolved_implementation;
867       }
868
869
870       if (__CAG_ENV__::TRACK_UNDEFER_FAIL) {
871         my $deferred_calls_seen = do {
872           no strict 'refs';
873           \%{"${current_class}::__cag_deferred_xs_shim_invocations"}
874         };
875         my @cframe = caller(0);
876
877         if (my $already_seen = $deferred_calls_seen->{$cframe[3]}) {
878           Carp::carp (
879             "Deferred version of method $cframe[3] invoked more than once (originally "
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 '
882           . 'of the proper method re-lookup, causing minor performance regressions'
883           );
884         }
885         else {
886           $deferred_calls_seen->{$cframe[3]} = "$cframe[1] line $cframe[2]";
887         }
888       }
889
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       {
894         no strict 'refs';
895         no warnings 'redefine';
896
897         my $fq_name = "${current_class}::${methname}";
898         *$fq_name = Sub::Name::subname($fq_name, $resolved_implementation);
899       }
900
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
909       # older perls segfault if the cref behind the goto throws
910       # http://rt.perl.org/rt3/Public/Bug/Display.html?id=35878
911       return $resolved_implementation->(@_) if __CAG_ENV__::BROKEN_GOTO;
912
913       goto $resolved_implementation;
914     };
915
916     Scalar::Util::weaken($cag_produced_crefs->{$ret} = $ret);
917
918     $ret; # returning shim
919   }
920
921   # no Sub::Name - just install the coderefs directly (compiling every time)
922   elsif (__CAG_ENV__::NO_SUBNAME) {
923     my $src = $accessor_maker_cache->{source}{$type}{$group}{$field} ||=
924       $maker_templates->{$type}{pp_generator}->($group, $field);
925
926     $cag_eval->(
927       "no warnings 'redefine'; sub ${class}::${methname} { $src }; 1",
928     );
929
930     undef;  # so that no further attempt will be made to install anything
931   }
932
933   # a coderef generator with a variable pad (returns a fresh cref on every invocation)
934   else {
935     ($accessor_maker_cache->{pp}{$type}{$group}{$field} ||= do {
936       my $src = $accessor_maker_cache->{source}{$type}{$group}{$field} ||=
937         $maker_templates->{$type}{pp_generator}->($group, $field);
938
939       $cag_eval->( "sub { my \$dummy; sub { \$dummy if 0; $src } }" );
940     })->()
941   }
942 };
943
944 1;