Skip to content

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also .

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also .
  • 14 commits
  • 20 files changed
  • 0 commit comments
  • 3 contributors
Commits on Aug 18, 2012
Tyler Riddle created benchmark suite for object delivery and consumption 5b3c101
@rcaputo Merge pull request #4 from triddle/master
Created benchmark suite for event delivery and consumption.

This will also help for profiling.
8011d01
Commits on Sep 27, 2012
@rcaputo Fix a typo that getty found. "BUIILD" isn't a real Moose method. 5090f76
@rcaputo Refactor the HTTP client example so that the client can be reused. 8a4f21a
@rcaputo Sartak's roles talk showed me how to make pluggable implementations. 4797a72
@rcaputo Create a consumer that tracks the number of pending HTTP reqeusts.
It's not ideal since POE::Component::Client::HTTP can be overloaded.
A more complete consumer would throttle the requests so too many
couldn't be started at once.
3fe11a8
@rcaputo This is a simpler version version of eg-22-multiple-http-requests.pl.
This one subclasses HttpClient and adds a pending() attribute to count
the requests currently in play.
5f75559
Commits on Sep 29, 2012
@rcaputo Try to illustrate a more common way to call back. 199cb74
Commits on Apr 21, 2013
@rcaputo [rt.cpan.org 81497] Make a couple attributes lazy to avoid hash order…
… issues.

