The Moose docs grant is finished, so I’m going to review what exactly was accomplished, and how it differed from the original grant proposal. I’ll take each section of the original proposal one at a time.

Write a set of Moose::Intro docs

I ended up calling this documentation Moose::Manual instead, but what was produced is pretty similar to the original plan.

The original proposal called for the following sections:

  • Moose::Intro::Attributes
  • Moose::Intro::Subclassing
  • Moose::Intro::MethodModifiers
  • Moose::Intro::MooseObject
  • Moose::Intro::Roles
  • Moose::Intro::Types
  • Moose::Intro::Introspection
  • Moose::Intro::MooseX

The end result produced the following:

  • Moose::Manual::Concepts

    This is just a reworking of the Moose::Intro documentation I had written prior to the grant. It focuses on concepts without including much code.

  • Moose::Manual::Classes

    This includes the proposed Subclassing documentation, as well as providing a general intro to Moose.

  • Moose::Manual::Attributes

    This is pretty much as I imagined.

  • Moose::Manual::Delegation

    The Attributes documentation is already quite huge, so it made sense to separate a chunk out.

  • Moose::Manual::Construction

    This is the proposed MooseObject page with a better name.

  • Moose::Manual::MethodModifiers

    More or less identical to the proposal.

  • Moose::Manual::Roles

    More or less identical to the proposal.

  • Moose::Manual::Types

    More or less identical to the proposal.

  • Moose::Manual::MOP

    This is the proposed Introspection page renamed.

  • Moose::Manual::MooseX

    More or less identical to the proposal.

  • Moose::Manual::BestPractices

    This is a collection of tips I came up with while writing the rest of the manual, and it also includes recommendations that were in Moose::Cookbook::Style, which existed prior to the grant.

  • Moose::Manual::Contributing and Moose::Manual::Delta

    Stevan Little added these pages later.

Revise all of the existing cookbook recipes for clarity and simplicity

Yep, did that.

Write the recipes marked as TODO

The original proposal called for:

  • Moose::Cookbook::Basics::Recipe8 – Managing complex relations with trigger

    I ended up putting a trigger example into Recipe 3 instead. Recipe 8 remains unwritten, and probably needs a new topic.

  • Moose::Cookbook::Basics::Recipe11 – BUILD and BUILDARGS

    I wrote this.

  • Moose::Cookbook::Roles::Recipe3 – Runtime Role Composition

    I wrote this too.

  • Moose::Cookbook::Meta::Recipe6 – Hooking into the immutabilization system

    This recipe did not get written at all. The core team realized that the immutabilization system is too nasty to document as-is. For Meta Recipe 6, I showed a Meta Method class that enforces method privacy.

  • Moose::Cookbook::Meta::Recipe7 – Custom meta-instances

    I wrote this.

I also ended up writing a new recipe, Basics Recipe 12, which shows how to subclass a non-Moose parent using Moose.

Complete API docs for all Moose and Class::MOP classes

While working on this, I realized that some of the more internalish classes had really weird APIs that should not be documented. Therefore, a few classes are still entirely undocumented. In many cases, I was able to rename, deprecate, and refactor to a point where we had a reasonably sane public API to document.

So the upshot is that while not every class is fully documented, everything that should be documented is documented, for now.

What next?

Doing all this documentation work definitely highlighted some areas of the code that need changing. There are further refactorings to come, especially for the pieces that were deemed to gross to document at all. That includes immutabilization, some aspects of the type system, and some other areas.

I’d like to thank the Perl Foundation again for sponsoring this work. The grant was motivational for me, because this was a huge amount of work. I might have done some of it over time, but I doubt I would have done all or done it nearly as quickly without the grant.

I hope people out there in Perl-land find the new documentation useful. If you have feedback, you can find us on IRC at irc://irc.perl.org/#moose, email the Moose list, or file a ticket in the appropriate RT.cpan.org queue.

I actually finished the grant about a week ago, but I’ve been so busy I forgot to say anything here!

