Gossamer Forum
Quote Reply
Creating a hook
It seems that to create a new hook, all you need to do is add:

Code:
$PLG->dispatch('hook_name', \&sub_to_run)


where you want it? Is that true?

And, secondly, in the handle routine, is this the preferred format now (calling the plugin dispatch, then re-assigning $in in the routine, )

Code:
sub handle {
# ------------------------------------------------------------------
# Determine what to do.
#
my $input = $IN->get_hash;
if ($input->{add_review}) { $PLG->dispatch('review_add', \&add_review) }
elsif ($input->{edit_review}) { $PLG->dispatch('review_edit', \&edit_review) }
elsif ($input->{helpful}) { $PLG->dispatch('review_helpful', \&helpful_review) }
else { $PLG->dispatch('review_search', \&review_search_results) }
return;
}
# ==================================================================
Then, in the sub, my $in = $IN->get_hash; ## assign the $in data to the local variable, again



Or, is this way just as acceptable, and in some cases, more logical,

Code:
my $input = $IN->get_hash;
if ($input->{add_review}) { &add_review($input) }
elsif ($input->{edit_review}) { &edit_review($input) }
elsif ($input->{helpful}) { &helpful_review($input) }
else { review_search_results($input) }
return;
sub add_review {
my $in = shift;
$PLG->dispatch('review_add', \&add_review);........
}



Or, basically, make everything go through the handle routine, not bypass it, and
pass the $IN tags into the subroutines, rather than relying on them to get them,
or make "external" requests on their own.

Each sub would "logically" be responsible for checking if it had any plugin actions.

It seems to me that while the first example is "cooler", the second way is more
logical, and all passed-in params are up front, rather than spread throughout the
module.

The second way, also seems more "object oriented" since it's "contained". The call
to the plugin dispatch is not missed if "handle" is bypassed, and $in is passed in
no matter how it's gotten -- whether it's actually from $IN or from another source.


PUGDOG� Enterprises, Inc.

The best way to contact me is to NOT use Email.
Please leave a PM here.
Quote Reply
Re: [pugdog] Creating a hook In reply to
Yes, I aggree.
First one looks cleaner, but second one seems more logical.
Second (currently used) one guarantees permanent input, and package separation.

Best regards,
Webmaster33


Paid Support
from Webmaster33. Expert in Perl programming & Gossamer Threads applications. (click here for prices)
Webmaster33's products (upd.2004.09.26) | Private message | Contact me | Was my post helpful? Donate my help...
Quote Reply
Re: [webmaster33] Creating a hook In reply to
The problem is, though, the second way won't work, if the plugin call is a wrapper around the function :(

It seems one has to choose between having the subroutine "pluggable" or passing parameters into it outside of $IN

Parameters would have to be passed as a separate "hash" to the plugin dispatcher.

I don't like the $IN hash as a means of passing parameters between subs, since anything can change it. The purpose of OOP/encapsulation is to prevent that sort of intentional or accidental change. While perl doesn't actually enforce it, not re-using $IN is one way to avoid "gotchas".


PUGDOG� Enterprises, Inc.

The best way to contact me is to NOT use Email.
Please leave a PM here.
Quote Reply
Re: [pugdog] Creating a hook In reply to
Quote:
The problem is, though, the second way won't work, if the plugin call is a wrapper around the function :(

What do you mean exactly?


Quote:
Parameters would have to be passed as a separate "hash" to the plugin dispatcher.

Yes, I aggree.


Quote:
I don't like the $IN hash as a means of passing parameters between subs, since anything can change it. The purpose of OOP/encapsulation is to prevent that sort of intentional or accidental change. While perl doesn't actually enforce it, not re-using $IN is one way to avoid "gotchas".
Yes, I aggree.

Best regards,
Webmaster33


Paid Support
from Webmaster33. Expert in Perl programming & Gossamer Threads applications. (click here for prices)
Webmaster33's products (upd.2004.09.26) | Private message | Contact me | Was my post helpful? Donate my help...
Quote Reply
Re: [webmaster33] Creating a hook In reply to
Using this convention, you can pass parameters into a hook:

my $res = GT::Plugins->dispatch($CFG->{admin_root_path} . '/Plugins', 'validate_review', \&_validate_review_record, $review->{$id});


