NAME Black::Board - publish messages and subscribe to topics VERSION version 0.0001 WARNING WARNING WARNING This module is currently considered alpha quality code by it's author, the current maintainer. This means that anything can change in the next minor version release. Use at your own risk! SYNOPSIS use Log::Dispatch; use Black::Board; my $publisher = Black::Board::Publisher->new; my $log_topic = Black::Board::Topic->new( name => "LogDispatch" ); $publisher->add_topic( $log_topic ); my $logger = Log::Dispatch->new( outputs => [ [ Screen => ( 'min_level' => 'debug' ) ] ] ); $log_topic->register_subscriber( Black::Board::Subscriber->new( subscription => sub { # $_ here is the Black::Board::Message object # which you can get explicitly from @_ if ( $logger->would_log( $_->params->{level} ) ) { $logger->log( %{ $_->params } ); # Let the caller have a way to check if we logged return $_->merge_params( { log_sent_for => $_->params->{level} }, ); } return $_->cancel_bubble; } ) ); $log_topic->register_subscriber( Black::Board::Subscriber->new( subscription => sub { return $_->merge_params( { message => '[Prefix] ' . $_->params->{message} } ) } ) ); my $other_logger = Log::Dispatch->new( outputs => [ [ File => ( 'filename' => 'intercepted-error.log' ) ] ] ); $log_topic->register_subscriber( Black::Board::Subscriber->new( subscription => sub { if ( $other_logger->would_log( $_->params->{level} ) ) { $other_logger->log( %{ $_->params } ); # Let the caller have a way to check if we logged return $_->merge_params( { other_log_sent_for => $_->params->{level} }, ); } return $_; } ) ); $publisher->publish( topic => 'LogDispatch', message => { params => { message => "Something that needs logging", level => "alert" } } ); # -- OR -- # # any arguments beyond the first are passed off to subscriber my $logger; topic LogDispatch => # Called the next time a message is delivered # only called once initialize => [ sub { require Log::Dispatch; $logger = Log::Dispatch->( outputs => [ [ Screen => ( 'min_level' => 'debug' ) ] ] ); } ], subscribe => [ sub { my $m = $_; if ( $logger->would_log( $m->params->{level} ) ) { $logger->log( %{ $m->params } ); # Let the caller have a way to check if we logged return $m->merge_params( { log_sent_for => $m->params->{level} }, ); } return $m->cancel_bubble; } ]; my $other_logger; topic LogDispatch => # Called the next time a message is delivered # only called once initialize => [ ` sub { $other_logger = Log::Dispatch->new( outputs => [ [ File => ( 'filename' => 'intercepted-error.log' ) ] ] ); } ]; subscriber LogDispatch => sub { my $m = $_; if ( $other_logger->would_log( $m->params->{level} ) ) { $other_logger->log( %{ $m->params } ); # Let the caller have a way to check if we logged $m = $m->clone_with_params( { other_log_sent_for => $m->params->{level} } ); } return $m; }; subscriber LogDispatch => sub { return $_->clone_with_params( { message => '[Prefix] ' . $_->params->{message} } ) }; publish LogDispatch => message => "Something that needs logging", level => "alert" # -params has precedence -params => { # level is now changed to debug level => "debug", more => "parameters merged with precedence" }; DESCRIPTION This code is inspired by Bread::Board and even a few bits were stolen from it. The purpose of this module is to provide a publisher/subscriber interface for passing messages. This subscriber interface has the ability for subscribers to act as filters on the message. Each subscriber can return a modified copy of the message. The message is cloned because the same message object should be able to be sent on multiple dispatch chains. ATTRIBUTES "Publisher" This is the singleton Publisher object. You can set this to a different Publisher object but you should do this before you start declaring Topics or be prepared to copy the previously registered Topics into the new object. "SubscriberClass" Used to create a "Subscriber" object when one is needed. Defaults to Black::Board::Subscriber. Can be changed to a custom topic class name for extending Black::Board. "TopicClass" Used to create a "Topic" object when one is needed. Defaults to Black::Board::Topic. Can be changed to a custom topic class name for extending Black::Board. "PublisherClass" Used to create a "Publisher" object when one is needed. Defaults to Black::Board::Publisher. Can be changed to a custom topic class name for extending Black::Board. FUNCTIONS "topic" First argument is the topic name to create, any additional arguments are passed off to "METHODS/subscriber" as new subscription callbacks. If the topic name already exists in the singleton "CLASS ATTRIBUTES/Publisher": 1 If subscribers are specified, the subscribers will be subscribed to the already existing topic. 2 If no subscribers are specified this topic call is an apparent no-op but does ensure the topic has been created "subscriber" Create a new Black::Board::Subscriber object and adds it to the topic specified. The first argument is a Black::Board::Topic object or the name of one which is already registered to the Singleton that lives in ">. The second argument should be a code reference. The code reference is passed off to Black::Board::Subscriber as the "subscription" callback. "publish" Publishes the given message to the given topic. Takes two conceptual arguments: The first argument can be the Black::Board::Topic object. If the first argument is a "TYPES/TopicName" in Black::Board::Types, it will be coerced by looking up the "TopicName" in the "CLASS ATTRIBUTES/Publisher". That failing, an exception will be thrown. Next you can pass in either a "HashRef" or a "Hash" (list of key/value pairs) which is converted into a "HashRef". This "HashRef" is taken as meta information for creating a Black::Board::Message object. All keys except those which start with a dash "-" are treated as "> key/value pairs. These are roughly equivalent: # simplest publish LogDispatch => message => "I got here", level => "debug"; and # can use a hash reference publish LogDispatch => { message => "I got here", level => "debug" }; Keys which start with a dash "-", have the dash removed and are passed along to the "Black::Board::Message" constructor. for example: publish Foo => -params => { hi => "there" }; Here are some more equivalent examples matching the ones above: # ditch sugar completely Black::Board->Publisher->publish( topic => Black::Board->Publisher->topic( "LogDispatch" ), message => Black::Board::Message->new( params => { message => "I got here", level => "debug" } ) ); and # pass in the message as an object, maybe the same object from a previous # call to publish publish LogDispatch => Black::Board::Message->new( params => { message => "I got here", level => "debug" } ); and # pass in the topic and message as objects publish Black::Board->Publisher->topic( "LogDispatch" ), Black::Board::Message->new( params => { message => "I got here", level => "debug" } ); NB: If you have a "-params" argument as well as non-dash arguments, the "-params" argument will be merged and will take precedence. EXPORTS * topic * subscriber * publish SEE ALSO * Black::Board::Publisher Dispatcher and owner of Topics * Black::Board::Topic A Topic object is a place to subclass for custom Topics that handle something more complicated than a "param()" based message. * Black::Board::Message A "param()" based Message. Subclass for a more complicated Message. * Black::Board::Subscriber Encapsulates subscriber hooks to maintain consistent calling conventions. * Black::Board::Types If you are doing any subclassing, look here for the MooseX::Types. AUTHOR Scott Beck COPYRIGHT AND LICENSE This software is copyright (c) 2010 by Scott Beck . This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.