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