Should RT docs include inheritance details for RT::<blah> modules?

I spent quite a bit of my time Perl hacking on RT to support localised uses here. As such I’m often found with the RT docs up in a browser tab looking up methods I can use on various RT:: namespace Perl objects in the system.

One thing I find is that I often have to revert to looking at the code itself as the docs don’t indicate what the inheritance is, although RT sensibly makes heavy use of that feature of Perl. For example RT::Group has manifests a HasRight method, but that is because it has inherited it from RT::Record. It doesn’t have a RevokeRight method directly available, because that is implemented by RT::Principal which itself inherits from RT::Record.

Would others find it useful to be able to follow these inheritance links in the documentation directly?

I would find this useful. Perhaps something autogenerated might do, such as a thing which follows the dependencies and generates a list for a given class like RT::Group along the lines of

RT::Group directly provides:

  • SelfDescription
  • Load ID

RT::Group inherits:

  • RT::Record::_Init
  • RT::Record::_PrimaryKeys
  • RT::Base::CurrentUser

and so on. Then you’d be able to see at a glance what methods will be available for a given class, and where they’ve come from.

(Edit)

Here is a quick and dirty attempt, based on this post: reflection - How do I loop over all the methods of a class in Perl? - Stack Overflow

#!/usr/bin/perl
#
# Report the methods provided by a class and its parents; takes the class
# name as a parameter.  Omits methods from ancestors which the descendants
# have overridden.
#

sub ListClassMethods {
    my ( $Class, $Prefix, $SeenClasses, $SeenMethods ) = @_;

    $Prefix      = '' if ( not defined $Prefix );
    $SeenClasses = {} if ( not defined $SeenClasses );
    $SeenMethods = {} if ( not defined $SeenMethods );

    return () if ( exists $SeenClasses->{$Class} );
    $SeenClasses->{$Class} = 1;

    eval "require $Class";
    no strict 'refs';

    my @Methods = (
        '<li>From <code>' . $Class . '</code><ul>',
        (
            map { '<li><code>' . $_ . '</code></li>' } sort { $a cmp $b }
            map { $SeenMethods->{$_} = 1; $Prefix . $_ }
            grep { not exists $SeenMethods->{$_} }
            grep { defined &{ $Class . "::$_" } } keys %{ $Class . '::' }
        ),
        '</ul>'
    );

    push @Methods, ListClassMethods( $_, $_ . '::', $SeenClasses, $SeenMethods )
      foreach @{ $Class . '::ISA' };

    return @Methods;
}

print '<ul>' . join( "\n", ListClassMethods( $ARGV[0] ) ) . "</ul>\n";

This gives you something like this:

  • From RT::Group
    • AddMember
    • AddRight
    • AvailableRights
    • BasicColumns
  • From RT::Record
    • RT::Record::ACLEquivalenceObjects
    • RT::Record::AddAttribute
    • RT::Record::AddCustomFieldDefaultValues

It might be more useful to just dump out the methods of each class horizontally, with this script instead:

#!/usr/bin/perl
#
# Report the methods provided by a class and its parents; takes the class
# name as a parameter.  Omits methods from ancestors which the descendants
# have overridden.  Shows a short version of the information.
#

sub ListClassMethods {
    my ( $Class, $SeenClasses, $SeenMethods ) = @_;

    $SeenClasses = {} if ( not defined $SeenClasses );
    $SeenMethods = {} if ( not defined $SeenMethods );

    return () if ( exists $SeenClasses->{$Class} );
    $SeenClasses->{$Class} = 1;

    eval "require $Class";
    no strict 'refs';

    my @Methods = (
        '<li><code>' . $Class . '</code>: <br />',
        map { '<code>' . $_ . '</code>' } sort { $a cmp $b }
          map { $SeenMethods->{$_} = 1; $_ }
          grep { not exists $SeenMethods->{$_} }
          grep { defined &{ $Class . "::$_" } } keys %{ $Class . '::' }
    );

    push @Methods, '<ul>',
      ListClassMethods( $_, $SeenClasses, $SeenMethods ), '</ul>'
      foreach @{ $Class . '::ISA' };

    push @Methods, '</li>';

    return @Methods;
}

print '<ul>' . join( "\n", ListClassMethods( $ARGV[0] ) ) . "</ul>\n";

This second version gives output like this, which gives an easier quick view of the methods available:

  • RT::Group :
    AddMember AddRight AvailableRights BasicColumns BeforeWipeout Create CreateRoleGroup CreateUserDefinedGroup CurrentUserCanSee DOES DeepMembersObj Delete DeleteMember Disabled
    • RT::Record :
      ACLEquivalenceObjects AddAttribute AddCustomFieldDefaultValues AddCustomFieldValue AllDependedOnBy AllDependsOn Attachments Attributes ClassifyTransaction ClearAttributes ColumnMapClassName CreatedAsString

Hopefully either of these might be useful in this context with a bit of tidying up.

This would certainly be handy, hopefully they can be made links to the relevant docs as well.

I also often find myself needing to refer to the code to get more detail on the behaviour of methods…