Hello, Jason, thanks for the feedback!

Omnitrigger.pm is intended as an alternative to standard Moose triggers, providing trigger functionality more in line with (what I suspect are) most folk's expectations.

Aside from the order and type of arguments provided to the callback, omnitriggers and standard triggers have the same interface:

   has foo => (is => 'rw', isa => 'Str', trigger => sub { die('POW!') });

has bar => (is => 'rw', isa => 'Str', omnitrigger => sub { die('POW!') });

Unlike standard triggers, omnitriggers will fire on any initialization, setting, or clearing of the attribute value, and they're protected from recursion. (And they eliminate a couple of very esoteric bugs I found with standard triggers.)

I'd come across Observable in my searches, but missed Listenable; though it seems the two are fairly similar. They're both roles implementing the observer pattern -- they're both intended to detect calls to methods in the consuming class in a generic way. What they're not intended to do is detect *any* change of an *attribute's* value -- they can't, because attributes' values aren't always changed directly via methods in the consuming class, particularly during construction, cloning, rebless fixup, and lazy population, and in inlined stuff.

Omnitriggers, on the other hand, like standard triggers, do their "listening" deeper in the bowels of things to intercept attribute changes as close to the Instance interface as possible, in as few places as possible. A (contrived) example may help:

#===================================================================================================

{ package OmniTrigClass;

   use Moose;
   use MooseX::OmniTrigger;

has foo => (is => 'rw', isa => 'Str', default => 'FOO', omnitrigger => \&_callback); has bar => (is => 'rw', isa => 'Str', default => 'BAR', lazy => 1, omnitrigger => \&_callback);

has baz => (is => 'rw', isa => 'Str', accessor => 'access_baz', reader => 'read_baz', writer => 'write_baz', clearer => 'clear_baz', omnitrigger => sub {

       my ($self) = shift;

       $self->_callback(@_);

       $self->write_baz('RECURSE, DAMN YOU');
   });

   sub _callback { say("OMNITRIGGERED FOR $_[1]") }
}

my $omnitriggered = OmniTrigClass->new;
# OMNITRIGGERED FOR foo -- default got set

$omnitriggered->bar;
# OMNITRIGGERED FOR bar -- lazy default got set

$omnitriggered->meta->get_attribute('foo')->set_value($omnitriggered, 'FOO!!!');
# OMNITRIGGERED FOR foo -- set through the attribute interface

$omnitriggered->write_baz('BAZ???');
# OMNITRIGGERED FOR baz

say $omnitriggered->read_baz;
# RECURSE, DAMN YOU

#===================================================================================================

{ package ObservableClass;

   use Moose;

   has foo => (is => 'rw', isa => 'Str', default => 'FOO');
   has bar => (is => 'rw', isa => 'Str', default => 'BAR', lazy => 1);

has baz => (is => 'rw', isa => 'Str', accessor => 'access_baz', reader => 'read_baz', writer => 'write_baz', clearer => 'clear_baz');

# WE HAVE TO BE EXPLICIT ABOUT WHAT WE'RE OBSERVING. THIS LIST COULD GET BIG.
   with 'MooseX::Observer::Role::Observable' => {notify_after => [qw(

       foo
       bar
       access_baz
       read_baz
       write_baz
       clear_baz
   )]};
}

{ package ObserverClass;

   use Moose;
       with 'MooseX::Observer::Role::Observer';

   sub update {

       say("OBSERVED $_[3]");

       # WE HAVE TO MAINTAIN OUR OWN "DISPATCHER" HERE.
$_[1]->write_baz('RECURSE, DAMN YOU') if $_[3] =~ /^(?:access_baz|read_baz|write_baz|clear_baz)$/;
   }
}

my $observable = ObservableClass->new;
# <NOTHING>

$observable->add_observer(ObserverClass->new);

$observable->bar;
# <NOTHING>

$observable->meta->get_attribute('foo')->set_value($observable, 'FOO!!!');
# <NOTHING>

$observable->write_baz('BAZ???');
# OBSERVED write_baz
# OBSERVED write_baz
# OBSERVED write_baz
# ...INIFINITE RECURSION

#===================================================================================================







-----Original Message----- From: Jason Galea
Sent: Sunday, December 18, 2011 9:36 PM
To: Todd Lorenz
Cc: moose@perl.org
Subject: Re: MooseX::OmniTrigger

On Mon, Dec 19, 2011 at 2:02 PM, Todd Lorenz <trlor...@hotmail.com> wrote:


So far as I’ve been able to find, there currently aren’t any other modules
like this one, so I’d like to make it available on the CPAN. But first I’d
like your feedback, if you’ve got the time to give it.

https://github.com/trlorenz/MooseX-OmniTrigger


Hi Todd,

have you seen these?

https://metacpan.org/search?q=moose+observable

MooseX::Observer::Role::Observable
MooseX::Role::Listenable

Observable looks like it might be close, it's brand new too..

are they similar? or what are the differences? (alway good to explain in
your docs)

cheers,

J



--

Jason Galea
Web Developer

Ph 07 40556926
Mob 04 12345 534
www.eightdegrees.com.au

-----Original Message----- From: Jason Galea
Sent: Sunday, December 18, 2011 9:36 PM
To: Todd Lorenz
Cc: moose@perl.org
Subject: Re: MooseX::OmniTrigger

On Mon, Dec 19, 2011 at 2:02 PM, Todd Lorenz <trlor...@hotmail.com> wrote:


So far as I’ve been able to find, there currently aren’t any other modules
like this one, so I’d like to make it available on the CPAN. But first I’d
like your feedback, if you’ve got the time to give it.

https://github.com/trlorenz/MooseX-OmniTrigger


Hi Todd,

have you seen these?

https://metacpan.org/search?q=moose+observable

MooseX::Observer::Role::Observable
MooseX::Role::Listenable

Observable looks like it might be close, it's brand new too..

are they similar? or what are the differences? (alway good to explain in
your docs)

cheers,

J



--

Jason Galea
Web Developer

Ph 07 40556926
Mob 04 12345 534
www.eightdegrees.com.au

Reply via email to