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

I intend to publish a few roles as a specification for third party support modules, e.g.:

package ModuleSpec;
use Moose::Role;
requires 
  'foo',
  'bar',
  'baz',
  ...
  ...;
# actually there are quite a lot of methods to require

The existence of module spec helps when developing a support module, by reminding developer what methods it should support.

However, in future releases I intend to incrementally update the spec by adding more methods. Yes, I know about API freezing and stable interface, but in my case the module spec is somewhat looser/less formal than an API specification, and most of the evolution will be new methods without affecting much of the old/existing methods.

I don't want third party developers to have to keep updating their modules whenever my module specification comes out and adds some new methods here and there. I want users to be able to use older support modules with the newer module specification role.

But I also like to keep this specification as a role to remind developers what methods are newly required, so they can update their module to reflect the new specification.

So, can I somehow make so that:

use AcmeSupportModule;
use Moose;
with 'ModuleSpec';
sub foo { ... }
sub bar { ... }
sub baz { ... }
...

when a method is missing/unimplemented, just emit a warning instead of failing to load the module.

Currently I wrap all 'requires' in ModuleSpec like this:

package ModuleSpec;
use Moose::Role;
use MyUtil;
apifunc 'foo', ...;
apifunc 'bar', ...;
apifunc 'baz', ...;
...

and, depending on the situation, apifunc() will eval a 'requires "foo"' or just 'sub foo { ... }'.

Any better way to do what I wish with less hackery?

Replies are listed 'Best First'.
Re: Evolving roles and 3rd party modules
by stvn (Monsignor) on Apr 14, 2010 at 19:42 UTC

    My first instinct is to do something like this:

    package MyAPI::Version::One; use Moose::Role; require 'foo'; require 'bar'; package MyAPI::Latest; use Moose::Role; with 'MyAPI::Version::One';
    This would mean that if a 3rd party dev wanted to support v1 then would make sure to do the MyAPI::Version::One role, and if they wanted to make the commitment to always stay on the bleeding edge they could do the MyAPI::Latest role.

    Then when you update to a new version you simply do this:

    package MyAPI::Version::Two; use Moose::Role; with 'MyAPI::Version::One'; # assuming back-compat require 'baz'; package MyAPI::Latest; use Moose::Role; with 'MyAPI::Version::Two';
    And then all 3rd party devs who were doing the MyAPI::Latest would have to be sure to update but those who did the MyAPI::Version::One role would still be safe and could upgrade to MyAPI::Version::Two when they are able to.

    This should still be workable when you break back-compat too since there is no strict requirement that the MyAPI::Version::Two does the MyAPI::Version::One role.

    -stvn

      Thanks for the suggestion. Yup, adding version to role's name surely came to mind, though probably not really preferred in my particular case because I want to do incremental/continuous improvements to the role. The role here functions not so much as a strict interface but only as a warning mechanism for module developers about new methods.

      I was hoping for some button or configuration in Moose that makes roles less strict (e.g. warn() instead of die() when a required method is missing). I guess no one else would need or like such a feature?

        I was hoping for some button or configuration in Moose that makes roles less strict (e.g. warn() instead of die() when a required method is missing). I guess no one else would need or like such a feature?

        Nope, we shun those kinds of ideas in Moose. The features are all there for a reason and often times other elements of the system rely on them working to provide the entirety of the given functionality. That said, Moose does provide a lot of meta-layer hooks and extension points so it might be possible to make a "soft" role like what you describe as a MooseX:: module.

        Depending on how you are using this role you might want to just think about plain old duck-typing instead. If you never provide implementation in the role then the Moose:Util::TypeConstratins duck_type() feature may be enough for your needs.

        -stvn