CGI/Perl Guide | Learning Center | Forums | Advertise | Login
Site Search: in

  Main Index MAIN
INDEX
Search Posts SEARCH
POSTS
Who's Online WHO'S
ONLINE
Log in LOG
IN

Home: Perl Programming Help: Advanced:
Plugins & Interfaces

 



mhx
Enthusiast

Jun 30, 2001, 11:53 AM

Post #1 of 6 (2086 views)
Plugins & Interfaces Can't Post

Hi all!

First, this is a problem that I have solved, I don't mind if I receive no replies. I'm just curious if there's a better or shorter or more elegant or more perlish solution than the one I applied. Here's my problem:

I was writing a tool, actually a parser for messages from a debugger I've written that runs in an embedded system, as it occurred to me that this parser should be easily extendable by other Perl modules - which I'm going to call plugins from now on - without touching the source of the parser script. I wanted the parser to have an option '-P plugin' so the user can pass the name of the plugin he has written.
The only thing that a user has to take care of when writing a plugin is that he has to provide an interface description in his plugin. I solved this with a hash table. Perhaps all this is easier to understand with some code.
So here's what a plugin template looks like in general. I left out some sections that have nothing to do with the problem. I left in the original comments, so it'll be easier to understand.

Code
package EcgDebug; 
use strict;

# ( I left out all the header stuff )

#----- Initialization Code ---------------------------------------------

BEGIN {
no strict 'refs';
$DBENV = \%{'main::DBENV'};
}


#----- Interface Definition --------------------------------------------

# The interface between the debug parser and the plugin is a hash table
# called `Interface'. It tells the debug parser which routines to call
# for certain messages or debug message ids.

our %Interface =
(
# Debugger Control & Status Message Headers
# `````````````````````````````````````````
# The debug parser needs to know these in case you want to put
# routines for debug messages in your plugin. The two keys must be
# both existing or both missing, otherwise the parser will fail.

DebugControlHeader => 'ThreeChanEcg_1_General_9_Int_1',
DebugStatusHeader => 'ThreeChanEcg_1_General_10_Int_1',

# Parser Table
# ````````````
# The parser table again is a hash table with either debug message
# ids or message headers as keys and subroutine references as values.
# You may add as much entries as you like. If your parser subroutine
# can handle more than one header or message id, feel free to use
# the same subroutine reference for different keys.

Parsers =>
{
ThreeChanEcg_1_Ecg_1_MdilRTSA_2_ObsVSPk => \&MyParser,
DB_MY_DEBUG_ID => \&MyParser,
},

# Routine Table
# `````````````
# The routine table is used for describing special properties of
# your parser routines. At the moment, the only special property
# of a routine is `options', which means that this routine can
# handle several option. Routines only need to be entered in the
# routine table if they have special properties. So if your
# routine doesn't take options, you don't have to put it in the
# routine table.
# An option is defined as a key => value pair with the key being
# the name of the option (an upper- or lowercase character) and
# the value being a string describing the option. The option
# name may be followed by a colon (`:') if the option takes an
# argument.

Routines =>
{
\&MyParser =>
{
options => {
'w' => 'write wave files',
'e:' => 'do something mysterious',
},
},
}
);

######################################################################
#
# ROUTINE: MyParser
#
# WRITTEN BY: ON:
# CHANGED BY: ON:
#
######################################################################
#
# DESCRIPTION: This is my favourite message parser.
#
######################################################################

sub MyParser
{
return whoami unless @_; # if called without arguments, return name
my $opt = shift; # get options

Print "No Options :-(\n" unless scalar %{$opt};
foreach( keys %{$opt} ) {
Print "Option `-$_' => $opt->{$_}\n";
}

if( exists $DBENV->{compiler} ) {
Print "ah, we're using the $DBENV->{compiler} compiler...\n";
}
else {
Print "no compiler info available...\n";
}

# just print the rest of the arguments
Print "@_\n";
}

1;

To use the plugin code from within the parser, I run the following loop after parsing the arguments and preparing some other things in the parser script. Again, I left out some further checking code that is not relevant here.

Code
foreach( keys %plugin ) { 
eval "require '$_.pm'" or die "$@\n";
{
no strict 'refs';

die "Invalid interface for plugin `$_'\n"
unless defined %{$_.'::Interface'};

$plugin{$_}->{interface} = \%{$_.'::Interface'};
}
# ...
}

