http://www.perlmonks.org?node_id=373660


in reply to Re: Is "ref($class) || $class" a bad thing?
in thread Is "ref($class) || $class" a bad thing?

But, I must take exception with one of the conclusions you're implying. The error in line 14 is caused by the fact that your new() method isn't verifying that it was actually passed anything.

I know thats why its happening, you know why its happening, but does Joe-Perl-Newbie know why? Maybe, maybe not. The error message given points back to the $foo->test line, which it not the actual source of the issue. IMO, its just adds to the insidiousness of this.

As for a way to solve this, code-wise, I am currently toying with using this:

sub new { my ($class) = @_; $class = ref($class) || $class; (UNIVERSAL::isa($class, __PACKAGE__) || UNIVERSAL::isa(__PACKAGE__, $class)) || die "Oh no you don't!!!!"; bless {}, $class; }
It works (or rather doesn't work) in all the following conditions:
Foo::new(); Foo::new("SomeOtherModuleName") ; Foo::new([]); Foo::new({});
As well as works correctly when inherited. I am trying to think of other ways it might get used/abused to see how well it holds up. Any thoughts are appreciated.

But, we're just getting stupid. :-)

I know, isn't it fun ;-P

-stvn

UPDATE:
I think this would actually be the most thorough approach, even though I think UNIVERSAL::isa probably handles $class being undef okay.

sub new {   my ($class) = @_;   $class = ref($class) || $class;   (defined $class && (UNIVERSAL::isa($class, __PACKAGE__)       || UNIVERSAL::isa(__PACKAGE__, $class)))           || die "Oh no you don't!!!!";     bless {}, $class; }

Replies are listed 'Best First'.
Re^3: Is "ref($class) || $class" a bad thing?
by chromatic (Archbishop) on Jul 12, 2004 at 23:31 UTC

    Seems like it'd be less work to write:

    sub new { croak "new() called as a function, not a method\n" unless @_; bless {}, shift; }

    If you really want to be paranoid, though, you should make something similar a precondition of all of your methods. (For convenience sake of everyone else, though, please use [CPAN://Scalar::Util]::blessed(). Also, you ought to call isa() directly, not through UNIVERSAL::, in case someone overrides it.

      Seems like it'd be less work to write:
      sub new {     croak "new() called as a function, not a method\n" unless @_;     bless {}, shift; }

      As dragonchild already pointed out, this would not handle Foo::new({}), nor would it handle Foo::new("Data::Dumper"). Both are things are equally as stupid for a user to do, but a new user may not realize the method/function difference, the real question is whether that is my reponsibility or not.

      If you really want to be paranoid, though, you should make something similar a precondition of all of your methods.

      I assume you mean to check $self, that is true, but even I think that would a little much.

      Also, you ought to call isa() directly, not through UNIVERSAL::, in case someone overrides it.

      That too would not work for Foo::new({}) or other such insanity.

      I am really just trying to find something in between totally over-the-top paranoid, and you-break-it-you-buy-it-an-I-dont-care. And I am still looking as I think the example I gave above it probably too much.

      -stvn
        #!/usr/bin/perl -w use strict; use Test::More tests => 5; package Foo; sub new { my $class = shift; return unless eval { $class->isa( __PACKAGE__ ) }; bless {}, $class; } sub is_object { 1 }; package Bar; @Bar::ISA = 'Foo'; package main; sub is_object { 0 }; my $foo = Foo->new(); isa_ok( $foo, 'Foo' ); my $bar = Bar->new(); isa_ok( $bar, 'Bar' ); ok( $foo->is_object(), '$foo is an object' ); ok( $bar->is_object(), '$bar is an object' ); my $hash = Foo::new( {} ); is( $hash, undef, 'Foo::new() should not bless a reference passed in' +);

      I'm surprised nobody has mentioned called_as_method from Devel::Caller.

      use Devel::Caller qw( called_as_method ); sub new { croak "must call new as method" unless called_as_method(0); ... };
        I'd consider that "a bit too much navel inspection". It breaks code I write that looks like this:
        my $destiny = $someobject->can("somemethod"); @_ = ($someobject, @args); goto &$destiny;
        There shouldn't be any way that this target method should be able to tell that I got there with ->can instead of a true method lookup. So if you write code that breaks that, shame on you.

        -- Randal L. Schwartz, Perl hacker
        Be sure to read my standard disclaimer if this is a reply.