root/trunk/POE-Session-Magic/Magic.pm

Revision 13, 10.3 kB (checked in by scottmc, 3 years ago)

Added old perl modules
Starting to think maybe I should structure the new repository differently

Line 
1package POE::Session::Magic;
2
3*VERSION = \0.04;
4
5use strict;
6use warnings FATAL => "all";
7use Scalar::Util qw( blessed );
8use Attribute::Handlers;
9use POE qw( Session Kernel );
10use Symbol qw( delete_package );
11
12my %Subroutine;
13my %Inline;
14my %Codemap;
15my %Session;
16my %Object;
17my %Method;
18my %Heap;
19my $RUNNING = 0;
20
21sub importsymbols {
22    my $package = shift;
23
24    no strict "refs";
25
26    *{ $package . "::destroy" } = \&destroy;
27    *{ $package . "::yield" } = \&yield;
28    *{ $package . "::post" } = \&post;
29    *{ $package . "::spawn" } = \&spawn;
30    *{ $package . "::Heap" } = \&Heap;
31    *{ $package . "::Kernel" } = \&Kernel;
32    *{ $package . "::Heap" } = \&Heap;
33}
34
35sub inlinehandle {
36    my ($referent, $package) = @_;
37
38    return sub {
39        if (defined $_[ARG0] and 
40                ($_[ARG0] eq $package) or (ref $_[ARG0] eq $package)) {
41            $referent->(@_[ARG1..ARG9]);
42        }
43        else {
44            $referent->(@_[ARG0..ARG9]);
45        }
46    };
47}
48
49sub packagehandle {
50    my ($referent, $package) = @_;
51
52    return sub {
53        if (defined $_[ARG0] and $_[ARG0] eq $package) {
54            $referent->(@_);
55        }
56        else {
57            $referent->($package, @_);
58        }
59    }
60}
61
62sub objecthandle {
63    my ($referent, $package) = @_;
64
65    return sub {
66        if (defined $_[ARG0] and ref $_[ARG0] eq $package) {
67            $referent->(@_);
68        }
69        elsif (defined $_[OBJECT] and ref $_[OBJECT] eq $package) {
70            $referent->($_[OBJECT], @_);
71        }
72    }
73}
74
75sub UNIVERSAL::Inline :ATTR {
76    my ($package, $symbol, $referent, $attr, $data) = @_;
77
78    $Session{$package}{inline_states}{$referent} =
79        inlinehandle $referent, $package;
80}
81
82sub UNIVERSAL::Object :ATTR {
83    my ($package, $symbol, $referent, $attr, $data) = @_;
84    unless ($Object{$package}) {
85        $Object{$package} = $package->new();
86        $Method{$package} = [];
87    }
88
89    push @{ $Method{$package} }, $referent;
90
91    $Session{$package}{object_states} ||= [
92        $Object{$package} => $Method{$package}
93    ];
94}
95
96sub UNIVERSAL::Package :ATTR {
97    my ($package, $symbol, $referent, $attr, $data) = @_;
98   
99    $Subroutine{$package} ||= [];
100   
101    push @{ $Subroutine{$package} }, $referent;
102   
103    $Session{$package}{package_states} ||= [
104        $package => $Subroutine{$package}
105    ];
106}
107
108sub post { $poe_kernel->post(@_) }
109sub yield { $poe_kernel->yield(@_) }
110sub Kernel () { $poe_kernel }
111sub Heap () { return $Heap{ $poe_kernel->get_active_session->ID } }
112
113sub eventcall {
114    my ($symbol, $package) = @_;
115
116    return sub {
117        printf "event(%d): %s called\n", $poe_kernel->get_active_session->ID,
118            *{$symbol}{NAME};
119
120        if (($_[0] eq $package) or (ref $_[0] eq $package)) {
121            if (defined wantarray) {
122                if (ref $_[0] eq $package) {
123                    return $poe_kernel->call
124                        ( ref($_[0]), *{$symbol}{NAME} => @_ );
125                }
126                else {
127                    return $poe_kernel->call
128                        ( $_[0], *{$symbol}{NAME} => @_ );
129                }
130            }
131            else {
132                if (ref $_[0] eq $package) {
133                    print "event(): Sent post to ", ref($_[0]);
134
135                    return $poe_kernel->post
136                        ( ref($_[0]), *{$symbol}{NAME} => @_ );
137                }
138                else {
139                    print "event(): Sent post to ", $_[0];
140                    return $poe_kernel->post
141                        ( $_[0], *{$symbol}{NAME} => @_ );
142                }
143            }
144        }
145        else {
146            if (defined wantarray) {
147                return $poe_kernel->call
148                    ( $poe_kernel->get_active_session->ID,
149                      *{$symbol}{NAME} => @_ );
150            }
151            else {
152                $poe_kernel->yield ( *{$symbol}{NAME} => @_ );
153            }
154        }
155    };
156}
157
158# TODO Make this delete any aliases that exist for this package
159#      which will then destroy any sessions which only exist from aliases.
160sub destroy {
161    my $package = shift;
162    print "Destroy called on $package";
163
164    delete_package $package unless blessed $package;
165    delete $Codemap{$package};
166
167    if (my $session = $poe_kernel->alias_resolve($package)) {
168        for my $alias ($poe_kernel->alias_list($session)) {
169            $poe_kernel->alias_remove($alias);
170        }
171    }
172}
173
174# This is bad, I call _data_alias_add -- a POE internal method!
175# Dont know any other way to do it...
176sub spawn {
177    my ($package, $alias) = @_;
178
179    my %SessionArgs = %{ $Session{$package} };
180
181    if (defined $alias) {
182        no strict "refs";
183        no warnings "redefine";
184
185        %{ $alias . "::" } = %{ $package . "::" };
186        createsession($alias);
187        return $alias;
188        for my $event (keys %{ $Codemap{$package} }) {
189            $SessionArgs{inline_states}{$event} =
190                inlinehandle $Codemap{$package}{$event}, $alias;
191        }
192    }
193
194    my $session = POE::Session->create(%SessionArgs);
195
196    my $session_id      = $session->ID;
197    my $object          = \$session_id;
198    $Heap{$session_id}  = $session->[0];
199
200    if (defined $alias) {
201        $Subroutine{$alias} = $Subroutine{$package};
202        $Codemap{$alias}    = $Codemap{$package};
203        $Object{$alias}     = $Object{$package};
204
205        no strict "refs";
206        no warnings "redefine";
207
208        $Session{$alias} = $Session{$package};
209
210        %{ $alias . "::" } = %{ $package . "::" };
211
212        createsession($alias);
213#        for my $symbol (keys %{ $package . "::" }) {
214#            $Codemap{$alias}{*{$symbol}{NAME}} =
215#                $Codemap{$package}{*{$symbol}{NAME}};
216#        }
217
218# I dont really know what the fuck I was thinking here...
219#        for my $symbol (values %{ $package . "::" }) {
220#            print $symbol;
221#            *{$symbol} = $Codemap{$package}{*{$symbol}{CODE}};
222#            *{$symbol} = eventcall *{$symbol}, $alias;
223#            $Codemap{$alias}{*{$symbol}{NAME}} =
224#                $Codemap{$package}{*{$symbol}{NAME}};
225#        }
226   
227        $poe_kernel->_data_alias_add($session, $alias);
228    }
229
230    if (defined wantarray) {
231        if ($alias) {
232            return $alias;
233        }
234        else {
235            return bless $object, $package;
236        }
237    }
238}
239
240sub createsession {   
241    my $package = @_ ? shift : $_;
242
243    my %Name;
244
245    {
246        no strict "refs";
247        no warnings "uninitialized";
248
249        for my $symbol (values %{ $package . "::" }) {
250            $Name{*{$symbol}{CODE}} = *{$symbol}{NAME};
251
252            no warnings "redefine";
253            if (defined $Session{$package}{inline_states}{*{$symbol}{CODE}}) {
254                $Codemap{$package}{*{$symbol}{NAME}} = *{$symbol}{CODE};
255                *{$symbol} = eventcall $symbol, $package;
256            }
257        }
258    }
259
260    for my $ref (keys %{ $Session{$package}{inline_states} }) {
261        $Session{$package}{inline_states}{$Name{$ref}} =
262            delete $Session{$package}{inline_states}{$ref};
263    }
264
265    my $x = 0;
266    for my $ref (@{ $Session{$package}{object_states} }) {
267        if (ref $ref eq "ARRAY") {
268            @{ $Session{$package}{object_states}[$x] } = map $Name{$_},
269                @{ $Session{$package}{object_states}[$x] };
270        }
271        $x++;
272    }
273
274    $x = 0;
275    for my $ref (@{ $Session{$package}{package_states} }) {
276        if (ref $ref eq "ARRAY") {
277            @{ $Session{$package}{package_states}[$x] } = map $Name{$_},
278                @{ $Session{$package}{package_states}[$x] };
279        }
280        $x++;
281    }
282   
283    my $start = 0;
284
285    $start++ if defined $Session{$package}{inline_states}{_start};
286
287    for my $package (@{$Session{$package}{package_states}}) {
288        if (ref $package eq "ARRAY") {
289            $start++ if grep m/_start/, @$package;
290        }
291    }
292
293    for my $object (@{$Session{$package}{object_states}}) {
294        if (ref $object eq "ARRAY") {
295            $start++ if grep m/_start/, @$object;
296        }
297    }
298
299    unless ($start) {
300        $Session{$package}{inline_states} ||= {};
301        $Session{$package}{inline_states}{_start} = sub {
302            $_[KERNEL]->alias_set($package);
303        };
304    }
305
306    spawn $package;
307}
308
309create POE::Session inline_states => {
310    _start => sub {
311        $_[KERNEL]->alias_set("MS_META");
312    },
313
314    create => sub {
315        createsession $_[ARG0];
316    },
317};
318
319sub import {
320    my $caller = caller;
321
322    eval "package $caller; POE::Session->import;";
323    eval "package $caller; POE::Kernel->import;";
324
325    importsymbols $caller;
326
327    $poe_kernel->post(MS_META => create => $caller);
328}
329
330INIT {
331    $RUNNING++;
332
333    POE::Kernel->run;
334};
335
336=head1 NAME
337
338  POE::Session::Magic - A very magical way to code in POE using attributes.
339
340=head1 SYNOPSIS
341
342 use POE::Session::Magic;
343
344 sub _start : Inline {
345     yield foo => "POE is running";
346 }
347 
348 sub foo : Inline {
349     print shift;
350 }
351 
352 sub _stop : Inline {
353     print "POE has been shut down...";
354 }
355
356=head1 RATIONALE
357
358I've spent a fair amount of time being a POE Advocate, telling people to use
359it for every task I could imagine would be appropriate.  I've heard quite
360a few times that its API is rather confusing in relation to the logic and
361mechanics of a POE application.  In fact, too many times.  Personally I really
362enjoy POE's API, but apparently its use of various unpopular Perl features is
363quite a bit for a new POE user to deal with.  This module is an attempt to
364address this issue.  The intent, is to make POE coding as straight forward as
365possible without confusing the user by doing many things they are unfamiliar
366with.  The goal is to make POE programs written almost exactly like average
367perl programs, without removing any of the features of POE.  Everything will
368be accessed in normal methods, and accessors will be provided for less commonly
369used aspects of the POE API.
370
371=head1 GOALS
372
373Subroutines automatically become events by having attributes.
374
375Packages automatically become sessions by using module.
376
377Sessions automatically create packages from session aliases.
378
379Arguements are just like normal arguements.
380
381Heap is standardized, easy to access, per session, and doesn't need passed
382around.
383
384Speed isn't hurt too bad, although it will be inherently.
385
386=head1 INTRODUCTION
387
388=over 4
389
390=item Using POE in a different way
391
392POE has quite a history.
393
394=back
395
396=head1 AUTHOR
397
398Scott S. McCoy (tag@cpan.org)
399
400=head1 THANKS
401
402Rocco as always, he provided some very useful insight while making design
403and semantic decisions for this module.
404
405=head1 SEE ALSO
406
407L<POE> L<POE::Session>
408
409=cut
410
4111;
Note: See TracBrowser for help on using the browser.