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 overridebless
. 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, throughCORE::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 towarn
is provided.use Devel::StrictObjectHash ( error_handling => "warn" );
Currently the only available options are
warn
anddie
, 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 todie
.
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.
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.