Discussion:
[cgi-prototype-users] persistent prototypes (mod_perl)
Ryan Tate
2005-06-25 18:50:26 UTC
Permalink
Hello,

I recently moved a simple CGI::Prototype-based Web app over to a mod_perl2
installation. It wasn't too hard.

One thing I eventually noticed: if you dispatch to objects representing
particular classes, as CGI::Prototype::Hidden does (see name_to_page and
its call to $package->reflect->object), you have to be careful to
re-initialize slots as needed, because package objects are not garbage
collected and thus will persist.

To handle initialization, for each class with slots I want killed before
the next
request, I do a control_leave with $self->reflect->deleteSlots(qw(userfoo
userbar tempfoo tempbar)), along with $self->SUPER::control_leave(@_) for
any classes above.

If you fail to re-initialize your slots, they persist to the next hit,
which is a feature if you know what you're doing and a bug otherwise. I
have not yet found it useful to persist any slots.

I am curious if name_to_page was made with an eye toward mod_perl -- was
it foreseen that in a persistent environment one might need to initialize
slots? I'm wondering if a better approach might be to make my own
name_to_page that does $package->new, thus not requiring any
initialization, but costing more (probably) in obj creation and requiring
extra work to keep track of shortname (essentially page_to_name to
remember what state name/package was used to gen the object).

I have not yet found any slots I wish to persist so I end up having to
remember to write an initializer for each slot I create.

Cheers
RT

PS Liked the first Linux Mag article.
Randal L. Schwartz
2005-06-25 19:05:10 UTC
Permalink
Ryan> Hello,
Ryan> I recently moved a simple CGI::Prototype-based Web app over to a
Ryan> mod_perl2 installation. It wasn't too hard.

That's good to hear. I tried to design CGIP with mod_perl in mind,
but I had no current customers using mod_perl directly.

Ryan> One thing I eventually noticed: if you dispatch to objects
Ryan> representing particular classes, as CGI::Prototype::Hidden does (see
Ryan> name_to_page and its call to $package->reflect->object), you have to
Ryan> be careful to re-initialize slots as needed, because package objects
Ryan> are not garbage collected and thus will persist.

Yes. And this can be trouble, as you've seen.

Ryan> To handle initialization, for each class with slots I want killed
Ryan> before the next request, I do a control_leave with
Ryan> $self->reflect->deleteSlots(qw(userfoo userbar tempfoo tempbar)),
Ryan> along with $self->SUPER::control_leave(@_) for any classes above.

That would seem to be the logical place to do this.

Another way would be to put all your slots into one big "per hit" slot,
and then ensure that this is cleared out:

sub My::App::control_enter {
my $self = shift;
$self->reflect->addSlot(per_hit => {});
}

Through testing, I see that addSlot also throws away any previous
meaning for the slot, so this trashes the previous round data just
fine.

Then in any thing that needed it:

sub randomfunc {
my $self = shift;
my $per_hit = $self->per_hit;

$per_hit->{foo} = "bar";
}

In this case, you just need to keep your keys of $per_hit clean,
but you had to do that with slots anyway. :) This'll also be faster
than adding and deleting slots every time.

Ryan> PS Liked the first Linux Mag article.

Parts two and three coming up... :)
--
Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095
<***@stonehenge.com> <URL:http://www.stonehenge.com/merlyn/>
Perl/Unix/security consulting, Technical writing, Comedy, etc. etc.
See PerlTraining.Stonehenge.com for onsite and open-enrollment Perl training!
Randal L. Schwartz
2005-06-25 19:43:52 UTC
Permalink
Randal> sub My::App::control_enter {
Randal> my $self = shift;
Randal> $self->reflect->addSlot(per_hit => {});
Randal> }

That should be My::App::app_enter { }

control_enter would cause it to get cleared as you transition from one
handler to another, although that might be an interesting use.
--
Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095
<***@stonehenge.com> <URL:http://www.stonehenge.com/merlyn/>
Perl/Unix/security consulting, Technical writing, Comedy, etc. etc.
See PerlTraining.Stonehenge.com for onsite and open-enrollment Perl training!
Ryan Tate
2005-06-26 06:23:09 UTC
Permalink
Post by Randal L. Schwartz
Another way would be to put all your slots into one big "per hit" slot,
sub My::App::app_enter {
my $self = shift;
$self->reflect->addSlot(per_hit => {});
}
I like this approach quite a bit.

After reading your message, though, I got a bit greedy and decided I
wanted to be able to add slots rather than just hash entries to per_hit,
so I could do cool things like autoload fields and coderefs that you don't
have to know are coderefs.

Then I got a bit lazy and decided I did not want to edit all my templates
to change things like "[% self.user %]" to "[% self.per_hit.user %]".

So I decided to extend your idea a bit to the following:

sub My::App::app_enter{
my $self = shift;
my $per_hit = Class::Prototyped->new;
$self->reflect->addSlots( 'per_hit*' => $per_hit,
per_hit => $per_hit );
}

I went through my classes and changed most every ->reflect->addSlots
to ->per_hit->reflect->addSlots, and things seem to be working fine. At
least, nothing has blown up yet ;->

I double-checked to make sure the 'per_hit*' parent classes were not
accumulating on top of one another, one for each hit. As I
suspected, writing a parent slot with the same name as
a previously-established parent slot seems to overwrite it.

One problem with this approach is that almost every slot I create seems to
be per_hit. I'm starting to think it would be smarter to create a
'persist*'/'persist' slot in the manner above, then in control_leave
iterate over
the slots and delete each one except 'persist'. This may be time consuming,
however.

RT
Randal L. Schwartz
2005-06-26 15:50:50 UTC
Permalink
Ryan> So I decided to extend your idea a bit to the following:

Ryan> sub My::App::app_enter{
Ryan> my $self = shift;
Ryan> my $per_hit = Class::Prototyped->new;
Ryan> $self->reflect->addSlots( 'per_hit*' => $per_hit,
Ryan> per_hit => $per_hit );
Ryan> }

Oooh. Cool. Shiny Object.

Now, are you starting to see why I started with Class::Prototyped?
I *knew* stuff like this would show up. :)

This is definitely going into my snippets file. I hope to have a
CGIP::Cookbook in a future release, or maybe we'll just take turns
updating CGIP::FAQ.
--
Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095
<***@stonehenge.com> <URL:http://www.stonehenge.com/merlyn/>
Perl/Unix/security consulting, Technical writing, Comedy, etc. etc.
See PerlTraining.Stonehenge.com for onsite and open-enrollment Perl training!
Ryan Tate
2005-06-26 18:47:31 UTC
Permalink
Post by Randal L. Schwartz
Now, are you starting to see why I started with Class::Prototyped?
I actually started to think, "prototyped objects are actually a little ...
_fun_". Prototypes are sort of the Perl of object oriented programming.

RE FAQ, I have not seen it, but let me know if you want anything (else)
written up on this. Glad you liked!

RT

Loading...