in reply to Saving/recovering sub refs in a file

Is there a way in my::Error::register() to "look up" the string and see if it's a real sub,

(ref($s) // '') eq 'CODE'
Is there any reasonable way to make this easier? I guess I can always add the 'my::Plugin::' prefix myself, or something, but... I dunno.

It sounds like your obsession about the length of a line that will only occur once per plugin.

By the way, it could be shortened from

my::Error::register('my::Plugin::SomePlugin::handler')
to
my::Error::register(__PACKAGE__)
if it's done from the module that needs registering.

Replies are listed 'Best First'.
Re^2: Saving/recovering sub refs in a file
by madscientist (Novice) on Jun 16, 2010 at 15:24 UTC
    Is there a way in my::Error::register() to "look up" the string and see if it's a real sub,
    (ref($s) // '') eq 'CODE'

    Sorry, I wasn't clear. I'm familiar with ref(); that's not what I'm looking for (unless it has hidden properties I'm not aware of).

    What I need to know is this: the caller will pass in a string naming a sub, and I want to know if the string corresponds to a real sub that has been defined, or not. In case the caller mistyped the name.

    So for example, the caller runs: my::Error::register('my::Plugins::ThisPlugin::miHandler') but the name of the real sub is myHandler... is there any way inside the register() function that I can figure out if the string I've been passed is the name of a real, existing sub or not (without running it of course!)

    I'd like to throw an error up-front in the register() function if someone passes a bad/invalid name for a sub, rather than waiting until a real error occurs and only finding out then, when someone tries to invoke the error handler, that it was bad.

      $ perl -le'sub f {} print exists(&$_)?1:0 for qw( f g )' 1 0

      So for example, the caller runs: my::Error::register('my::Plugins::ThisPlugin::miHandler') but the name of the real sub is myHandler...

      Then you should call my::Plugins::ThisPlugin::miHandler. If it throws an error, so be it. If you want to catch the error, use eval BLOCK or whatever module you prefer.

      That said, it might make more sense to register the package, then always call a specific method of that package (say ->handle_error()). You could even allow objects in addition to packages for free.

        So for example, the caller runs: my::Error::register('my::Plugins::ThisPlugin::miHandler') but the name of the real sub is myHandler...
        Then you should call my::Plugins::ThisPlugin::miHandler. If it throws an error, so be it. If you want to catch the error, use eval BLOCK or whatever module you prefer.

        I can't do that, because I can't call their error handler unless I get an error! It would be very incorrect to just call the handler "as a test". Certainly when I do call their handler I absolutely run it in an eval block and catch any undefined reference there.

        What I'm trying to do is validate that the sub name is correct and real at registration time, because the input they're giving is only very rarely used (only when a bad error occurs) and so it's quite possible that they could not notice the typo. Yes, of course, all these types of failure scenarious SHOULD be tested, but I prefer to implement defensive programming and fail immediately on the register if the argument I was given is bogus.

        Is it possible?

        That said, it might make more sense to register the package, then always call a specific method of that package (say ->handle_error()).

        Actually I can't do that either: some of these plugins have very complex error conditions and they register multiple error handlers, as the plugin proceeds and makes more changes, and they expect the handlers to be run in the proper order. Further, the same plugin can be called multiple times during the invocation, with different contexts, and this requires different cleanup operations.

        There are lots of options here of course, that involve more work by the plugin writers to consolidate all that into one error handler and keep their own internal state (which would need to be cached to disk--remember what I'm trying to implement is a way to "restart" the process after kill -9 or power failure). I can easily come up with these alternatives. However, the method that I have today is powerful and perfect for my needs and for the plugin authors' needs, and I really don't want to rewrite the error handling API and push a lot more work onto them: I want to continue to manage it for them inside my framework, and keep the framework easy to use and robust in the face of misconfiguration.

        Thanks!