1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
|
package POEx::Role::SessionInstantiation;
BEGIN {
$POEx::Role::SessionInstantiation::VERSION = '1.101040';
}
use MooseX::Declare;
#ABSTRACT: A Moose Role for turning objects into POE Sessions
role POEx::Role::SessionInstantiation with MooseX::CompileTime::Traits
{
with 'POEx::Role::SessionInstantiation::Meta::Session::Magic';
with 'POEx::Role::SessionInstantiation::Meta::Session::Implementation';
with 'POEx::Role::SessionInstantiation::Meta::Session::Events';
with 'POEx::Role::SessionInstantiation::Meta::Session::Sugar';
}
1;
=pod
=head1 NAME
POEx::Role::SessionInstantiation - A Moose Role for turning objects into POE Sessions
=head1 VERSION
version 1.101040
=head1 DESCRIPTION
POEx::Role::SessionInstantiation provides a nearly seamless integration for
non-POE objects into a POE environment. It does this by handling the POE stuff
behind the scenes including allowing per instances method changes, session
registration to the Kernel, and providing some defaults like setting an alias
if supplied via the attribute or constructor argument, or defining a _default
that warns if your object receives an event that it does not have.
This role exposes your class' methods as POE events.
=head1 SYOPSIS
package My::Class;
use 5.010;
use MooseX::Declare;
class My::Class
{
# using the role instantly makes it a POE::Session upon instantiation
with 'POEx::Role::SessionInstantiation';
# alias the decorator
use aliased 'POEx::Role::Event';
# decorated methods are all exposed as events to POE
method foo (@args) is Event
{
# This event is not only added through POE but also added as a
# method to each instance that happens to have 'foo' fired
# Access to POE information is done through the 'poe' accessor
$self->poe->kernel->state
(
'added_event',
sub
{
say 'added_event has fired'
}
);
# Some sugar to access the kernel's yield method
# This will push the 'bar' method into POE's event queue
$self->yield('bar');
}
method bar (@args)
{
# $self is also safe to pass as a session reference
# Or you can pass along $self->ID()
$self->post($self, 'baz')
}
method baz (@args)
{
# call also works too
$self->call($self, 'added_event';
}
}
# Constructing the session takes all the normal options
my $session = My::Class->new({ options => { trace => 1 } });
# Still need to call ->run();
POE::Kernel->run();
=head1 NOTES
Like all dangerous substances, this Role needs a big fat warning. It should be
noted thoroughly that this Role performs some pretty heinous magic to
accomplish a polished and consistent transformation of your class into a
Session.
=over 4
=item PER INSTANCE METHOD CHANGES
This Role enables your /objects/ to have method changes. You read that right.
POE allows Sessions to have runtime event handler modification. It is sort of
required to support wheels and whatever. Anyhow, to support that functionality
class level changes are executed via Moose::Meta::Class to add/change/remove
methods as events are added, changed, and removed via POE. But how is that
possible, you ask, to make class level changes without affecting all of the
other instances? An anonymous clone of the composed class is created and the
object is reblessed into that new clone that has changes for each change to the
events that occurs. This segregates changes so that they only affect the
individual object involved.
This functionality should likely be broken out into its own evil module, but
that is a job for another day.
=item BREAKING POE ENCAPSULATION
POE internally tracks Sessions by their stringified reference. So how do make
changes to references, such as reblessing them into different classes, and not
break POE? You do some scary crap. Stringification is overloaded (via overload
pragma) to return the original string from the instance before changes are made
to it and it is reblessed. The original string is stored in the orig attribute.
POE also does reference comparisons as well to check if the current session is
the same as the one it just got and so != and == are also overloaded to do
string comparisons of references. But what about the reference that is already
stored by POE? The reference is overwritten in one spot (where POE stores its
Sessions) and is done every time an event change takes place.
=item OVERLOAD PRAGMA IN A ROLE? WTF?
Moose does the right thing, mostly, when it comes to the overload pragma in a
Role. The methods defined are composed appropriate, but the magic doesn't make
it through the composition. So the magic must be enabled manually. This
includes messing with the symbol table of the composed class. This happens
inside the after 'BUILD' advice, and also during event handler changes from POE
(the anonymous classes need to have the magic enabled each time). So what is
the moral to this? If you need to overload "", !=, or == in your composed class
things will likely break. You have been warned.
=back
So please heed the warnings and don't blame me if this summons the terrasque
into your datacenter and you left your +5 gear at home.
=head1 FURTHER NOTES
=head2 CUSTOMIZING VIA TRAITS
POEx::Role::SessionInstantiation now allows for Trait declarations upon import.
This is similar to how Moose itself allows for modification of its own Meta
things through arguments passed to use (ie. use Moose -traits => qw /Foo/), but
allows for parameterized roles. Below are some examples of using traits to
modify POEx::Role::SessionInstantiation's default behavior.
First let's declare a role that frobinates something at start:
use MooseX::Declare;
role FrobAtStart
{
with 'POEx::Role::SessionInstantiation::Meta::Session::Events';
has frobinator =>
(
is => 'ro',
isa => 'Object',
required => 1,
handles =>
{
'frob' => 'frob'
}
);
after _start is POEx::Role::Event
{
$self->frob();
}
}
And how about a logger role that logs unknown delivered events that wants the
logging method/event to be a named parameter
role SomeLogger(Str :$foo)
{
with 'POEx::Role::SessionInstantiation::Meta::Session::Events';
has logger =>
(
is => 'ro',
isa => 'Object',
required => 1
);
method $foo(Str $event) is POEx::Role::Event
{
$self->logger->log("Unknown event: $event")
}
after _default is POEx::Role::Event
{
$self->$foo($self->poe->state);
}
}
Now let's use them
class My::Session
{
# need to make sure these are loaded
use FrobinateAtStart;
use SomeLogger;
# and now the magic
use POEx::Role::SessionInstantiation
traits => [ 'FrobAtStart', SomeLogger => { foo => 'log' } ];
# compose it now that it has traits applied
with 'POEx::Role::SessionInstantiation';
...
}
For more information on how this mechanism works, please see
MooseX::CompileTime::Traits
=head2 WRITING YOUR OWN TRAITS
To make it easy to advise just little parts of POEx::Role::SessionInstantiation
it is broken down into a few different roles that you can 'with' like in the
examples above.
=over 4
=item POEx::Role::SessionInstantiation::Meta::Session::Magic
This is where the voodoo happens to turn your objects into sessions.
=item POEx::Role::SessionInstantiation::Meta::Session::Events
Here are the default events such as _start, _stop, _default, etc.
=item POEx::Role::SessionInstantiation::Meta::Session::Sugar
This role holds the delegated methods from POE::Kernel (post, yield, call)
=item POEx::Role::SessionInstantiation::Meta::Session::Implementation
And this is the implementation piece that implements the POE::Session
interface that lets POE interact with our sessions
=back
Please see their POD for more details on the inner workings of this module.
=head1 AUTHOR
Nicholas Perez <nperez@cpan.org>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2010 by Nicholas Perez.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut
__END__
|