Note the last parameter passed to dispatch.

I don't know how I missed this the first few times through:

Code:
my $self = shift;
my $directory = ref $self && $self->{directory} ? $self->{directory} : shift;
my ($hook_name, $code, @args) = @_;
$hook_name = lc $hook_name;
defined $PLUGIN_CFG{$directory} or $self->load_cfg($directory);


The last parameter, after the return hook_name_reference is an array of args.

So, it might be possible to:

Code:
sub handle {
# ------------------------------------------------------------------
# Determine what to do.
#
my $input = $IN->get_hash;
if ($input->{add_review}) { $PLG->dispatch('review_add', \&add_review, ($in, $plugin_cfg)) }
elsif ($input->{edit_review}) { $PLG->dispatch('review_edit', \&edit_review) }
elsif ($input->{helpful}) { $PLG->dispatch('review_helpful', \&helpful_review) }
else { $PLG->dispatch('review_search', \&review_search_results) }
return;
}


And get in sub add_review:

Code:

sub add_review {
my ($in, $PLUGIN_CFG) = shift;

which would pass in the $in hash and the $plugin_cfg init hash.

I like this better, since at the top of the module, the data at "initialization" is requested, then is passed into the routine. The routines don't request any data from outside. (They do/can make requests of other routines, but those are also sketched out in the "use" commands at the top of the module as well).


PUGDOG� Enterprises, Inc.

The best way to contact me is to NOT use Email.
Please leave a PM here.
Quote Reply
Re: [pugdog] Creating a hook In reply to
Quote:
Using this convention, you can pass parameters into a hook:
my $res = GT::Plugins->dispatch($CFG->{admin_root_path} . '/Plugins', 'validate_review', \&_validate_review_record, $review->{$id});

Note the last parameter passed to dispatch.
I don't know how I missed this the first few times through.
The last parameter, after the return hook_name_reference is an array of args.

I did analyze Plugins.pm since a long time, so I also forgot this fact Smile
I had not need to create hooks by myself, but analyzed the possibility already.


Code:
package Links::User::Review;

sub handle {
# ------------------------------------------------------------------
# Determine what to do.
#
my $input = $IN->get_hash;
if ($input->{add_review}) { $PLG->dispatch('review_add', \&add_review, ($in, $plugin_cfg)) }
elsif ($input->{edit_review}) { $PLG->dispatch('review_edit', \&edit_review) }
elsif ($input->{helpful}) { $PLG->dispatch('review_helpful', \&helpful_review) }
else { $PLG->dispatch('review_search', \&review_search_results) }
return;
}
Hmm, it seems, you are developing something, like I did earlier.
Perhaps you do original code replacing?


I see why do you want pass parameters, but it is not needed, IMHO.
There are good reasons to put $plugin_cfg as global, so you should not bother with it furthermore...
Also $IN is also a global variable, so you don't need to pass it...

Best regards,
Webmaster33


Paid Support
from Webmaster33. Expert in Perl programming & Gossamer Threads applications. (click here for prices)
Webmaster33's products (upd.2004.09.26) | Private message | Contact me | Was my post helpful? Donate my help...
Quote Reply
Re: [webmaster33] Creating a hook In reply to
There are reasons for this.

$IN is a global variable, but it's also a CGI object, and anything running in the script can modify it. If a subroutine adds something to the $IN hash, it's there,next time something calls for it. $IN is a global object, and $in is a hash ref of the data that was in the object at the time $in was initialized. $IN can be altered anywhere it's called. $in can only be changed where it's passed.

Also, under persistant environments, it's not a good idea to set $PLUGIN_CFG as a global, and call it in each subroutine. Alex had said it's a low overhead call in each routine, but it's also duplicated code. and sort of messy again. I like to know what each subroutine "needs" before hand. If you grab your plugin_cfg at initialization, you are again working on the same values, and explicitly pass it. If a subroutine is going to use configuration values, and potentially set values, you should know it needs that data in the call.

If in the "handle" routine you set up your objects and variables, then pass them as needed, you can avoid errors.

Calling _sub_do_something and letting it grab what it wants from $IN doesn't tell you what the sub is going to do. It's better, IMHO, to call _sub_do_something_with($in) and then you *know* what it's operating on.

I just don't like calling $IN more than once in any program instance. $IN is just that, the "input". Once input, it's over and done, and should be flushed. From that point on, you work with what you have, and return only what you've decided to. Altering $IN, by setting/resetting values, and then passing it on, just seems *wrong* to me.

It's just a different style.


PUGDOG� Enterprises, Inc.

The best way to contact me is to NOT use Email.
Please leave a PM here.
Quote Reply
Re: [pugdog] Creating a hook In reply to
Quote:
$IN is a global variable, but it's also a CGI object, and anything running in the script can modify it. If a subroutine adds something to the $IN hash, it's there,next time something calls for it. $IN is a global object, and $in is a hash ref of the data that was in the object at the time $in was initialized. $IN can be altered anywhere it's called. $in can only be changed where it's passed.
You must have very good reason to afraid about a modified $IN global value.
While LSQL uses $IN globally, I don't see good reason to use it differently in plugins.



Quote:
Also, under persistant environments, it's not a good idea to set $PLUGIN_CFG as a global, and call it in each subroutine. Alex had said it's a low overhead call in each routine, but it's also duplicated code. and sort of messy again.
This is not true. If $PLUGIN_CFG is encapsulated into a package (for example: $APP::PLUGINS::MyPlugin::PLUGIN_CFG), then there is no problem under persistent environments.
Also I don't know what overhead or subroutine call are you talking about. The way I wrote, duplicated codes are avoided. You must have misunderstood my words.


Quote:
I like to know what each subroutine "needs" before hand.
If a subroutine is going to use configuration values, and potentially set values, you should know it needs that data in the call.

It's not a good idea, to list all input data as argument for a function or method. Especially for methods.
There are some globals, which are available in any package where needed & imported, like $IN, $CFG, $DB.
$PLUGIN_CFG (plugin config data derived for current plugin) needs to be imported in package of current plugin, only. We have a good reason to make this globally available. $PLUGIN_CFG could be even be an object like $DB is, so when destroyed, updates could be written back (Plugins::Config module might be derived for that task based on Links::Config).
But until that time, get_plugin_user_cfg & set_plugin_user_cfg is good enough.

My idea is, that plugins should have PUBLIC and PRIVATE config.
The get_plugin_user_cfg should have access to the current plugin's PUBLIC and PRIVATE config, and PUBLIC config of other plugins. Furthermore, should be able to get info of installed plugins & versions.
This way could be possible to protect sensitive data of saved by other active plugins... IMHO, this feature would be more important, than to protect $IN content (however I can aggree, $IN protection would be also fine). This is the paranoid level, I think. We should not go to that level, as this would make users afraid of plugins. But if GT would take these paranoid warnings, and modify LSQL to protect input and some sensitive config data, I would welcome it.


I walked away from the subject, sorry. Just got some thoughts, and wrote it.

Best regards,
Webmaster33


Paid Support
from Webmaster33. Expert in Perl programming & Gossamer Threads applications. (click here for prices)
Webmaster33's products (upd.2004.09.26) | Private message | Contact me | Was my post helpful? Donate my help...
Quote Reply
Re: [webmaster33] Creating a hook In reply to
Conceptually, I don't like the parts of Links that reference $IN in different places. It's messy.

There is a reason for global objects, but also a reason to limit what methods do. Methods should be simple. They should be self evident. Some routines in Links are overly complex -- one routine rather than several smaller ones.

Sometimes there is a reason, other times it's legacy.

Web scripts are not "circular" or "interupt driven." They are linear, top down, with a start and and end. Variables should be initialized, and data passed at the start of the script, not inserted as the script progresses. States on the web change, and the script should grab what it needs to finish it's journey when it starts. If all it needs is an $id, grab it, and pass it. If it needs a more complex data structure, set it up, validate it, and pass it.

Conceptually it bothers me that certain processes or actions, grab $IN or other values several times during a single process.

I also don't like using $IN->{values} in parts of the script. I much prefer grabbing them, and assigning them to another local variable for use.

Just a difference in style. Perl lends itself to development of cryptic short hands and one-liners when 3 or 4 lines are much clearer. Sometimes, there is a performance savings, other times there isn't. But the more cryptic a program gets, and the more "fuzz" it gets in it's logic, the harder it is to maintain over time. Burried function calls, parameter requests, value overrides, etc, can cause endless hours of debugging for no good reason, other than it was "cute" or "quick" at the time originally written.

Perl scripts are not device drivers. pure efficiency is *not* the prime motivation. The easier a segment of code "reads" the *better* it is.

The paramters issue is something different. You have to grab $IN to pass through the handle/dispatch routines, but there is no reason to call it again, once you get there.


PUGDOG� Enterprises, Inc.

The best way to contact me is to NOT use Email.
Please leave a PM here.
Quote Reply
Re: [pugdog] Creating a hook In reply to
You seem to be in a very philosophic phase now Wink

Quote:
I also don't like using $IN->{values} in parts of the script. I much prefer grabbing them, and assigning them to another local variable for use.

Using a separate variable, depends if it will be used more than 1 time, and if makes the code easier to read.


Quote:
Some routines in Links are overly complex -- one routine rather than several smaller ones.
Yes, there are still some functions, methods which could be simplified into smaller parts. I also saw several ones.

The question is, how much does it worth to atomicize the code? Each additional function, method is slowing down the code. Therefore there is an optimal limit where the atomification doesn't worth anymore...
So the question always is: how much performance do we lose, and how much better, clearer code do we get?

Best regards,
Webmaster33


Paid Support
from Webmaster33. Expert in Perl programming & Gossamer Threads applications. (click here for prices)
Webmaster33's products (upd.2004.09.26) | Private message | Contact me | Was my post helpful? Donate my help...
Quote Reply
Re: [webmaster33] Creating a hook In reply to
Actually, you shouldn't slow the code much at all, maybe improve it. Especially if some routines are called more often than others, if the routines do only what is most often asked, there is a much lower overhead on the cpu.

As pointed out, these are not device drivers.

If plugin code was more objectified, the way the internal object were, overhead would be lower in general.

$my_results = $my_objec->add_record($rec, $args);

is a much simpler, cleaner, and *better* call than $my_results = &my_objec($rec, $args);

Which is essentially the same thing.

If you want to get rid of parameter order, it becomes:

$my_results = $my_objec->add_record({rec=>$rec, args=>$args});

Perl does not have real objects, so there is some imposed overhead to using objects, but it makes things cleaner, better, and faster overall. These are all high-level calls, where the connection to the database is the limiting step, *NOT* anything in the perl code. The database has to get the call, get the data (over a network), then queue it, store it, get the resuts, and return the results, again over the network. Those are the rate limiting steps, not the procedure, function or object call.

perl also has a *lot* of gotcha's when it comes to passing variables, marking references, deleting references, etc. It's much better to explicitly try to type a variable, or set of values, than to allow (count on) perl to do it. Sure, some instances it's a neat trick. But you try that trick again, and it back fires, and you turn into that frog. Tricks are good for core-module code, which rarely, if ever, will change again, once properly debugged, and where it might save a few milliseconds now and again. They are not for application code which will change, needs to be debugged, or viewed and understood often.

In medicine, we define an acronym or abreviation as "something that saves your time, but wastes a lot of others' time."


PUGDOG� Enterprises, Inc.

The best way to contact me is to NOT use Email.
Please leave a PM here.
Quote Reply
Re: [pugdog] Creating a hook In reply to
You didn't answer to my last few sentences.

Repeating again:
Quote:
Yes, there are still some functions, methods which could be simplified into smaller parts. I also saw several ones.

The question is, how much does it worth to atomicize the code? Each additional function, method is slowing down the code. Therefore there is an optimal limit where the atomification doesn't worth anymore...
This was not the OOP or not OOP. Your examples showed OOP ws non-OOP code. Which has no difference. I was talking about analyzing a function how to split into smaller parts, which would result cleaner higher-level code, but would result more funtion calls, which has some CPU overhead.

So my thoughts, was about to atomicise the program flow. To have more function calls, in exchange to have cleaner code. Basically this means we exchange CPU for cleaner code. Which is good (at least until an optimal ratio).
But the question is, how much atomicity is worth (in exchange for CPU) for cleaner code?

Best regards,
Webmaster33


Paid Support
from Webmaster33. Expert in Perl programming & Gossamer Threads applications. (click here for prices)
Webmaster33's products (upd.2004.09.26) | Private message | Contact me | Was my post helpful? Donate my help...