Now, there are two things that I'm curious about:
First, is there a better way to do it? I'm sure there are other ways, but which is the most perlish? Is there generally a better way to implement an interface with the requirements I have?
The second thing I'm sure there's a better solution for: I have a hash table DBENV in the parser script, which I'd like to share with all plugins. You can see my solution in the BEGIN block in the plugin. I would like it better if I could access the hash as a hash and not as a hash reference from within the plugins.
It's now up to you: complain about my code, tell me what I could do better, any feedback is appreciated.

-- Marcus



japhy
Enthusiast / Moderator

Jun 30, 2001, 1:13 PM

Post #2 of 6 (2082 views)
Re: Plugins & Interfaces [In reply to] Can't Post

I would suggest you change your BEGIN block (which doesn't need to be a BEGIN block if your module is used at compile-time) to:


Code
*DBENV = \%::DBENV;

That aliases the %main::DBENV hash to the █ENV hash in the current package.

As for your plugin loop, I would suggest a couple small changes:


Code
for (keys %plugins) { 
require; # require()s $_
die "Invalid interface for $_" unless $::{"${_}::Interface::"};
$plugin{$_}{interface} = $::{"${_}::Interface"};
}

That code runs fine under strict. It also reads a bit nicer, to me at least.

Jeff "japhy" Pinyan -- accomplished hacker, teacher, lecturer, and author


mhx
Enthusiast

Jun 30, 2001, 1:31 PM

Post #3 of 6 (2081 views)
Re: Plugins & Interfaces [In reply to] Can't Post

Cool!

I knew there would be the possibility of using a typeglob for my DBENV hash. By the time I wrote the code I just couldn't figure out how. It seems so logical here, I don't know what I've been trying back then...

The plugin loop reads a lot nicer to me, too. Except I'll keep using the foreach instead of the for. Which is just because from my C-Programmers point of view it describes better what's happening, although they're equivalent.

Do you think the way the interfacing itself is done is ok for what I'm doing?

Thanks a lot, your answers were great!

-- Marcus



japhy
Enthusiast / Moderator

Jul 1, 2001, 7:53 AM

Post #4 of 6 (2073 views)
Re: Plugins & Interfaces [In reply to] Can't Post

The layout and interface look fine to me.

Jeff "japhy" Pinyan -- accomplished hacker, teacher, lecturer, and author


rGeoffrey
User

Jul 1, 2001, 9:38 PM

Post #5 of 6 (2067 views)
Re: Plugins & Interfaces [In reply to] Can't Post

I notice that you make use of the new to perl 5.6 "our" variables. Are you getting some special benefit from an our variable that you could not get otherwise? If you are just doing it to be cute or clean you are eliminating a large chunk of your potential users.

But then I also try to avoid HTML features that are not available in the 4.00 browsers unless I really need the newer feature. You can never overestimate the ability of people to procrastinate when it comes to upgrading browsers and perl interpreters.

--
Sun Sep 9, 2001 - 1:46:40 GMT, a very special second in the epoch. How will you celebrate?


mhx
Enthusiast

Jul 1, 2001, 10:59 PM

Post #6 of 6 (2066 views)
Re: Plugins & Interfaces [In reply to] Can't Post

Hi,


In Reply To
I notice that you make use of the new to perl 5.6 "our" variables. Are you getting some special benefit from an our variable that you could not get otherwise? If you are just doing it to be cute or clean you are eliminating a large chunk of your potential users.

I'm mainly doing it to be cute and clean. There are for sure other ways to do it. But since this feature is actually available in 5.6, I do use it. Of course you're right that this restricts the script to users that have 5.6 installed. But since the script and plugins that I refer to in my post are only used by developers in our division (i.e. I know all potential users personally and the script is absolutely useless for anybody else) and we all have 5.6 installed (and 5.6 is out now for quite some time) I don't care. If I would be making a public script or module, I would surely care more about this.
But thanks for pointing out that this code wouldn't work under <5.6. I guess there are some more 5.6 specials lurking somewhere in the code, but I don't have a problem with this.
Concerning the HTML, it's really a hard job to design pages that look at least similar on all the different kinds of browsers. I really wouldn't want to do it all day long. I think the last 'high level' feature I used on my page was a frameset ;-)

-- Marcus


 
 


Search for (options) Powered by Gossamer Forum v.1.2.0

Web Applications & Managed Hosting Powered by Gossamer Threads
Visit our Mailing List Archives