Eric Wilhelm wrote:
# from Christopher J. Madsen
# on Saturday 15 March 2008 17:55:

$plugin->pre_METHOD($builder, $parameters, $context, $return)
$plugin->post_METHOD($builder, $parameters, $context, $return)

* To completely override a method, set $_[-1] to an appropriate value
   and return 'done'.

I don't like this return-by-reference scheme. Can we say that the $return is an object and use methods for that?

How does that work with post hooks? (In a pre hook, $return is output only. In a post hook, you must pass the current return value in. It may or may not get modified by the hook.) How does it handle scalar/list context?

What happens if you call done but don't return immediately? What if you call both done and continue? (Although we don't really need continue. If you didn't call done, that means processing should continue.)

Of course, return() doesn't carry a consistent meaning in Module::Build right now, so maybe that needs to be specified. What meaning should return values have for plugins?

I felt the return value of post hooks is ignored. Pre hooks return 'done'/'continue'. If we make done a method, I'd say we ignore the return value of pre hooks also.

Also, note that die() is probably the best way to deal with an error condition.

Sure.

The priority and ordering are a concern when any plugin can abort the action based on run order. This seems like it provides a lot of room for subtle bugs. How would a plugin declare that it is incompatible with another or otherwise avoid the silent error of "only one of my plugins ran"?

I don't think it would. That's up to the person selecting the plugins to run. Hopefully, user & system level plugins would be used only for things unlikely to conflict with other plugins.

I don't think many pre hooks will need to short-circuit evaluation. But I think it needs to be possible to prevent the default method from being called. I suppose we could say that if a pre hook short-circuits, the remaining pre hooks are still called (but any values they try to return would be ignored) and only the default method gets skipped. But I'm not sure that's better.

So, do plugins only get hooks on "ACTION_*", or is everything hookable?

No, that was just my examples. Any method is fair game. That was one of my main goals.

I'm a little concerned about what overhead is being added when no hooks are defined (but I suppose we could redefine our method with a hooked wrapper when a hook request appears.)

That's what I'm thinking.  When a method is hooked, we define something like

sub Module::Build::method_name {
  _call_method_with_hooks(@_, 'method_name')
}

So we'd have overhead only on the methods that actually got hooked. This is made simpler because M::B is essentially an empty class.

The requirement of get_hooks() is nice from M::B's reduced coding point of view, but it requires the plugin author to repeat themselves.

True. One point I didn't mention about subclassing: What if I want to subclass a plugin but prevent it from hooking one of the methods that it normally hooks? With get_hooks, I just eliminate that method from the list.

I suppose I could just add an empty method to my subclass:

sub pre_METHOD_I_DONT_WANT_HOOKED {}

But then we still get the overhead of hooking the method, even though we didn't need to. And what about future versions of the superclass hooking new methods?

Of course, if we put the symbol-grabbing get_hooks in a base class, then I can just override that. That may be the best approach.

One reason I didn't want a base class for plugins is that I'd like them to be able to use any object style they liked. On the other hand, get_hooks wouldn't actually need any per-object data, so if that's all the base class does, it wouldn't need to care what kind of object the plugin is using.

--
Chris Madsen                                          [EMAIL PROTECTED]
  --------------------  http://www.cjmweb.net  --------------------

Reply via email to