Re: Altering Package Subs and Running In To Problems
by aufflick (Deacon) on Nov 11, 2004 at 05:09 UTC
|
Don't forget that the body of your package is run when it is use'd or require'd, so I do something like this:
#! start of my/db.pm
package my::db;
use Sybase::DBlib;
sub new
{
return Sybase::DBlib->new(@_);
}
{
package Sybase::DBlib;
sub my_version_of_an_existing_method
{
# foo
}
}
So my end cgi scripts only 'use' the my::db module, and my::db->new returns a Sybase::DBlib object, but when you call $dbh->myVersionOfAnExistingMethod() you get my version instead of the built in version.
You can also add methods to the namespace that way as well.
Note that this is less clean if you are using a non-OO module, but by 'use'ing your module after the original one, the same effect will be had. | [reply] [d/l] |
|
package My::Db;
use Sybase::DBlib;
our @ISA=qw(Sybase::DBlib);
# new() is only needed if you need to do additional
# stuff - otherwise Sybase::DBlib's new() is called
# automatically
sub new {
my $package = shift;
my $dbh = $package::SUPER->new(@_);
# do something with $dbh here ....
return $dbh;
}
# Override dbnextrow() to allow ad-hoc processing
sub dbnextrow {
my $self = shift;
# Call the real dbnextrow()
my @row = $self::SUPER->dbnextrow(@_);
# and now do some magic with @row before
# returning it...
....
return @row;
}
If you do it this way the whole system becomes extensible - you'll note that I never mention Sybase::DBlib in the package itself - only in the @ISA - and this lets perl decide at run-time what methods to call based on the inheritance tree.
Michael
| [reply] [d/l] |
|
That's true, but the author of Sybase::DBlib has said on a number of occasions that it is very hard to subclass for a number of reasons. This can be confirmed by trying!
Apparently the latest version which either just got to CPAN or is just about to includes some changes which will make subclassing easier.
So if all you want to do is, say, add a few methods - then something like I suggested is a good way to do it.
In most situations your advice is, of course, correct.
| [reply] |
|
|
Re: Altering Package Subs and Running In To Problems
by Aighearach (Initiate) on Nov 11, 2004 at 03:17 UTC
|
The problem is, you are standing there with a bunch of bricks trying to build a grass hut.
Figure out first what you're trying to do, THEN decide how to do it.
Which is to say, your implementation is wrong. You're trying to change the way SomeModule behaves, but you're only the user of that module. Normally this is better changed by subclassing SomeModule. Because then you're not trying to be more than a user of SomeModule. Because, you're only a user.
To do it the way you are wanting to, you'll need a real pragma and not just a regular module. If that doesn't tell you what you have to do differently, you're not ready. Sorry if that sounds dismissive. But, bone up on internals and then you will be in a better place for this.
If the module you're trying to change is arbitrary, then it's harder. But if you're trying to change a SPECIFIC module, then you could also just make sure you've loaded it already in your module, and then as long as your module gets loaded first, use OtherModule will be redundant and ignored and not a problem. But that's very brittle, and not a good idea. THough I'm not sure what you're trying to do in the first place is a Good Idea
| [reply] |
|
Sorry I was unclear about my intentions, though i do have some.
I updated my post if youd like to read it again.
I'm trying to make writing extensions easy, and to turn the OO world upsidedown for the fun of it.
Also would boning up on internals be easy with just the perl manual, and if not would you have any suggestions?
| [reply] |
|
Unfortunately, internals means picking a good C book, and setting down with the perl source code. There are also lots of good online resources. YMMV
As far as, turning the OO world on it's head, from what I can tell your system is just really brittle, and steps on everybody's toes.
What about your system differentiates and improves on subclassing? Can you give a realworld example where your system makes plugins easier than subclassing?
I use a custom plugin system for my web template system (because Mason lags), I just define a simple interface and then plugins just have to implement a handler(). I thought about making them just run without forcing them to define their own package names, etc, but then it just gets harder to tell which is which when I'm looking at the performance times. That is, doing it the "right" way, I can track stats based on package names. Would your system help me?
| [reply] |
|
|
Re: Altering Package Subs and Running In To Problems
by simonm (Vicar) on Nov 11, 2004 at 08:27 UTC
|
To get run-time behavior, replace your "use MyCoolModule" with "require MyCoolModule; import MyCoolModule;" which does the same thing but after your subroutines have been defined.
For what it's worth, the fact that your tree is standing on its head doesn't mean you can't use inheritance. I'm using multiple inheritance to generate subclasses on demand with different sets of mixin classes; see Solving the SUPER problem in Mixins with a Dispatch Method. | [reply] |
|
Hmm... really good idea.
Maybe there still is a way to use 'use' (or one line and still keep it at the top)?
I was also thinking of playing around with the BEGIN, INIT, etc. subs in my Plugin class and seeing if anything like that worked.
Even though the dispatch-ness sounds pretty cool, my goal is to have the base module have all the subs.
Lets say package Foo (base) uses Bar (child) as a plugin. Foo's subs would be overwritten by Bar's (limited by, say, an export hash like Exporter uses), thus letting Foo's subs transparently point to Bar's.
Perl code probably explains this better...
foreach (@Bar::PLUGIN_SUBS)
{
# error checking and the like
*{"Foo::$_"} = \&{"Bar::$_"};
}
So I could call Foo::something, but actually (permitting Bar does plug into the 'something' subroutine) Bar::something would be called. Kind of like flattening a tetris board (without the falling pieces) such that, at the end, only the blocks that were originally at the top are now lining the bottom.
(hope that made sense) | [reply] [d/l] [select] |
|
I tested and doing it in INIT works as you'd expect:
import is called when the module is used, thereby replacing
the function before the base one being replaced is defined.
So the base ends up replacing the plug-in. But INIT
is called right before execution so it does the job.
Regarding the evilness of this: Playing around like this is
one of the ways I learned how Perl works. You will no doubt
experience several lightbulbs going on as try different
approaches, which is a good thing. Just think about your
clients, the interfaces they use, and how much you may be
bending standard views of the world before you turn what
you play with into production code.
| [reply] |
|
|
Ahhhh, this code explains it. You're not wanting to step on toes, you're wanting your users to step on YOUR toes!
Another thing to consider, is using the Safe module (with everything turned on perhaps) to load the plugins into a certain namespace. And then use that namespace for the parts of your code you want the plugins to overwrite. That way, you can even still publish an interface, but each plugin can only see the plugin-accessible part of the namespace.
use Safe;
my $cpt = Safe->new( 'PluginNamespace' );
$cpt->deny_only();
$cpt->rdo( PLUGINFILENAME );
And then the plugin doesn't have to mess with PluginNamespace, that happens automatically. You can even pass things in (which is as if the plugin got them from you with export. Like a reverse export) and other crazy stuff.
I use Safe alot, but rarely for what it was intended for...
| [reply] [d/l] |
|
|
package Foo;
sub shared { print "Shared method" }
sub foozle { print "Foo's foozle" }
package Bar;
sub foozle { print "Bar's foozle" }
package Blip;
sub foozle { print "Blip's foozle" }
package FooBar;
@ISA = qw( Bar Foo );
package FooBlip;
@ISA = qw( Blip Foo );
With that kind of setup, you can choose which set of plugins you want to have active, and can have one part of your code use one plugin while another part does not.
package main;
$package = 'Foo';
$package->shared();
$package->foozle(); # Calls Foo's foozle
$package = 'FooBar';
$package->shared();
$package->foozle(); # Calls Bar's foozle
This approach also lets you dynamically stack your "plugins" so that a method in one of them can call NEXT and get the results of the next method up the chain; see my earlier article for details of this technique. | [reply] [d/l] [select] |
|