using Exporter::export_fail


A

A. Sicken

Hello,

I want to use the Exporter::export_fail-method to set some internal
debugging vars like:

use MyModul qw/enable_debug/; # to enable debugging messages

in following code example:

# --- CODE ---

use strict;
use 5.008; # and/or above version

package MyModul;

use Exporter;

our @ISA = qw/Exporter/; # to avoid namespace pollution

our @EXPORT_OK = qw/Some Symbols go here/;
our @EXPORT_FAIL = qw/enable_debug/;
our %EXPORT_TAGS = ( 'tag' => [@EXPORT_OK] };

our $VERSION = 1;

sub export_fail {
my $class = shift;
my $sym_name;
my @sym_fail;
while ($sym_name = shift) {
if ($sym_name eq 'enable_debug') {
# set var to use it later in module code
}else{
push @sym_fail, sym_name;
}#end_if
}#end_while
@sym_fail;
}#end_sub export_fail

# some module code goes hereafter

1;

__END__

# --- CODE ---

Now my questions: Is there a way to use the export_fail method without
declaring it within my module MyModul (or to destroy it after its first
invokation), because this means anyone can call this method as if it would
be a real method?
To clarify: I'm writing an oo-style framework/library and it seems
unnesseccary to me to provide this method as a real method for objects.

Maybe there is an easy way around this problem that takes inheritance into
account?

Thank you for your help and please excuse bad english.

A. Sicken
 
Ad

Advertisements

A

A. Sicken

Ben said:
No. If you must, you can get rid of the method on it's first invocation
by deleting the glob from the symbol table, but I wouldn't recommend
doing that.

You could alternatively write your own import method, which removes
enable_debug from the symbol list and calls Exporter->export_to_level to
do the actual exporting.

Ok. The method I want to get rid of is the export_fail-method (not the local
tied enable_debug var or the symbol) because you can call it on every object
created through this modul. Manipulation of the glob-table is a messy job
and very error prone; and it does not take modul inheritance into account.

