in reply to Wrong Error Message When Testing for Failure of Constructor

I think the problem is that you are taking a reference to the %default_values hash, instead of making a copy of it. Try this:

my $self = ref($class) ? bless( +{ %default_values }, ref($class) +) : bless( +{ %default_values }, $class );
In general,
+{ %hash }
makes a (shallow) copy of %hash and returns a reference to the copy. (The leading + is not strictly necessary, but there are situations in which perl misconstrues the curlies as a block instead of an anonymous hash(ref) constructor, so I've gotten in the habit of disambiguating such expressions with a unary +.)

Likewise,

[ @array ]
makes a (shallow) copy of @array and returns a reference to the copy.

the lowliest monk

Replies are listed 'Best First'.
Re^2: Wrong Error Message When Testing for Failure of Constructor
by jkeenan1 (Deacon) on Jul 22, 2005 at 17:39 UTC
    I think the problem is that you are taking a reference to the %default_values hash, instead of making a copy of it.

    The point made by both davidrw and tlm is well taken. I tried it out by rewriting the relevant part of the constructor as follows:

    warn "DV: " . \%default_values; my $self = ref($class) ? bless( +{ %default_values }, ref($class) +) : bless( +{ %default_values }, $class ); warn "self: " . $self;

    It worked ...

    ... at first.

    Then it didn't.

    To keep my original posting to a reasonable length, I only included two test blocks in t/001_load.t. With the suggested revisions to the constructor, both passed.

    However, the real-world module of which Woeisme is a distillation included additional test block both before and after the two listed originally. I first added those tests which, in the real-world test suite, occurred before the two cited.

    All tests passed. I added one more test similar to the last one.

    All tests still passed. But when I started to add more tests testing values for the other keys of the AUTHOR hash, I started to get exactly the same type of error as I reported yesterday. Adding one test:

    failsafe( [ 'NAME' => 'ABC::Alpha', 'AUTHOR' => { NAME => 'James E Keenan', EMAIL => 'jkeenancpan.org', }, ], "^EMAIL addresses need to have an at sign", "Constructor correctly failed; e-mail must have '\@' sign");

    ... led to this error:

    not ok 22 - Constructor correctly failed; e-mail must have '@' sign # Failed test (t\001_load.t at line 88) # 'CPAN IDs are 3-9 characters # EMAIL addresses need to have an at sign # # No such file or directory at t\001_load.t line 87 # ' # doesn't match '(?-xism:^EMAIL addresses need to have an at sign) +'

    Note that in the last test block, I was not testing the CPANID key; only the NAME key (which should have PASSed) and the EMAIL key (which should have been the sole FAIL). There shouldn't have been any reference at all to the CPANID key.

    So it appears we have not yet found the bug! Thanks for those who took the time to look at this. Any other ideas?

      I suspect the reason is fundamentally the same. I missed that you have references among the values of %default_values, meaning that you need a deep copy (not the shallow copy originally proposed). Try this quick fix (just to confirm the hypothesis):

      my %default = %default_values; # shallow copy $default{ AUTHOR } = +{ %{ $default_values{ AUTHOR } } }; # copy next level my $self = ref($class) ? bless( \%default, ref($class) ) : bless( \%default, $class );
      Ultimately, my preferred code for this would be something like
      my $self = bless deep_copy( \%default_values ), $class;
      where deep_copy takes a reference as argument and returns a reference to a deep copy of it.

      I know that there are CPAN modules for doing deep copying, but I have no direct experience with any of them, so I can't recommend any one in particular. Maybe some other monk can.

      the lowliest monk

        tlm wrote:

        Ultimately, my preferred code for this would be something like

        my $self = bless deep_copy( \%default_values ), $class;

        where deep_copy takes a reference as argument and returns a reference to a deep copy of it.

        I struggled with this some more but, as long as my default values were stored in a hash referenced by the testing subroutine, kept coming up with the same problem. Here's a little program I wrote which takes all the OO stuff out and demonstrates that, contrary to my hope/expectation, %default_values does indeed get transformed over a series of code blocks.

        Note that what is dumped for $dvref the second time around differs from what the first time.

        I have, however, found a solution: Localize %default_values within a subroutine and have that sub return a reference to that hash. The above program gets transformed into:

        The constructor is transformed in this manner:

        my $self = ref($class) ? bless( default_values(), ref($class) ) : bless( default_values(), $class );

        So, instead of the constructor blessing a reference to a hash of default values, it blesses the return value of a subroutine where that value is in turn a reference to the hash of default values.

        As I said at the outset, this example was a stripped-down version of a problem I was facing with the ongoing development of a module I maintain. Ironically, the solution above is the solution which the original module author (Geoff Avery) came up with in earlier versions of the module (ExtUtils::ModuleMaker). Plus ça change ....

        Thanks again.

        I will check this out over the weekend and report back. Thanks.