Yeah, I'm a big fan of Legos. When I was a kid, I never got a complete set. I had /some/, mixed with pieces I got from sharing my toys with cousins, sibling, or friends. The write-up that follows is concrete evidence that building with modular components is now deeply ingrained, and I tend to view Perl modules in the same context.
The basic premise of this concept hangs completely on the fact that POE states/events can be added and removed during execution. The rest is simple architecture and code handling, coupled with eval. As Rocco comments, the whole thing is deceptively simple.
Note: I stole some chunks of the code below from the IRC component modules, because they're fabulous.
#!/usr/bin/perl use strict; use warnings; use POE; POE::Session->create( package_states => ['main' => [qw(_default _start _reload)],]); $poe_kernel->run(); exit 0; sub _start { my ($kernel, $session) = @_[KERNEL, SESSION]; $kernel->yield('_reload'); # Load event code $kernel->sig(HUP => 'sig_hup') ; # Hitch sig HUP to the wagon, call a code reload whenever we get one $kernel->yield('init'); # Call externally loaded init for dynamic setup } sub _reload { my ($kernel, $session) = @_[KERNEL, SESSION]; foreach my $subroutine (<events/*>) { my $code; chomp($subroutine); open(my $fh_in, '<', $subroutine); while (<$fh_in>) { $code .= $_ } close($fh_in); $subroutine =~ s/^events\///; if (my $new_state = eval q(sub { my ($kernel, $heap) = @_[KERNEL, HEAP]; ) . $code . q(})) { $kernel->state($subroutine, $new_state); } } } sub _default { my ($kernel, $session) = @_[KERNEL, SESSION]; my ($event, $args) = @_[ARG0 .. $#_]; my @output = ("$event: "); foreach my $arg (@$args) { if (ref($arg) eq 'ARRAY') { push(@output, "[" . join(" ,", @$arg) . "]"); } else { push(@output, "'$arg'"); } } print STDOUT 'Default: ' . join ' ', @output, " (session $session)\n"; return 0; }
And that's it! At least, that's the basic premise. Some things to keep in mind:
- Scoping grows teeth in this environment.
- You're modifying code that's running concurrently.
- eval() is your friend. Compartmentalizing code that may or not have been syntax or error checked (and reporting of $@) will save you some pain.
- Code management in a db context is sometimes a problem, depending on how you set things up. I officially declare it to be "your problem." As most people manage their code differently (if at all), I leave this as an exercise of the implementor.
- Rocco notes 'eval "sub { $code }"' has historically had issues leaking memory. Caveat utilitor.
-- billn <billn at billn.net>