Subject: | apply :init_arg and :default before BUILD |
Currently, it is not possible to know the values of ATTRS which are being set via :init_arg or :default during the BUILD phase. This makes it impossible to create derivative values within BUILD based on these ATTRS.
The solution is to move the INIT loop before the BUILD loop in Class::Std, while continuing to defer the check for missing init_args until after BUILD.
Test case below, patch attached.
--
Marvin Humphrey
#--------------------------------------------------------------------
#!/usr/bin/perl
# no_deriv.plx -- demonstrate inability to derive values during BUILD
package NoDeriv;
use strict;
use warnings;
use Class::Std;
my %basename_of :ATTR(:init_arg<basename> :default<'index'>);
my %filename_of :ATTR;
sub BUILD {
my ($self, $ident, $arg_ref) = @_;
# Fails because we don't know $filename_of{$ident} yet.
$filename_of{$ident} = "$basename_of{$ident}.html";
}
my $foo = NoDeriv->new();
--- lib/Class/Std.old 2005-08-08 10:43:57.000000000 -0700
+++ lib/Class/Std.pm 2005-08-08 10:01:46.000000000 -0700
@@ -375,11 +375,6 @@
BUILD: for my $base_class (_reverse_hierarchy_of($class)) {
my %arg_set = ( %{$arg_ref}, %{$arg_ref->{$base_class}||{}} );
- # Apply BUILD() methods...
- if (my $build_ref = *{$base_class.'::BUILD'}{CODE}) {
- $build_ref->($new_obj, $new_obj_id, \%arg_set);
- }
-
# Apply init_arg and default for attributes still undefined...
INIT:
for my $attr_ref ( @{$attribute{$base_class}} ) {
@@ -403,6 +398,17 @@
next INIT;
}
+ }
+
+ # Apply BUILD() methods...
+ if (my $build_ref = *{$base_class.'::BUILD'}{CODE}) {
+ $build_ref->($new_obj, $new_obj_id, \%arg_set);
+ }
+
+ # Detect missing required attrs...
+ CHECK_MISSING:
+ for my $attr_ref ( @{$attribute{$base_class}} ) {
+ next CHECK_MISSING if defined $attr_ref->{ref}{$new_obj_id};
if (defined $attr_ref->{init_arg}) {
# Record missing init_arg...
push @missing_inits,