Andreas Koenig reported a hash-order issue in Reflex.  I couldn't see
it, but Matt S. Trout pointed out that it's probably because some
trait attributes are too eager.  Here I make those traits lazy on his
recommendation.  Tests pass.  Will commend it to the CPAN testers for
further evaluation.
3d916a2
@rcaputo Fix a method name typo. 937c476
@rcaputo [rt.cpan.org 83718] Implement a start method for Reflex::Role::Interval.
Pointed out by CPAN user MBETHKE.
d9d928c
Commits on Apr 23, 2013
@kthakore kthakore Update intro.pod
Trying space so the first lines of this code is not missing when rendered on cpan/metacpan/github.
cffa017
@kthakore kthakore Cleaned spacing for the code sections
Cleaned up spacing for code sections and added for author tag in front of TODO. Not sure if the TODO is needed.
e696d6c
Commits on Jul 05, 2013
@rcaputo I'm told that "=for comment" is better than "=for author". e58f283
View
169 bench.pl
@@ -0,0 +1,169 @@
+#!/usr/bin/env perl
+
+use lib qw(bench/lib);
+
+use strict;
+use warnings;
+use Data::Dumper;
+
+$ENV{EVENT_SEND_COUNT} = 500000 unless exists $ENV{EVENT_SEND_COUNT};
+$ENV{MIN_TEST_TIME} = 60 unless exists $ENV{MIN_TEST_TIME};
+$ENV{BENCH_DIR} = 'bench' unless exists $ENV{BENCH_DIR};
+
+logger("Configuration: $ENV{EVENT_SEND_COUNT} events and $ENV{MIN_TEST_TIME} seconds minimum test time\n");
+
+my @results = doTests();
+my $error = shift(@results);
+doLog(@results);
+exit($error);
+
+exit(0);
+
+sub logger {
+ print STDERR @_;
+}
+
+sub genFiles {
+ my ($dir) = @_;
+ my $dh;
+ my @paths;
+
+ die "$dir is not a directory or is not accessable" unless -d $dir;
+
+ die "Could not opendir($dir): $!" unless opendir($dh, $dir);
+
+ foreach(readdir($dh)) {
+ next if m/^\./;
+ next if -d $_;
+
+ push(@paths, "$dir/$_");
+ }
+
+ die "Could not closedir($dir): $!" unless closedir($dh);
+
+ return @paths;
+}
+
+sub genTests {
+ my @files;
+ my @buf;
+
+ if (scalar(@ARGV) > 0) {
+ @files = @ARGV;
+ } else {
+ logger("Loading benchmarks from $ENV{BENCH_DIR}/... ");
+ @files = genFiles($ENV{BENCH_DIR});
+ logger("found ", scalar(@files), " files\n");
+ }
+
+ foreach(@files) {
+ my %test = ( file => $_ );
+
+ logger("loading $_... ");
+
+ $test{cb} = require $_;
+
+ logger("done\n");
+
+ push(@buf, \%test);
+ }
+
+ return @buf;
+}
+
+sub randomInt {
+ return int(rand(10));
+}
+
+sub genData {
+ my @buf = @_;
+
+ logger("Generating test data... ");
+
+ foreach(1 .. $ENV{EVENT_SEND_COUNT}) {
+ push(@buf, [ randomInt(), randomInt() ]);
+ }
+
+ logger("done\n");
+ return @buf;
+}
+
+sub sumData {
+ my $sum = 0;
+
+ foreach(@_) {
+ $sum += $_->[0] + $_->[1];
+ }
+
+ return $sum;
+}
+
+sub doTests {
+ my @tests = genTests();
+ my @testData = genData();
+ my $sum = sumData(@testData);
+ my $error = 0;
+
+ foreach(@tests) {
+ my $seconds = 0;
+ my $eventCount = 0;
+ my $iterations = 0;
+
+ logger("Measuring $_->{file}... ");
+
+ while(1) {
+ my ($results, $bench);
+
+ $iterations++;
+
+ $results = $_->{cb}(@testData);
+ $bench = $results->{bench};
+
+ if ($results->{sum} != $sum) {
+ logger("INVALID SUM $results->{sum} != $sum ");
+ $error = 1;
+ }
+
+ $seconds += $bench->[0];
+ $eventCount += $ENV{EVENT_SEND_COUNT};
+
+ if ($seconds >= $ENV{MIN_TEST_TIME}) {
+ last;
+ }
+
+ logger("$iterations ");
+ }
+
+ $_->{analysis} = {
+ cpuTime => $seconds,
+ eventCount => $eventCount,
+ eventsPerSecond => $eventCount / $seconds,
+ };
+
+ logger("done\n");
+ }
+
+ return ($error, @tests);
+}
+
+sub doLog {
+ my (@tests) = @_;
+
+ logger("Report format: file\tevents per second\tcost of solution\n");
+
+ foreach(sort({ $b->{analysis}->{eventsPerSecond} <=> $a->{analysis}->{eventsPerSecond} } @tests)) {
+ my $analysis = $_->{analysis};
+ my $eventsPerSecond = int($analysis->{eventsPerSecond});
+ our $fastest;
+ my $cost;
+
+ if (! defined($fastest)) {
+ $fastest = $eventsPerSecond;
+ }
+
+ $cost = $fastest / $eventsPerSecond;
+
+ print "$_->{file}\t$eventsPerSecond\t$cost\n";
+ }
+}
+
View
13 bench/lib/EventBench/Reflex/Event.pm
@@ -0,0 +1,13 @@
+package EventBench::Reflex::Event;
+
+use Moose;
+use Reflex;
+
+extends 'Reflex::Event';
+
+has arg1 => ( is => 'ro', isa => 'Num', required => 1 );
+has arg2 => ( is => 'ro', isa => 'Num', required => 1 );
+
+__PACKAGE__->meta->make_immutable;
+
+1;
View
27 bench/method-array.pl
@@ -0,0 +1,27 @@
+package EventBench::Method::Array;
+
+use strict;
+use warnings;
+use Benchmark ':hireswallclock';
+
+sub receive_event {
+ my ($arg1, $arg2) = @_;
+ our($sum);
+
+ $sum += $arg1 + $arg2;
+}
+
+return sub {
+ my (@testData) = @_;
+ our $sum = 0;
+ my $bench;
+
+ $bench = timeit(1, sub {
+
+ foreach(@testData) {
+ receive_event($_->[0], $_->[1]);
+ }
+ });
+
+ return { bench => $bench, sum => $sum };
+};
View
26 bench/method-hash.pl
@@ -0,0 +1,26 @@
+package EventBench::Method::Hash;
+
+use strict;
+use warnings;
+use Benchmark ':hireswallclock';
+
+sub receive_event {
+ my (%event) = @_;
+ our $sum;
+
+ $sum += $event{arg1} + $event{arg2};
+}
+
+return sub {
+ my (@testData) = @_;
+ our $sum = 0;
+ my $bench;
+
+ $bench = timeit(1, sub {
+ foreach(@testData) {
+ receive_event(arg1 => $_->[0], arg2 => $_->[1]);
+ }
+ });
+
+ return { bench => $bench, sum => $sum };
+};
View
32 bench/objectmethod-array.pm
@@ -0,0 +1,32 @@
+package EventBench::ObjectMethod::Array;
+
+use strict;
+use warnings;
+use Benchmark ':hireswallclock';
+
+sub new {
+ return bless({}, $_[0]);
+}
+
+sub receive_event {
+ my ($self, $arg1, $arg2) = @_;
+ our $sum;
+
+ $sum += $arg1 + $arg2;
+}
+
+return sub {
+ my (@testData) = @_;
+ my $test = EventBench::ObjectMethod::Array->new;
+ our $sum = 0;
+ my $bench;
+
+ $bench = timeit(1, sub {
+
+ foreach(@testData) {
+ $test->receive_event($_->[0], $_->[1]);
+ }
+ });
+
+ return { bench => $bench, sum => $sum };
+};
View
57 bench/objectmethod-cbmanager.pm
@@ -0,0 +1,57 @@
+package EventBench::ObjectMethod::CBManager;
+
+use strict;
+use warnings;
+use Benchmark ':hireswallclock';
+use Scalar::Util qw(weaken);
+
+sub new {
+ my $self = bless({ }, $_[0]);
+ our $sum;
+
+ $self->{handlers}->{sum} = $self->weakcb(sub {
+ my ($self, %event) = @_;
+
+ $sum += $event{arg1} + $event{arg2};
+ });
+
+ return $self;
+}
+
+sub receive_event {
+ my ($self, %event) = @_;
+
+ $self->{handlers}->{$event{name}}->($self, %event);
+}
+
+#pass in code ref or method name as string
+sub weakcb {
+ my ($self, $cb) = @_;
+
+ weaken($self);
+
+ return sub {
+ my ($self) = shift(@_);
+
+ die "expected weak reference to self to be valid but it was undefined" unless defined $self;
+
+ return $self->$cb(@_);
+ };
+}
+
+return sub {
+ my (@testData) = @_;
+ my $test = EventBench::ObjectMethod::CBManager->new;
+ my $bench;
+ our $sum;
+
+ $bench = timeit(1, sub {
+ our $sum = 0;
+
+ foreach(@testData) {
+ $test->receive_event(name => 'sum', arg1 => $_->[0], arg2 => $_->[1]);
+ }
+ });
+
+ return { bench => $bench, sum => $sum };
+};
View
31 bench/objectmethod-hash.pm
@@ -0,0 +1,31 @@
+package EventBench::ObjectMethod::Hash;
+
+use strict;
+use warnings;
+use Benchmark ':hireswallclock';
+
+sub new {
+ return bless({}, $_[0]);
+}
+
+sub receive_event {
+ my ($self, %event) = @_;
+ our $sum;
+
+ $sum += $event{arg1} + $event{arg2};
+}
+
+return sub {
+ my (@testData) = @_;
+ my $test = EventBench::ObjectMethod::Hash->new;
+ our $sum = 0;
+ my $bench;
+
+ $bench = timeit(1, sub {
+ foreach(@testData) {
+ $test->receive_event(arg1 => $_->[0], arg2 => $_->[1]);
+ }
+ });
+
+ return { bench => $bench, sum => $sum };
+};
View
8 docs/TODO.otl
@@ -810,6 +810,14 @@
[_] 0% Do we need call() at all?
[_] 0% Explore IO::Lambda's cheat.
[_] 0% Consolidate POE-specific code out into a single role.
+ From Sartak's slides on roles:
+ | package Database;
+ |
+ | has backend => (
+ | is => 'ro',
+ | does => 'Backend::Transactional',
+ | handles => 'Backend::Transactional',
+ | );
Allows other event loops to be supported directly rather than through POE.
me> Let's say I have a class Mumble that's a generic API for something with platform-specific implementations. I have Mumble::Yay and Mumble::Boo with the bits that the Yay! and Boo! platforms need.
me> I want to use Mumble, and Mumble->putty() to do the putty() thing regardless how the platform needs to putty mumbles.
View
15 docs/intro/intro.pod
@@ -62,7 +62,9 @@ are named after this key by default.
That's a bit of work, but roles are generally the biggest Reflex
modules. The classes that use it are much smaller.
+=for comment
TODO - Changed.
+
package AfterAwhileRole;
use Reflex::Role;
@@ -100,7 +102,9 @@ All Reflex classes should be based on Reflex::Base. The class exposes
"name" and "awhile" attributes so they may be set during object
construction.
+=for comment
TODO - Changed.
+
package AfterAwhileClass;
use Moose;
extends 'Reflex::Base';
@@ -121,6 +125,7 @@ classes. Here's a subclass of AfterAwhileClass that internally
consumes its on_done() callback. Even though Moose would makethis a
bit shorter, we deliberately avoid it because we can.
+
package AfterAwhileSubclass;
use warnings;
@@ -214,7 +219,9 @@ returns the next asynchrnous event they emit.
First, here's how it's done:
-TODO - Changed.
+=for comment
+Changed.
+
#!/usr/bin/env perl
use warnings;
@@ -230,7 +237,9 @@ TODO - Changed.
We need to define AsyncAwhileClass before that will run. It's just a
renamed AfterAwhileClass, with the new asynchronous role.
-TODO - Changed.
+=for comment
+Changed.
+
package AsyncAwhileClass;
use Moose;
extends 'Reflex::Base';
@@ -247,7 +256,9 @@ This AsyncAwhileRole replaces the blocking sleep() with a non-blocking
Reflex::Interval object. Later we'll introduce magic to make it more
concise.
+=for comment
TODO - Changed.
+
package AsyncAwhileRole;
use Reflex::Role;
use Reflex::Interval;
View
79 eg/HttpClient.pm
@@ -0,0 +1,79 @@
+package HttpClient;
+
+use Moose;
+extends 'Reflex::Base';
+
+use POE::Component::Client::HTTP;
+use Reflex::POE::Event;
+use HttpResponseEvent;
+
+has alias => (
+ is => 'ro',
+ isa => 'Str',
+ default => 'user-agent',
+);
+
+sub BUILD {
+ my ($self) = @_;
+
+ # Start an HTTP user-agent when the object is created.
+ #
+ # A more complete example would expose all of PoCo::Client::HTTP's
+ # configuration options as attributes.
+
+ POE::Component::Client::HTTP->spawn(Alias => $self->alias());
+}
+
+sub DESTRUCT {
+ my ($self) = @_;
+
+ # Shut down POE::Component::Client::HTTP when this object is
+ # destroyed.
+
+ POE::Kernel->post(ua => $self->alias());
+}
+
+sub request {
+ # Make a request.
+
+ my ($self, $http_request) = @_;
+
+ # There is no guarantee that the caller of request() is running in
+ # the same POE session as this HttpClient object.
+ #
+ # Reflex::Base's run_within_session() method makes sure that the
+ # right session is active when interacting with POE code. This
+ # ensures that POE-based responses are properly routed.
+
+ # The Reflex::POE::Event object created here is an event for POE's
+ # purpose, but it includes Reflex magic to route responses back to
+ # the correct Reflex object.
+
+ $self->run_within_session(
+ sub {
+ POE::Kernel->post(
+ $self->alias(),
+ 'request',
+ Reflex::POE::Event->new(
+ object => $self,
+ method => 'internal_http_response',
+ ),
+ $http_request,
+ );
+ }
+ );
+}
+
+sub internal_http_response {
+ my ($self, $args) = @_;
+
+ my ($request, $response) = @{ $args->{response} };
+ $self->emit(
+ -type => 'HttpResponseEvent',
+ -name => 'response',
+ request => $request->[0],
+ response => $response->[0],
+ );
+}
+
+1;
View
24 eg/HttpResponseEvent.pm
@@ -0,0 +1,24 @@
+package HttpResponseEvent;
+
+use Moose;
+extends 'Reflex::Event';
+
+use HTTP::Request;
+use HTTP::Response;
+
+has request => (
+ is => 'ro',
+ isa => 'HTTP::Request',
+ required => 1,
+);
+
+has response => (
+ is => 'ro',
+ isa => 'HTTP::Response',
+ required => 1,
+);
+
+__PACKAGE__->make_event_cloner;
+__PACKAGE__->meta->make_immutable;
+
+1;
View
105 eg/eg-21-poco-client-http.pl
@@ -5,109 +5,8 @@
use strict;
use lib qw(../lib);
-# Define an HTTP response event.
-
-{
- package Reflex::Event::HttpResponse;
- use Moose;
- extends 'Reflex::Event';
-
- use HTTP::Request;
- use HTTP::Response;
-
- has request => (
- is => 'ro',
- isa => 'HTTP::Request',
- required => 1,
- );
-
- has response => (
- is => 'ro',
- isa => 'HTTP::Response',
- required => 1,
- );
-}
-
-# Wrap POE::Component::Client::HTTP in a Reflex object.
-
-{
- package HttpClient;
-
- use Moose;
- extends 'Reflex::Base';
-
- use POE::Component::Client::HTTP;
- use Reflex::POE::Event;
-
- has alias => (
- is => 'ro',
- isa => 'Str',
- default => 'user-agent',
- );
-
- sub BUILD {
- my ($self) = @_;
-
- # Start an HTTP user-agent when the object is created.
- #
- # A more complete example would expose all of PoCo::Client::HTTP's
- # configuration options as attributes.
-
- POE::Component::Client::HTTP->spawn(Alias => $self->alias());
- }
-
- sub DESTRUCT {
- my ($self) = @_;
-
- # Shut down POE::Component::Client::HTTP when this object is
- # destroyed.
-
- POE::Kernel->post(ua => $self->alias());
- }
-
- sub request {
- # Make a request.
-
- my ($self, $http_request) = @_;
-
- # There is no guarantee that the caller of request() is running in
- # the same POE session as this HttpClient object.
- #
- # Reflex::Base's run_within_session() method makes sure that the
- # right session is active when interacting with POE code. This
- # ensures that POE-based responses are properly routed.
-
- # The Reflex::POE::Event object created here is an event for POE's
- # purpose, but it includes Reflex magic to route responses back to
- # the correct Reflex object.
-
- $self->run_within_session(
- sub {
- POE::Kernel->post(
- $self->alias(),
- 'request',
- Reflex::POE::Event->new(
- object => $self,
- method => 'internal_http_response',
- ),
- $http_request,
- );
- }
- );
- }
-
- sub internal_http_response {
- my ($self, $args) = @_;
-
- my ($request, $response) = @{ $args->{response} };
- $self->emit(
- -type => 'Reflex::Event::HttpResponse',
- -name => 'response',
- request => $request->[0],
- response => $response->[0],
- );
- }
-}
+# HttpClient is a wrapper for POE::Component::Client::HTTP.
+use HttpClient;
### Main usage.
View
67 eg/eg-22-multiple-http-requests.pl
@@ -0,0 +1,67 @@
+#!/usr/bin/env perl
+# vim: ts=2 sw=2 noexpandtab
+
+use warnings;
+use strict;
+use lib qw(../lib);
+
+{
+ package ConcurrentHttpClient;
+ use Moose;
+ extends 'Reflex::Base';
+
+ use HttpClient;
+ use Reflex::Trait::Watched qw(watches);
+
+ watches http_client => (
+ is => 'ro',
+ isa => 'HttpClient',
+ default => sub { HttpClient->new() },
+ );
+
+ has pending => (
+ is => 'rw',
+ isa => 'Int',
+ default => 0,
+ );
+
+ sub requests {
+ my ($self, @http_requests) = @_;
+
+ foreach my $request (@http_requests) {
+ $self->http_client()->request( $request );
+ }
+
+ $self->pending( $self->pending() + @http_requests );
+ }
+
+ sub on_http_client_response {
+ my ($self, $response) = @_;
+ $self->re_emit( $response, -name => 'response' );
+
+ return if $self->pending( $self->pending() - 1 );
+
+ $self->emit( -name => "empty" );
+ };
+}
+
+### Main usage.
+
+use HTTP::Request;
+
+my $client = ConcurrentHttpClient->new();
+$client->requests(
+ map { HTTP::Request->new( GET => $_ ) }
+ 'http://poe.perl.org',
+ 'http://duckduckgo.com/',
+ 'http://metacpan.org/',
+ 'http://perl.org/',
+ 'http://twitter.com/',
+);
+
+while (my $event = $client->next()) {
+ last if $event->_name() eq 'empty';
+ print $event->response()->code(), " = ", $event->request()->uri(), "\n";
+}
+
+exit;
View
49 eg/eg-23-multiple-http-subclass.pl
@@ -0,0 +1,49 @@
+#!/usr/bin/env perl
+# vim: ts=2 sw=2 noexpandtab
+
+use warnings;
+use strict;
+use lib qw(../lib);
+
+{
+ package ConcurrentHttpClient;
+ use Moose;
+ extends 'HttpClient';
+
+ has pending => (
+ is => 'rw',
+ isa => 'Int',
+ default => 0,
+ );
+
+ after request => sub {
+ my ($self) = @_;
+ $self->pending( $self->pending() + 1 );
+ };
+
+ after internal_http_response => sub {
+ my ($self) = @_;
+ return if $self->pending( $self->pending() - 1 );
+ $self->emit( -name => "empty" );
+ };
+}
+
+### Main usage.
+
+use HTTP::Request;
+
+my $client = ConcurrentHttpClient->new();
+$client->request( HTTP::Request->new( GET => $_ ) ) foreach (
+ 'http://poe.perl.org',
+ 'http://duckduckgo.com/',
+ 'http://metacpan.org/',
+ 'http://perl.org/',
+ 'http://twitter.com/',
+);
+
+while (my $event = $client->next()) {
+ last if $event->_name() eq 'empty';
+ print $event->response()->code(), " = ", $event->request()->uri(), "\n";
+}
+
+exit;
View
38 eg/eg-24-client-http-callbacks.pl
@@ -0,0 +1,38 @@
+#!/usr/bin/env perl
+# vim: ts=2 sw=2 noexpandtab
+
+use warnings;
+use strict;
+use lib qw(../lib);
+
+# HttpClient is a wrapper for POE::Component::Client::HTTP.
+use HttpClient;
+
+### Main usage.
+
+use HTTP::Request;
+
+# 1. Create a user-agent object.
+
+my $ua = HttpClient->new(
+ on_response => sub {
+ my ($self, $event) = @_;
+ print $event->response()->code(), " = ", $event->request->uri(), "\n";
+ },
+);
+
+# 2. Send a request.
+
+$ua->request( HTTP::Request->new( GET => $_ ) ) foreach (
+ 'http://poe.perl.org',
+ 'http://duckduckgo.com/',
+ 'http://metacpan.org/',
+ 'http://perl.org/',
+ 'http://twitter.com/',
+);
+
+# 3. Wait for stuff.
+
+Reflex->run_all();
+
+exit;
View
2 eg/eg-33-all-callbacks.pl
@@ -103,7 +103,7 @@
is => 'rw',
);
- sub BUIILD {
+ sub BUILD {
my $self = shift;
$self->ticker(
Reflex::Interval->new(
View
2 lib/Reflex/Role/Encoding.pm
@@ -33,7 +33,7 @@ role {
$message->is_combinable() and
$self->messages()->[-1]->is_combinable()
) {
- $self->messages()->[-1]->absorb($message);
+ $self->messages()->[-1]->append($message);
return;
}
View
30 lib/Reflex/Role/Interval.pm
@@ -27,6 +27,7 @@ role {
has $att_auto_repeat => ( is => 'ro', isa => 'Bool', default => 1 );
has $att_auto_start => ( is => 'ro', isa => 'Bool', default => 1 );
+ my $method_start = $p->method_start();
my $method_repeat = $p->method_repeat();
my $method_stop = $p->method_stop();
@@ -71,6 +72,35 @@ role {
);
};
+ method $method_start => sub {
+ my ($self, $args) = @_;
+
+ # Nothing to start if the timer is already going.
+ if ($self->$timer_id_name()) {
+ carp($method_start . "() called on a running timer");
+ return;
+ }
+
+ # Switch to the proper session.
+ return unless (
+ defined $self->$att_interval() and $self->call_gate($method_repeat)
+ );
+
+ # Put a weak $self in an envelope that can be passed around
+ # without strenghtening the object.
+
+ my $envelope = [ $self, $cb_tick, 'Reflex::Event::Interval' ];
+ weaken $envelope->[0];
+
+ $self->$timer_id_name(
+ $POE::Kernel::poe_kernel->delay_set(
+ 'timer_due',
+ $self->$att_interval(),
+ $envelope,
+ )
+ );
+ };
+
after DEMOLISH => sub {
my ($self, $args) = @_;
$self->$method_stop();
View
1 lib/Reflex/Trait/EmitsOnChange.pm
@@ -77,6 +77,7 @@ has initializer => (
has event => (
isa => 'Str',
is => 'ro',
+ lazy => 1,
default => sub {
my $self = shift;
return $self->name();
View
1 lib/Reflex/Trait/Watched.pm
@@ -82,6 +82,7 @@ has initializer => (
has role => (
isa => 'Str',
is => 'ro',
+ lazy => 1,
default => sub {
my $self = shift;
return $self->name();

No commit comments for this range

Something went wrong with that request. Please try again.