The other way around (for inheritance only) I guess would be to write
export_fail-stubs for every single modul (currently about 20) which is also
a messy job :-( - and it would make it worse - because then every modul
would need to have an export_fail method which is completely unneccessary
for the object stuff.

Somehow I was hoping to scope the usage to a BEGIN{} block, so it will
invoked only once and then goes out of scope. The real annoying thing is
that the objects itself have debugging capability for their state and
contents whereas the debugging I reffered to is to trace the methods in a
certain module itself and I need this functionallity because of the very
nature of frameworks and their deep nested calling stack.

To use the export_to_level method isn't an option so far to me because I do
not know where to export; a caller based reference doesn't work all the
time, so I have to inspect @ISA to find the modul where to export to.

It seems a long way to go und feels frustrating.

Do you have any knowlege about fitting cpan modules or maybe
references/links where I can search for.

Thanx for your help.

Andreas
..
 
A

A. Sicken

Ben said:
No. If you must, you can get rid of the method on it's first invocation
by deleting the glob from the symbol table, but I wouldn't recommend
doing that.

You could alternatively write your own import method, which removes
enable_debug from the symbol list and calls Exporter->export_to_level to
do the actual exporting.

Ok. The method I want to get rid of is the export_fail-method (not the local
tied enable_debug var or the symbol) because you can call it on every object
created through this modul. Manipulation of the glob-table is a messy job
and very error prone; and it does not take modul inheritance into account.

The other way around (for inheritance only) I guess would be to write
export_fail-stubs for every single modul (currently about 20) which is also
a messy job :-( - and it would make it worse - because then every modul
would need to have an export_fail method which is completely unneccessary
for the object stuff.

Somehow I was hoping to scope the usage to a BEGIN{} block, so it will
invoked only once and then goes out of scope. The real annoying thing is
that the objects itself have debugging capability for their state and
contents whereas the debugging I reffered to is to trace the methods in a
certain module itself and I need this functionallity because of the very
nature of frameworks and their deep nested calling stack.

To use the export_to_level method isn't an option so far to me because I do
not know where to export; a caller based reference doesn't work all the
time, so I have to inspect @ISA to find the modul where to export to.

It seems a long way to go und feels frustrating.

Do you have any knowlege about fitting cpan modules or maybe
references/links where I can search for.

Thanx for your help.

Andreas

EDIT: Maybe I am thinking in the wrong direction! When is the export_fail
method called? Before or after any BEGIN-block? Or is there any run-state
that can be called right after export_fail (as hook for example)?
 
R

Rainer Weikusat

A. Sicken said:
Ok. The method I want to get rid of is the export_fail-method (not the local
tied enable_debug var or the symbol) because you can call it on every object
created through this modul.

And what's the problem with that? 'class modules' I'm using usually
also contain auxiliary subroutines which could also be called 'on
every object' except that this wouldn't be particularly useful: If
you're creating modules supposed to be used by other people and a
significant minority of these use them in ways you originally
considered to be 'useless', that indicates your 'public interface' is
lacking.

[...]
The other way around (for inheritance only) I guess would be to write
export_fail-stubs for every single modul (currently about 20) which is also
a messy job :-( - and it would make it worse - because then every modul
would need to have an export_fail method which is completely unneccessary
for the object stuff.

Leaving the issue mentioned above aside, you could create a single
'export fail stub' module, put the subroutine into @EXPORT and then do
a

use export_fail_stub;

to import it as method into every module/ class supposed to utilize
it.
 
A

A. Sicken

Rainer said:
And what's the problem with that? 'class modules' I'm using usually
also contain auxiliary subroutines which could also be called 'on
every object' except that this wouldn't be particularly useful: If
you're creating modules supposed to be used by other people and a
significant minority of these use them in ways you originally
considered to be 'useless', that indicates your 'public interface' is
lacking.
Many modules are base-class modules to many oher modules - either directly
(as parent) or indirectly (as grand parent).
The potentional down side for the import method is to get unwanted
functionallity... you mostly can ignore that. Beside: Does anyone use the
import-method within a given class modul unless in pragmas to extend perls
bahavior (directly without use-statement)?
The export method - I asume - will be likely more often called. For example
if you want to provide defaults, constants or such thing and that is the
problem - the export-fail method is implicite called if it exists and it
will exists by all module siblings (in case of inheritance). The export_fail
method is usaully used to degrade (more or less gracefull) functionallity
and that as side effect will be pain in the a.. - not only if you need to
find the source of the problem.
[...]
The other way around (for inheritance only) I guess would be to write
export_fail-stubs for every single modul (currently about 20) which is
also a messy job :-( - and it would make it worse - because then every
modul would need to have an export_fail method which is completely
unneccessary for the object stuff.

Leaving the issue mentioned above aside, you could create a single
'export fail stub' module, put the subroutine into @EXPORT and then do
a

Ok, the problem for both solutions is messy, because in both cases (either
deleting globs and or build method stubs) means, you have to find a way to
traverse @ISA to find the right parent, caller or what ever make sence.
Some of my modules use Object as their method arguments and decide to work
in different ways upon what comes in...
use export_fail_stub;

to import it as method into every module/ class supposed to utilize
it.

I do use this kind of stuff very often (as often as I can :), but as a
stateted above, sometimes this will not work...

So I guess I can close the thread as not so well solved but informative and
I will leave this problem untouched - I know: someday it will hunt me...

Thanks for your help, comments and advices.

Andreas
 
A

A. Sicken

Ben Morrow wrote:

I agree it's a bad idea.


This package you are exporting from is a base class? Certainly, that
would be messy.
Most of them, you guessed right.
Incidentally, are you inheriting from Exporter or importing its import
method? If you are inheriting then you've just scquired a whole lot of
methods anyway.
No, no - I use ONLY the import method in my own classes this way:

use Exorter qw/import/;
A further alternative would be not to use a 'use' parameter at all but
simply to provide a function or method to turn this debugging on. Given
that it has global effect, I doubt you'll want to do this very often.
That is the point: I can not decide at this point, where and when classes
will be used - so having a global sitting around will work perfectly except
for cases where you deal with stuff like web-frontend, pg-backend and so
on... my modules will work as type of connectors or templates.
I don't understand what you mean here. If you're currently using
Exporter::import then writing your own import method which calls
Exporter->export_to_level(1, @exports) will do the same thing.
Sorry my fault. Most modules work like connectors and they are somehow aware
what to do. For example: I have written two modules - one
(Ext::Data::Object::Float) does stuff like defining what a float number
should look alike (size, precision, minimum and many things more) - the
other (PgSerializer) stuff things into an Postgres-Table and or Database.
Now if you define a Float-object and provide it to PGSerializer it will use
the given definitions to create a fitting table - even SQL-constraints. Same
goes for a String object. But if you provide an PgSerializer object to a
Float object (Ex. Ext::Data::Object::Float->new($PgSerializer) ) it will
simply create a Float object with meating definitions.
For both objects exists a dumper class: If you call that with Float it will
behave in default menor (writing some xml) but if you put the Float object
wrapped in PgSerializer in it, it will pass handling the stuff to
PgSerializer.
In short you can call them nested and depending on how they are nested you
get functionality.
This works because of a well defined object and method api that carry
required information. One leg to it is to reuse code (where inheritance come
in).
Is any of this important? Does it actually matter that your objects have
an extra method? After all, they have a useless 'import' method already.
To Rainer I have explained before why the import method do not bother me
(much) and why the export and the allways implicite called export_failed
method is in such a case different.
There are other Exporters on CPAN; possible one will do what you want.


export_fail is called from Exporter::export, which is called from
Exporter::import or Exporter::export_to_level.
I need a run-state like exporter is called right after its own begin-blocks
but before your modul begin-blocks. In that case I can simply use a begin-
block to override or delete my own eport_fail method and resolve any
depending problem at once...

So far I haven't found information about when export is called.

Also to you many thanks for your help.

Andreas
 
Ad

Advertisements

R

Rainer Weikusat

A. Sicken said:
Rainer said:
"A. Sicken" <[email protected]> writes:
[...]
And what's the problem with that? 'class modules' I'm using usually
also contain auxiliary subroutines which could also be called 'on
every object' except that this wouldn't be particularly useful
[...]

Many modules are base-class modules to many oher modules - either directly
(as parent) or indirectly (as grand parent).
[...]

the export-fail method is implicite called if it exists and it
will exists by all module siblings (in case of inheritance).

This is not quite true: According to the documentation,
Exporter::import will end up calling the export_fail method when an
import of a symbol listed in @EXPORT_FAIL is attempted. Modules/
classes not using that would be unaffected. For others, the default
behaviour of turning every attempt to import something on @EXPORT_FAIL
into a compile-time error would change. A workaround for that could be
to add

return &Exporter::export_fail unless $_[0] eq __PACKAGE__;

as first line to your export_fail method. This would forward the call
to the default implementation unless the method was called 'directly'
via the module/ class defining it.
 
Ad

Advertisements

A

A. Sicken

Rainer said:
This is not quite true: According to the documentation,
Exporter::import will end up calling the export_fail method when an
import of a symbol listed in @EXPORT_FAIL is attempted. Modules/
classes not using that would be unaffected. For others, the default
behaviour of turning every attempt to import something on @EXPORT_FAIL
into a compile-time error would change. A workaround for that could be
to add

return &Exporter::export_fail unless $_[0] eq __PACKAGE__;
With this you made my day: Thanks. So Exporter does, what I want to avoid
for my self - it traverse the calling tree to find its right member
export_fail.

The part where @EXPORT_FAIL must exist, does not actually help, because i
will implement it to all my modules as trigger/hook to enable debugging code
- but as the return line above show: Only if the given classename (by
caller) is not equal to the static class name, which implements export_fail
you have to call Exporter::export_fail staticly to defere the handling
stuff. So no inheritance - and that is exactly what I wanted. I tested it so
far and you are totally right.

Out of curiousity: My docs (perldoc 5.16.3) do not cite this... gush...

Many many thanks for your help: Problem solved!

Andreas
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top