The last recipe I wrote was a new Basics recipe on subclassing non-Moose classes. The original plan called for a recipe on hooking into Moose’s immutabilization system, but that part of Moose is really nasty, and Yuval Kogman has plans for a major re{factor,write}. There’s really no point is documenting what’s there now. If you’re really intrepid, you can cargo-cult from some other MooseX module.

Thanks to TPF for funding this grant. I think it’s led to some good improvements in the Moose docs. The feedback I’ve gotten on the Manual has been quite positive so far. Of course, nothing’s really ever done, so patches and suggestions are welcome.

Working on the Moose and Class::MOP API documentation recently has once again reinforced the importance of writing documentation. Writing docs hugely improves your APIs. Conversely, not writing docs makes it much easier to write really bad APIs.

I’m hoping that going forward, all new Moose and Class::MOP work will require API docs for all public APIs. This will help us catch bad designs before they’re released.

Here’s some code smells I think docs help catch …

The Parameter of Many Names

For example, “package_name” and “class_name”. Which one takes precedence if both are passed? Or is that an error? Bite the bullet and deprecate a name right now. Add a big fat warning if the deprecated name is used.

Parameter Co-dependency

This shows up as complicated inter-parameter dependencies or exclusions. Your API allows various mutually exclusive sets of parameters, or some parameters always exclude others. For example, you can pass A and B, or B, C, and D, or, C, E, and F. But if you mix sets you get an error. The fix for this is usually to split the one method into several, one per valid set of parameters.

Who the Hell Are You?

Inconsistent naming is painful. Moose and Class::MOP have some real problems with this, for example we use “class” and “metaclass” to refer to the same thing (a metaclass object).

What the Hell Are You?

Inconsistent concepts are also really painful. Another problem for Moose and CMOP. This problem manifests when similar APIs take slightly different sets of parameters. For example, sometimes we take a class name, and sometimes we take a metaclass object, and sometimes we even take both! Considering that with CMOP, we can convert from a metaclass object to a name and vice versa, we need to just pick one thing we want and stick with it.

State of Madness

This is a whole subcategory of smells. They manifest in documentation with phrases like “you need to call methods X and Y before calling Z” or “if you have already called X, then Y returns a foo, otherwise it returns a bar”.

When your docs have this sort of information, you are pushing way too much information about internal object state into the minds of your API users. Hide this stuff. This often can be done by making some methods private and simply calling them as needed when the (fewer) public methods are called.

If a method has multiple return values depending on internal state, you probably need multiple methods, each of which returns the appropriate piece of information.

If you have a lot of methods that will die absent some piece of state, then you may be best served by adding some required parameters to the constructor, and/or some good defaults. A good API has a constructor that returns a fully usable object. Bad APIs have a constructor that gives you an object that requires you to call five more methods before it’s useful.

Fortunately, this isn’t too much of a problem for Moose and CMOP. For the most part, internal state is not exposed in a way that leads to weird complications. The biggest exception was Class::MOP::Immutable, which I refactored before documenting for exactly this reason.

Conclusion

Write docs before you release an API. If you find the docs hard to write, revisit your APIs until the docs flow naturally. If you don’t you’ll pay for it later, when you do write docs and realize you now need to go through some painful and annoying deprecation cycles.

I’m just inches away from declaring the grant done.

Last week, I released new versions of Class::MOP and Moose which feature (mostly) complete API docs. I say mostly because some things are intentionally being left undocumented for now. These are methods with public names (no leading underscore) that I think should be made private, or in a few cases entire classes with really weird APIs that need some rethinking. I think the API docs are done enough to satisfy the grant requirements.

Some of this rethinking has already happened, and doing this doc work let to some refactoring and deprecations, with hopefully more to come.

In the area of Moose Cookbook recipes, I’m one recipe away from done-ness. New recipes cover custom meta-instance and meta-method classes. Back in mid-February I released recipes for using BUILD and BUILDARGS, and applying roles to an object instance.

I expect to have the last recipe finished sometime by mid-April.