The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Devel::StrictObjectHash - A strict access-controlled hash for debugging objects

SYNOPSIS

    use Devel::StrictObjectHash (
            # replace bless in the modules that match this reg-ex
            strict_bless => qr/.*?Module$/,
                                                                       
            # allow hash autovivification in routines other than 'new'
            allow_autovivification_in => qr/create_.*|_init/,
            
            # set the field access identifiers
            field_access_identifiers => {
                # no public access allowed
                public => undef
                # first char is an underscore followed by alpha
                protected => /^_[a-zA-Z]/  
                # the word 'private' followed by an underscores 
                # followed by an alpha character
                private => /^private_[a-zA-Z]/ 
            },
            
            # turn on debugging
            debug => 1           
        );

DESCRIPTION

The goal of this module is to provide a drop in bless replacement for debugging object field access issues during development. It should never be used in production, as it has performance costs.

What does this module do?

This module implements a tied hash which has OO style access control. By default, it provides protected style access control for regular hash keys, and private style access control for hash keys that are prefixed with a underscore (_), and does not allow any form of public access. However, if this is too strict a setting, it is possible to configure the key-style for public, protected and private access using regular expressions (see INTERFACE).

How do I use this module?

The idea is that you configure this module at the top of your script (or in your mod_perl startup.pl file) to turn it on. Your application will then raise an error (the default is to die) if you try to access your object fields incorrectly. It will quickly help you to find where someone (possibly you) is doing bad things with your objects.

Do I need to change my code to use this module?

Yes and No.

No - If your code is well written OO code, then you should not have to make any other changes then to load and configure Devel::StrictObjectHash. I have tried (and am trying) to make this object as configurable as possible to cover many styles of hash-based OO code. However, if I am not accomadating your style (and you would like me too), let me know.

Yes - If your OO is not so good and you do things like allow private fields to be accessed by subclasses, or access fields outside of object methods or other such nastiness. Then you will likely either not want to use this module at all, or you will need to recode.

However, if your goal is to recode/refactor "bad-style" OO, then you actually may find this module very useful.

INTERFACE

strict_bless

The strict_bless option tells Devel::StrictObjectHash which packages in which to override bless. The result of this is that the classes contained in those packages will be under the watch of Devel::StrictObjectHash. This parameter accepts three different types of arguments. The first is an array reference of package names:

    use Devel::StrictObjectHash (
                        strict_bless => [ qw(MyModule HisModule HerModule) ]
                        );

The second option is a regular expression reference, which is used to decide which modules to place under the control of Devel::StrictObjectHash. This option actually uses the little known trick of pushing a subroutine onto the @INC array, which means that it will only work for modules loaded after this line:

    use Devel::StrictObjectHash (
                        strict_bless => qr/.*?Module$/
                        ); 

The third option, is not usually recommended, but may be handy at times. This option will override bless for everything, through CORE::bless.

    use Devel::StrictObjectHash strict_bless => 'global';

Be careful with this option, since it will override bless not only for your modules, but for any other you happen to load as well. However, if your program only uses your modules, then by all means, use this.

allow_autovivification_in

Allowing autovivification of the object's internal hash to happen in routines other than just new (the default). This option exists because at times it makes sense for the creation of an object's instance (the constructor) to be seperate from the initialization of that instance.

The argument can either be a string

    use Devel::StrictObjectHash (
                        allow_autovivification_in => "_init"
                        );   

or a regular expression reference

    use Devel::StrictObjectHash (
                        allow_autovivification_in => qr/create_.*|_init/
                        ); 
                        

It is recommended that your regular expression be a strict as possible, so as to not match unintended routines.

field_access_identifiers

As mentioned above, by default, Devel::StrictObjectHash provides protected style access control for 'regular' hash keys, and private style access control for hash keys that are prefixed with a underscore (_), and does not allow any form of public access. I realize this is a very strict, OO-purist style, and not for everyone, so I have made it possible to configure your hash-key access as you see fit.

