Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses
 
PerlMonks  

Re: Coding perl a plugin system?

by skx (Parson)
on Dec 15, 2002 at 16:13 UTC ( [id://220016]=note: print w/replies, xml ) Need Help??


in reply to Re: Coding perl a plugin system?
in thread Coding perl a plugin system?

 Thats an interesting idea - previously I've only used 'Safe' to run code from an untrusted environment.

 In this case I wanted to run trusted, external, code in ::main scope, and I didn't consider the Safe module.

 Thanks for the pointer!

Replies are listed 'Best First'.
Re: Re: Coding perl a plugin system?
by sauoq (Abbot) on Dec 15, 2002 at 21:12 UTC
    In this case I wanted to run trusted, external, code in ::main scope

    Eh? Well, it seems I misread your question. From the original:

    Initially I though I could have a 'Plugins::Foo' package and 'use/require' it at runtime via 'eval' however this has the problem that the package loaded thusly can't access variables in the main scope of the server, because they're defined via 'my $foo = ..'.

    Is there a simple solution to this, without using 'our'??

    Why look for tricks to give your plugins access to lexicals when you don't have to use lexicals in the first place?

    You really should allow interaction between your main program and your plugins via a well-defined API, not by jumping through hoops to get the plugin code evaluated in your main program's file scope. (Note that this has nothing to do with the main package scope as your reference to "::main scope" attempts to imply.) That will eliminate your ability to hide implementation specifics from your plugins. Your plugins will all be one unfortunate identifier choice away from clobbering your main program or another plugin.

    You might make such a system work as long as you are the only one writing plugins but that defeats the purpose and, in that case, you might as well just extend the main program directly.

    I really suggest you rethink your design. You should consider a design whereby the main program shares data with the plugins only by

    • Calling subs defined in the plugins,
    • Providing subs which are called by the plugins,
    • Managing documented global variables.

    -sauoq
    "My two cents aren't worth a dime.";
    
      You really should allow interaction between your main program and your plugins via a well-defined API, not by jumping through hoops to get the plugin code evaluated in your main program's file scope.

       True I should. However I'm not really sure how to go about doing that. In the C++ version of the code which I'm re-coding in Perl I could enforce this through inheritence, and passing around pointers to abstract base classes. At the moment I'm unsure how I should handle this in perl.

      (Note that this has nothing to do with the main package scope as your reference to "::main scope" attempts to imply.)

       I apologise for my imprecise wording, I was under the impression that things at file scope in the main file/script would be at main scope. Are the two not the same? (Assuming the abscence of a 'package' statement)

      Your plugins will all be one unfortunate identifier choice away from clobbering your main program or another plugin.

       True, the plugin functions will clobber identifiers with the same name in the main scope; but this will be caught quickly by the perl interpretter.

       By design only one plugin is ever loaded in the execution of the main script. (There is forking involved and one forked child will ever attempt to load one plugin)

      You might make such a system work as long as you are the only one writing plugins but that defeats the purpose and, in that case, you might as well just extend the main program directly.

       I'm not sure I agree with that 100%. Consider the case of something like a webserver which wanted to generate, and serve, realtime statistics to a small number of clients.

       Writing the statistics display as a CGI script (or plugin) which would simply serve the last 100 lines from the access.log file is conceptually simpler than adding the functionality in the core of the server.

       That's a fairly similar situation to how my plugins are used. Yes they could be in the core of the script but having them as external plugins does simplify the core of the script.

      * Calling subs defined in the plugins,

       This happens, there are only three functions exported by each plugin, as hinted at in my original code snippet.

      * Providing subs which are called by the plugins,

       I'm not 100% sure how this could be enforced or achived, if you had the time I'd appreciate a small example, or a pointer to something discussing this more.

      * Managing documented global variables.

       This is already done, and such interactions are very minimal.

       Thanks for taking the time to respond - I'm certainly open to better ways of coding this system.

      Steve
      ---
      steve.org.uk

        If you want to do stuff C++ OO style in perl here is one way to do it. The basic API spec is as follows:

        A plugin must supply three methods, namely func1(), func2() and func3(). The plugin code is expected to be OO as shown in the sample code and nothing should be exported from it. These methods do foo bar and baz (ie take the following args, set whatever widgets, return whatever) To enable the plugin methods to do foo bar and baz access to the configuration variables $var1, $var2, $var3 is provided via get and set methods. The MP3 package also provides the following utility methods blah....

        With the vars in the MP3 package the closures mean using the set and get methods is the only way to set/get these vars. You can thus impose whatever level of sanity checking you like on what happens to them (from your code or the Plugin).

        By making the plugin OO you don't have to worry about namespace polution and you can pass initializing config data to it if required

        #!/usr/bin/perl -w package MP3; use Plugin; use strict; # get a plugin object so we can access its methods # we could pass config details to the plugin object # here if we want..... my $p = new Plugin( config_var => 'some config var' ); # use closures to make truly private lexical vars that # can only be accessed by the get and set methods { my $var1; sub set_var1 { $var1 = $_[0] } sub get_var1 { return $var1 } } { my $var2; sub set_var2 { $var2 = $_[0] } sub get_var2 { return $var2 } } { my $var3; sub set_var3 { $var3 = $_[0] } sub get_var3 { return $var3 } } sub say_gday { print "G'day World!\n" } set_var1(123456); print 'Get \$var1 ', get_var1(), "\n"; print "\$var1 is very private '$var1'\n"; # now call some of the Plugin functions.... $p->func1(); # sets $var1 to 123 and prints it... $p->func2(); # calls a function in MP3 package print $p->func3(); # return a config var package Plugin; sub new { my ($class, @init) = @_; return bless {@init}, $class; } sub func1 { MP3::set_var1(123); print MP3::get_var1(), "\n"; } sub func2 { MP3::say_gday(); } sub func3 { my $self = shift; return $self->{config_var}; } 1;

        cheers

        tachyon

        s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

        I apologise for my imprecise wording, I was under the impression that things at file scope in the main file/script would be at main scope. Are the two not the same?

        As you originally were talking about lexically scoped variables (i.e. variables declared with my), yes. There is a big difference. Lexicals are only visible from their declaration to the end of their enclosing block, eval, or file. Lexicals do not have symbol table entries. Global variables, such as those declared with our, are visible everywhere and do have symbol table entries. Your mention of "::main" was reminiscent of symbol tables (though "main::" would have made a bit more sense.)

        True, the plugin functions will clobber identifiers with the same name in the main scope; but this will be caught quickly by the perl interpretter.

        Variables as well as functions will run the risk of being clobbered. Warnings will help catch redefined subs but not variables.

        That's a fairly similar situation to how my plugins are used. Yes they could be in the core of the script but having them as external plugins does simplify the core of the script.

        I think you missed the point of my assertion. I wasn't suggesting that the notion of plugins wasn't useful. I was suggesting that, if you are going to let your plugins traipse all over your main program, then you are better off not using them. I say so because that approach is likely to cause a maintenance nightmare.

        * Providing subs which are called by the plugins,

        I'm not 100% sure how this could be enforced or achived, if you had the time I'd appreciate a small example, or a pointer to something discussing this more.

        I simply meant that one way to share information between the main program and the plugin is to call subs that are implemented in main from the plugin. The concept is pretty simple but I wasn't very clear. Here's a quick example:

        sub f { print "Data from plugin: $_[0]\n"; return 'foo'; }; package Plugin; my $r = main::f('bar'); print "Data to plugin: $r\n"
        See? It's so simple that I almost felt silly writing that. :-) A more useful example of this might be requiring your plugin to always register itself with the main program by calling main::register_plugin() or some such.

        As you say you'll only use a single "plugin" at a time, I'm not convinced that simply having multiple branches of execution wouldn't make more sense in your case. It sounds like you really just want some code to be loaded on demand and you could implement that via AutoLoader if necessary. I don't really know what functionality the various plugins provide though so it's hard to form an opinion.

        -sauoq
        "My two cents aren't worth a dime.";
        

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://220016]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others scrutinizing the Monastery: (1)
As of 2024-04-25 04:35 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found