glwtta has asked for the wisdom of the Perl Monks concerning the following question:

Greetings Monks,

Say I have a class: My::Foo::Bar which inherits from My::Foo, calling My::Foo::Bar->can('a_method') will return true whether a_method is defined in My::Foo::Bar or My::Foo - is there a way to differentate between the two? In other words can I find out not only if a class "can" a method, but also that it's not inherited?

A bit of background on why I need this: I have a set of Class::DBI classes all inheriting from a base class and all defined for the same table. In the base class I have an init() which looks at a specific field and reblesses itself to one of the inheriting classes depending on the value. At this point, I want to call the init() on the newly reblessed reference, only if it defines one (obviously bad things happen if it doesn't).

Sure, there are dozens of trivial workarounds, but this is about learning things, right? :)

Thanks,

PS Sorry if something similar has been answered before - for some reason I am having little luck Super Searching for "can"

Replies are listed 'Best First'.
Re: inheritance and can
by chromatic (Archbishop) on Aug 10, 2003 at 06:16 UTC

    In general, it's tough, partly because Perl's such a dynamic language and partly because it's a question of design. If I were in your shoes, I'd likely save the class name as it came into the constructor.

    If you really need to compare references, one approach is take a reference to the method within the class. It's easiest to do that with symbolic references. That should be one warning against the idea...

    package Foo; sub foo {} package Bar; @Bar::ISA = 'Foo'; sub bar {} package main; my $bar = bless {}, 'Bar'; my $foo = $bar->can( 'foo' ); my $fooinbar = do { no strict 'refs'; my $subname = ref $foo . '::foo'; \&{ ref $foo . '::foo' } if defined *{ $subname }{CODE}; }; print "$foo -> $fooinbar\n";
Re: inheritance and can
by PodMaster (Abbot) on Aug 10, 2003 at 05:33 UTC
    `perldoc -f defined' (and there's nothing stopping you from doing Foo::Bar->can and not Foo->can). Aside from that, what you're doing is fishy by design, and I say you should seriously rethink it (i'm guessing you want to create some kind of factory).

    update: You may also wanna checkout Class::Inspector (and also Module::Info and Devel::ModInfo, as well as any other similar modules which might exist on cpan -- I didn't look past the ones I have installed).

    MJD says "you can't just make shit up and expect the computer to know what you mean, retardo!"
    I run a Win32 PPM repository for perl 5.6.x and 5.8.x -- I take requests (README).
    ** The third rule of perl club is a statement of fact: pod is sexy.

      Thanks, those modules would do it (though I will probably do something simpler).

      What's fishy about it? Seems it's a common enough thing to do with Class::DBI; it's been brought up as a possible default feature in the future.

        What you are doing _is_ a type of factory...

        What's fishy (to me) is that you have an init in the base class that creates, while all of the child classes have an init that initializes. Why not just rename the init method in the base class to "create"? Then you can use the can method on the newly blessed object to see if it has an init method (because the base class will no longer have a method named "init").
Re: inheritance and can
by perrin (Chancellor) on Aug 10, 2003 at 12:27 UTC
    Frankly, when you start needing to do something tricky like this, it usually means you're doing something wrong, and working harder than you need to. There has to be a simpler solution. In this case, I'd suggest you just make your additional init method be called something else, make it a no-op method in the base class, override it in the one class that needs to, and just always call it. Re-blessing the class from a base class sounds really fishy too, but since I don't know why you're doing it I can't easilly suggest an alternative.
Re: inheritance and can
by bbfu (Curate) on Aug 10, 2003 at 17:04 UTC

    Update: Nevermind. I forgot the @ISA's. What was I thinking. :(

    (Removed code trying to use $self->can('My::Foo::Bar::init'), which doesn't help.)

    Of course, I tend to agree that it's likely bad design. *shrug*

    Update 2: I guess you could do print "Bar can\n" if do { local @My::Foo::Bar::ISA; $self->can('init') }; but, man, that seems ugly.

    bbfu
    Black flowers blossom
    Fearless on my breath

Re: inheritance and can
by ehdonhon (Curate) on Aug 11, 2003 at 12:43 UTC

    Does My::Foo::Bar implement method "a_method"?

    if ( defined &{My::Foo::Bar::a_method} ) { ... }

    Does generic package $x implement "a_method?"

    no strict "refs"; if ( defined &{"${x}::a_method"} ) { ... }