1 package Class::Accessor::Grouped;
16 our $VERSION = '0.10002';
17 $VERSION = eval $VERSION if $VERSION =~ /_/; # numify for warning-free dev releases
19 # when changing minimum version don't forget to adjust L</PERFORMANCE> and
20 # the Makefile.PL as well
21 our $__minimum_xsa_version;
23 $__minimum_xsa_version = '1.11';
27 # the unless defined is here so that we can override the value
28 # before require/use, *regardless* of the state of $ENV{CAG_USE_XS}
29 $USE_XS = $ENV{CAG_USE_XS}
30 unless defined $USE_XS;
32 # Yes this method is undocumented
33 # Yes it should be a private coderef like all the rest at the end of this file
34 # No we can't do that (yet) because the DBIC-CDBI compat layer overrides it
36 sub _mk_group_accessors {
37 my($self, $maker, $group, @fields) = @_;
38 my $class = Scalar::Util::blessed $self || $self;
41 no warnings 'redefine';
43 # So we don't have to do lots of lookups inside the loop.
44 $maker = $self->can($maker) unless ref $maker;
47 if( $_ eq 'DESTROY' ) {
48 Carp::carp("Having a data accessor named DESTROY in ".
49 "'$class' is unwise.");
52 my ($name, $field) = (ref $_)
57 my $alias = "_${name}_accessor";
59 for my $meth ($name, $alias) {
61 # the maker may elect to not return anything, meaning it already
62 # installed the coderef for us (e.g. lack of Sub::Name)
63 my $cref = $self->$maker($group, $field, $meth)
66 my $fq_meth = "${class}::${meth}";
68 *$fq_meth = Sub::Name::subname($fq_meth, $cref);
69 #unless defined &{$class."\:\:$field"}
74 # coderef is setup at the end for clarity
79 Class::Accessor::Grouped - Lets you build groups of accessors
85 This class lets you build groups of accessors that will call different
90 =head2 mk_group_accessors
94 =item Arguments: $group, @fieldspec
100 Creates a set of accessors in a given group.
102 $group is the name of the accessor group for the generated accessors; they
103 will call get_$group($field) on get and set_$group($field, $value) on set.
105 If you want to mimic Class::Accessor's mk_accessors $group has to be 'simple'
106 to tell Class::Accessor::Grouped to use its own get_simple and set_simple
109 @fieldspec is a list of field/accessor names; if a fieldspec is a scalar
110 this is used as both field and accessor name, if a listref it is expected to
111 be of the form [ $accessor, $field ].
115 sub mk_group_accessors {
116 my ($self, $group, @fields) = @_;
118 $self->_mk_group_accessors('make_group_accessor', $group, @fields);
122 =head2 mk_group_ro_accessors
126 =item Arguments: $group, @fieldspec
132 Creates a set of read only accessors in a given group. Identical to
133 L</mk_group_accessors> but accessors will throw an error if passed a value
134 rather than setting the value.
138 sub mk_group_ro_accessors {
139 my($self, $group, @fields) = @_;
141 $self->_mk_group_accessors('make_group_ro_accessor', $group, @fields);
144 =head2 mk_group_wo_accessors
148 =item Arguments: $group, @fieldspec
154 Creates a set of write only accessors in a given group. Identical to
155 L</mk_group_accessors> but accessors will throw an error if not passed a
156 value rather than getting the value.
160 sub mk_group_wo_accessors {
161 my($self, $group, @fields) = @_;
163 $self->_mk_group_accessors('make_group_wo_accessor', $group, @fields);
166 =head2 make_group_accessor
170 =item Arguments: $group, $field, $method
172 Returns: \&accessor_coderef ?
176 Called by mk_group_accessors for each entry in @fieldspec. Either returns
177 a coderef which will be installed at C<&__PACKAGE__::$method>, or returns
178 C<undef> if it elects to install the coderef on its own.
182 sub make_group_accessor { $gen_accessor->('rw', @_) }
184 =head2 make_group_ro_accessor
188 =item Arguments: $group, $field, $method
190 Returns: \&accessor_coderef ?
194 Called by mk_group_ro_accessors for each entry in @fieldspec. Either returns
195 a coderef which will be installed at C<&__PACKAGE__::$method>, or returns
196 C<undef> if it elects to install the coderef on its own.
200 sub make_group_ro_accessor { $gen_accessor->('ro', @_) }
202 =head2 make_group_wo_accessor
206 =item Arguments: $group, $field, $method
208 Returns: \&accessor_coderef ?
212 Called by mk_group_wo_accessors for each entry in @fieldspec. Either returns
213 a coderef which will be installed at C<&__PACKAGE__::$method>, or returns
214 C<undef> if it elects to install the coderef on its own.
218 sub make_group_wo_accessor { $gen_accessor->('wo', @_) }
224 =item Arguments: $field
230 Simple getter for hash-based objects which returns the value for the field
231 name passed as an argument.
236 return $_[0]->{$_[1]};
243 =item Arguments: $field, $new_value
249 Simple setter for hash-based objects which sets and then returns the value
250 for the field name passed as an argument.
255 return $_[0]->{$_[1]} = $_[2];
263 =item Arguments: $field
269 Simple getter for Classes and hash-based objects which returns the value for
270 the field name passed as an argument. This behaves much like
271 L<Class::Data::Accessor> where the field can be set in a base class,
272 inherited and changed in subclasses, and inherited and changed for object
280 if ( defined( $class = Scalar::Util::blessed $_[0] ) ) {
281 if (Scalar::Util::reftype $_[0] eq 'HASH') {
282 return $_[0]->{$_[1]} if exists $_[0]->{$_[1]};
285 Carp::croak('Cannot get inherited value on an object instance that is not hash-based');
293 no warnings 'uninitialized';
295 my $cag_slot = '::__cag_'. $_[1];
296 return ${$class.$cag_slot} if defined(${$class.$cag_slot});
298 # we need to be smarter about recalculation, as @ISA (thus supers) can very well change in-flight
299 my $cur_gen = mro::get_pkg_gen ($class);
300 if ( $cur_gen != ${$class.'::__cag_pkg_gen__'} ) {
301 @{$class.'::__cag_supers__'} = $_[0]->get_super_paths;
302 ${$class.'::__cag_pkg_gen__'} = $cur_gen;
305 for (@{$class.'::__cag_supers__'}) {
306 return ${$_.$cag_slot} if defined(${$_.$cag_slot});
316 =item Arguments: $field, $new_value
322 Simple setter for Classes and hash-based objects which sets and then returns
323 the value for the field name passed as an argument. When called on a hash-based
324 object it will set the appropriate hash key value. When called on a class, it
325 will set a class level variable.
327 B<Note:>: This method will die if you try to set an object variable on a non
333 if (defined Scalar::Util::blessed $_[0]) {
334 if (Scalar::Util::reftype $_[0] eq 'HASH') {
335 return $_[0]->{$_[1]} = $_[2];
337 Carp::croak('Cannot set inherited value on an object instance that is not hash-based');
342 return ${$_[0].'::__cag_'.$_[1]} = $_[2];
346 =head2 get_component_class
350 =item Arguments: $field
356 Gets the value of the specified component class.
358 __PACKAGE__->mk_group_accessors('component_class' => 'result_class');
360 $self->result_class->method();
363 $self->get_component_class('result_class')->method();
367 sub get_component_class {
368 return $_[0]->get_inherited($_[1]);
371 =head2 set_component_class
375 =item Arguments: $field, $class
381 Inherited accessor that automatically loads the specified class before setting
382 it. This method will die if the specified class could not be loaded.
384 __PACKAGE__->mk_group_accessors('component_class' => 'result_class');
385 __PACKAGE__->result_class('MyClass');
387 $self->result_class->method();
391 sub set_component_class {
394 require Class::Inspector;
395 if (Class::Inspector->installed($_[2]) && !Class::Inspector->loaded($_[2])) {
396 eval "require $_[2]";
398 Carp::croak("Could not load $_[1] '$_[2]': ", $@) if $@;
402 return $_[0]->set_inherited($_[1], $_[2]);
405 =head2 get_super_paths
407 Returns a list of 'parent' or 'super' class names that the current class inherited from.
411 sub get_super_paths {
412 return @{mro::get_linear_isa( ref($_[0]) || $_[0] )};
417 To provide total flexibility L<Class::Accessor::Grouped> calls methods
418 internally while performing get/set actions, which makes it noticeably
419 slower than similar modules. To compensate, this module will automatically
420 use the insanely fast L<Class::XSAccessor> to generate the C<simple>-group
421 accessors if this module is available on your system.
425 This is the result of a set/get/set loop benchmark on perl 5.12.1 with
426 thread support, showcasing most popular accessor builders: L<Moose>, L<Mouse>,
427 L<Moo>, L<CAF|Class::Accessor::Fast>, L<CAF_XS|Class::Accessor::Fast::XS>,
428 L<XSA|Class::XSAccessor>, and L<CAF_XSA|Class::XSAccessor::Compat>:
430 Rate CAG moOse CAF moUse moo HANDMADE CAF_XS moUse_XS moo_XS CAF_XSA XSA CAG_XS
431 CAG 169/s -- -21% -24% -32% -32% -34% -59% -63% -67% -67% -67% -67%
432 moOse 215/s 27% -- -3% -13% -13% -15% -48% -53% -58% -58% -58% -58%
433 CAF 222/s 31% 3% -- -10% -10% -13% -46% -52% -57% -57% -57% -57%
434 moUse 248/s 46% 15% 11% -- -0% -3% -40% -46% -52% -52% -52% -52%
435 moo 248/s 46% 15% 11% 0% -- -3% -40% -46% -52% -52% -52% -52%
436 HANDMADE 255/s 50% 18% 14% 3% 3% -- -38% -45% -50% -51% -51% -51%
437 CAF_XS 411/s 143% 91% 85% 66% 66% 61% -- -11% -20% -20% -21% -21%
438 moUse_XS 461/s 172% 114% 107% 86% 86% 81% 12% -- -10% -11% -11% -11%
439 moo_XS 514/s 204% 139% 131% 107% 107% 102% 25% 12% -- -0% -1% -1%
440 CAF_XSA 516/s 205% 140% 132% 108% 108% 103% 26% 12% 0% -- -0% -0%
441 XSA 519/s 206% 141% 133% 109% 109% 104% 26% 13% 1% 0% -- -0%
442 CAG_XS 519/s 206% 141% 133% 109% 109% 104% 26% 13% 1% 0% 0% --
444 Benchmark program is available in the root of the
445 L<repository|http://search.cpan.org/dist/Class-Accessor-Grouped/>:
447 =head2 Notes on Class::XSAccessor
449 You can force (or disable) the use of L<Class::XSAccessor> before creating a
450 particular C<simple> accessor by either manipulating the global variable
451 C<$Class::Accessor::Grouped::USE_XS> to true or false (preferably with
452 L<localization|perlfunc/local>, or you can do so before runtime via the
453 C<CAG_USE_XS> environment variable.
455 Since L<Class::XSAccessor> has no knowledge of L</get_simple> and
456 L</set_simple> this module does its best to detect if you are overriding
457 one of these methods and will fall back to using the perl version of the
458 accessor in order to maintain consistency. However be aware that if you
459 enable use of C<Class::XSAccessor> (automatically or explicitly), create
460 an object, invoke a simple accessor on that object, and B<then> manipulate
461 the symbol table to install a C<get/set_simple> override - you get to keep
466 Matt S. Trout <mst@shadowcatsystems.co.uk>
468 Christopher H. Laco <claco@chrislaco.com>
472 Caelum: Rafael Kitover <rkitover@cpan.org>
474 groditi: Guillermo Roditi <groditi@cpan.org>
476 Jason Plum <jason.plum@bmmsi.com>
478 ribasushi: Peter Rabbitson <ribasushi@cpan.org>
481 =head1 COPYRIGHT & LICENSE
483 Copyright (c) 2006-2010 Matt S. Trout <mst@shadowcatsystems.co.uk>
485 This program is free software; you can redistribute it and/or modify
486 it under the same terms as perl itself.
490 ########################################################################
491 ########################################################################
492 ########################################################################
494 # Here be many angry dragons
495 # (all code is in private coderefs since everything inherits CAG)
497 ########################################################################
498 ########################################################################
502 die "Huh?! No minimum C::XSA version?!\n"
503 unless $__minimum_xsa_version;
509 $err = eval { require Sub::Name; 1; } ? undef : do {
510 delete $INC{'Sub/Name.pm'}; # because older perls suck
513 *__CAG_NO_SUBNAME = $err
520 require Class::XSAccessor;
521 Class::XSAccessor->VERSION($__minimum_xsa_version);
525 delete $INC{'Sub/Name.pm'}; # because older perls suck
526 delete $INC{'Class/XSAccessor.pm'};
529 *__CAG_NO_CXSA = $err
535 *__CAG_BROKEN_GOTO = ($] < '5.008009')
541 *__CAG_UNSTABLE_DOLLARAT = ($] < '5.013002')
547 *__CAG_TRACK_UNDEFER_FAIL = (
548 $INC{'Test/Builder.pm'} || $INC{'Test/Builder2.pm'}
550 $0 =~ m|^ x?t / .+ \.t $|x
556 # Autodetect unless flag supplied
557 my $xsa_autodetected;
558 if (! defined $USE_XS) {
559 $USE_XS = __CAG_NO_CXSA ? 0 : 1;
563 my $maker_templates = {
565 xs_call => 'accessors',
567 my $set = "set_$_[0]";
568 my $get = "get_$_[0]";
574 ? shift->$set('$field', \@_)
575 : shift->$get('$field')
580 xs_call => 'getters',
582 my $get = "get_$_[0]";
588 ? shift->$get('$field')
590 my \$caller = caller;
591 my \$class = ref \$_[0] || \$_[0];
592 Carp::croak(\"'\$caller' cannot alter the value of '$field' \".
593 \"(read-only attributes of class '\$class')\");
599 xs_call => 'setters',
601 my $set = "set_$_[0]";
607 ? shift->$set('$field', \@_)
609 my \$caller = caller;
610 my \$class = ref \$_[0] || \$_[0];
611 Carp::croak(\"'\$caller' cannot access the value of '$field' \".
612 \"(write-only attributes of class '\$class')\");
620 my ($accessor_maker_cache, $no_xsa_warned_classes);
622 # can't use pkg_gen to track this stuff, as it doesn't
623 # detect superclass mucking
624 my $original_simple_getter = __PACKAGE__->can ('get_simple');
625 my $original_simple_setter = __PACKAGE__->can ('set_simple');
627 # Note!!! Unusual signature
628 $gen_accessor = sub {
629 my ($type, $class, $group, $field, $methname) = @_;
630 if (my $c = Scalar::Util::blessed( $class )) {
634 # When installing an XSA simple accessor, we need to make sure we are not
635 # short-circuiting a (compile or runtime) get_simple/set_simple override.
636 # What we do here is install a lazy first-access check, which will decide
637 # the ultimate coderef being placed in the accessor slot
639 # Also note that the *original* class will always retain this shim, as
640 # different branches inheriting from it may have different overrides.
641 # Thus the final method (properly labeled and all) is installed in the
642 # calling-package's namespace
643 if ($USE_XS and $group eq 'simple') {
644 die sprintf( "Class::XSAccessor requested but not available:\n%s\n", __CAG_NO_CXSA )
647 my ($expected_cref, $cached_implementation);
648 my $ret = $expected_cref = sub {
649 my $current_class = Scalar::Util::blessed( $_[0] ) || $_[0];
651 # $cached_implementation will be set only if the shim got
652 # 'around'ed, in which case it is handy to avoid re-running
653 # this block over and over again
654 my $resolved_implementation = $cached_implementation->{$current_class} || do {
656 $current_class->can('get_simple') == $original_simple_getter
658 $current_class->can('set_simple') == $original_simple_setter
660 # nothing has changed, might as well use the XS crefs
662 # note that by the time this code executes, we already have
663 # *objects* (since XSA works on 'simple' only by definition).
664 # If someone is mucking with the symbol table *after* there
665 # are some objects already - look! many, shiny pieces! :)
667 # The weird breeder thingy is because XSA does not have an
668 # interface returning *just* a coderef, without installing it
670 Class::XSAccessor->import(
672 class => '__CAG__XSA__BREEDER__',
673 $maker_templates->{$type}{xs_call} => {
677 __CAG__XSA__BREEDER__->can($methname);
680 if (! $xsa_autodetected and ! $no_xsa_warned_classes->{$current_class}++) {
681 # not using Carp since the line where this happens doesn't mean much
682 warn 'Explicitly requested use of Class::XSAccessor disabled for objects of class '
683 . "'$current_class' inheriting from '$class' due to an overriden get_simple and/or "
688 # that's faster than local
690 my $c = $gen_accessor->($type, $class, 'simple', $field, $methname);
697 # if after this shim was created someone wrapped it with an 'around',
698 # we can not blindly reinstall the method slot - we will destroy the
699 # wrapper. Silently chain execution further...
700 if ( !$expected_cref or $expected_cref != $current_class->can($methname) ) {
702 # there is no point in re-determining it on every subsequent call,
703 # just store for future reference
704 $cached_implementation->{$current_class} ||= $resolved_implementation;
706 # older perls segfault if the cref behind the goto throws
707 # http://rt.perl.org/rt3/Public/Bug/Display.html?id=35878
708 return $resolved_implementation->(@_) if __CAG_BROKEN_GOTO;
710 goto $resolved_implementation;
713 if (__CAG_TRACK_UNDEFER_FAIL) {
714 my $deferred_calls_seen = do {
716 \%{"${current_class}::__cag_deferred_xs_shim_invocations"}
718 my @cframe = caller(0);
719 if (my $already_seen = $deferred_calls_seen->{$cframe[3]}) {
721 "Deferred version of method $cframe[3] invoked more than once (originally "
722 . "invoked at $already_seen). This is a strong indication your code has "
723 . 'cached the original ->can derived method coderef, and is using it instead '
724 . 'of the proper method re-lookup, causing performance regressions'
728 $deferred_calls_seen->{$cframe[3]} = "$cframe[1] line $cframe[2]";
732 # install the resolved implementation into the code slot so we do not
733 # come here anymore (hopefully)
734 # since XSAccessor was available - so is Sub::Name
737 no warnings 'redefine';
739 my $fq_name = "${current_class}::${methname}";
740 *$fq_name = Sub::Name::subname($fq_name, $resolved_implementation);
742 # need to update what the shim expects too *in case* its
743 # ->can was cached for some moronic reason
744 $expected_cref = $resolved_implementation;
745 Scalar::Util::weaken($expected_cref);
748 # older perls segfault if the cref behind the goto throws
749 # http://rt.perl.org/rt3/Public/Bug/Display.html?id=35878
750 return $resolved_implementation->(@_) if __CAG_BROKEN_GOTO;
752 goto $resolved_implementation;
755 Scalar::Util::weaken($expected_cref); # to break the self-reference
759 # no Sub::Name - just install the coderefs directly (compiling every time)
760 elsif (__CAG_NO_SUBNAME) {
761 my $src = $accessor_maker_cache->{source}{$type}{$group}{$field} ||=
762 $maker_templates->{$type}{pp_code}->($group, $field);
764 no warnings 'redefine';
765 local $@ if __CAG_UNSTABLE_DOLLARAT;
766 eval "sub ${class}::${methname} { $src }";
768 undef; # so that no further attempt will be made to install anything
771 # a coderef generator with a variable pad (returns a fresh cref on every invocation)
773 ($accessor_maker_cache->{pp}{$type}{$group}{$field} ||= do {
774 my $src = $accessor_maker_cache->{source}{$type}{$group}{$field} ||=
775 $maker_templates->{$type}{pp_code}->($group, $field);
777 local $@ if __CAG_UNSTABLE_DOLLARAT;
778 eval "sub { my \$dummy; sub { \$dummy if 0; $src } }" or die $@;