This option sets the field access identifiers for the three categories; public, protected, private. The public option can be set to undef, which will result in disallowing public access.

    use Devel::StrictObjectHash (
                        field_access_identifiers => {
                                # first char is alpha
                                public => /^[a-zA-Z]/, 
                                # first char is an underscore followed by alpha
                                protected => /^_[a-zA-Z]/  
                                # first 2 chars are underscores followed by alpha
                                private => /^__[a-zA-Z]/ 
                                }
                        );
error_handling

By default, Devel::StrictObejctHash will die if it encounters an incorrect field access. This may not be acceptable to your application, so an option to send the error to warn is provided.

    use Devel::StrictObjectHash (
                error_handling => "warn"
            );

Currently the only available options are warn and die, although there are plans for allowing custom error handlers to be written.

debug

Devel::StrictObjectHash has a number of embedded debug statements, which can be used essentially to 'watch' your code with. These statements are sent to STDERR and prepended with a debug line number for easy reference. Turning this feature on is as simple as this:

    use Devel::StrictObjectHash debug => 1;  

There are future plans for allowing a custom debug handler to be used.

METHODS

strict_bless

This is the method that Devel::StrictObjectHash uses to replace bless with. It can also be used on its own if you like, although it kind of defeats the whole purpose of the module.

Dump

Since this module doesn't play to well with Data::Dumper's Dump method, we supply a replacement method here. This will essentially dump the underlying tied hash that Devel::StrictObjectHash uses.

handleError

Handles the error based upon the value of the error_handling import parameter. Defaults to die.

CAVEATS

Does not work well with Data::Dumper, as it gets caught in the tied hash access routines. Use our Dump instead to see the tied hash that Devel::StrictObjectHash uses.

Currently, checking access within DESTORY methods appears a little wonky, so we just dont go there.

Currently you are not allowed use each, keys or values on an object's internal hash and doing so will result in an error being thrown. To me, this makes since, as that it really an operation that you should not be doing with your internal hash. If you disagree with me, and can explain to my why it does make sense, email me, and I will add this ability.

Currently you are not allowed to clear or untie the hash. I cannot see a reason to where it would ever make sense to do this, so errors are thrown if it is attempted.

TO DO

Tests and Code Coverage

Currently we have 141 tests in the test-suite, but there are 2 test files which are yet to be written. I have included stub test files, and there are comments detailing the test plans, but I just don't have the time currently to write them. I want to get this 0.01 release up to CPAN to replace the very incomplete 0.01a that is up there currently.

Also for some reason Devel::Cover chokes on the tests, even though they run fine under Test::Harness. Again, I don't have time currently to investigate this, so I will leave it be for now.

Custom error handlers

Ideally you could write your own error handler, it would look something like this.

    use Devel::StrictObjectHash (
                error_handling => sub {
                    MyErrorHandler::HandleError("MyApplication::Error [", @_, "]")
                }
            ); 
Using exceptions for die errors

I would like to eventually use exceptions when the error handling is set to die, this way we can include stack trace information in the errors.

Custom debug handlers

The same for debug handlers as well.

    use Devel::StrictObjectHash (
                debug => sub {
                    MyDebugger::debug("DEBUG [", @_, "]");
                }
            ); 
            
More debug statements

There are a number of debug statements embedded in the code, but I am sure that they are not enough, nor are the 100% descriptive enough. They can/should be improved.

Fix command-line interface

Ideally you wouldn't even touch your own code at all. But instead, just do something like this:

  perl -MDevel::StrictObjectHash=strict_bless,global,debug,1 my_script.pl
  

And the module would do its magic. But for some reason, that doesn't work, and I have no clue why.

Configuration file

It would be nice to have the CLI be able to check for a configuration file, most likely stored in the local directory.

BUGS

So far, so good, but if you find a bug, let me know, and I will be sure to fix it.

SEE ALSO

Clearly this module was inspired by Tie::StrictHash. The difference is that Tie::StrictHash is a general purpose hash with access controls, while this module is meant for debugging object field access issues only.

AUTHOR

stevan little, <stevan@iinteractive.com>

COPYRIGHT AND LICENSE

Copyright 2004 by Infinity Interactive, Inc.

http://www.iinteractive.com

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.