Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight
 
PerlMonks  

Re^2: Is "ref($class) || $class" a bad thing?

by stvn (Monsignor)
on Jul 12, 2004 at 16:55 UTC ( #373660=note: print w/ replies, xml ) Need Help??


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; }


Comment on Re^2: Is "ref($class) || $class" a bad thing?
Select or Download Code
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.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://373660]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others cooling their heels in the Monastery: (11)
As of 2014-07-23 06:21 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My favorite superfluous repetitious redundant duplicative phrase is:









    Results (133 votes), past polls