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