use warnings; use strict; package Jifty::Subs; use constant new => __PACKAGE__; =head1 NAME Jifty::Subs - =head1 SYNOPSIS my $sid = Jifty->subs->add( class => 'Tick', queries => [{ like => '9' }], mode => 'Replace', region => "clock-time", render_with => '/fragments/time', ); Jifty->subs->cancel($sid); my @sids = Jifty->subs->list; =head1 DESCRIPTION =cut =head2 add PARAMHASH Add a subscription for the current window or session. Takes the following parameters =over =item class What class of object shall we subscribe to notifications on =item queries An array of queries to match items of class C against. The implementation of C is dependent on the type of object events are being recorded against =item mode How should the fragment sent to the client on matching events be rendered. Valid modes are C, C and C =item region The moniker of the region that updates to this subscription should be rendered into =item render_with The path of the fragment used to render items matching this subscription =back =cut sub add { my $class = shift; my $args = {@_}; unless (Jifty->config->framework('PubSub')->{'Enable'}) { Jifty->log->error("PubSub disabled, but $class->add called"); return undef; } my $id = ($args->{window_id} || Jifty->web->session->id); my $event_class = Jifty->app_class("Event", $args->{class}); my $queries = $args->{queries} || []; my $channel = $event_class->encode_queries(@$queries); # The ->modify here is calling into the callback sub{...} with # the previous value of $_, that is a hashref of channels to # queries associated with those channels. The callback then # massages it to add a new channel/queries mapping; the value # of $_ at the end of the callback is then atomically updated # into the message bus under the same key. Jifty->bus->modify( "$event_class-subscriptions" => sub { $_->{$channel} = $queries; } ); # The per-window/session ($id) rendering information ("$id-render") # contains a hash from subscribed channels to rendering information, # including the frament, region, argument and ajax updating mode. Jifty->bus->modify( "$id-render" => sub { $_->{$channel} = { map { $_ => $args->{$_} } qw/render_with region arguments mode/ }; } ); # We create/update a IPC::PubSub::Subscriber object for this $id, # and have it subscribe to the channel that we're adding here. Jifty->bus->modify( "$id-subscriber" => sub { if ($_) { $_->subscribe($channel) } else { $_ = Jifty->bus->new_subscriber($channel) } } ); return "$channel!$id"; } =head2 cancel CHANNEL_ID Cancels session or window's subscription to CHANNEL_ID =cut sub cancel { my ($class, $channel_id) = @_; unless (Jifty->config->framework('PubSub')->{'Enable'}) { Jifty->log->error("PubSub disabled, but $class->add called"); return undef; } my ($channel, $id) = split(/!/, $channel_id, 2); my ($event_class) = split(/-/, $channel); $id ||= Jifty->web->session->id; Jifty->bus->modify( "$event_class-subscriptions" => sub { delete $_->{$channel}; } ); Jifty->bus->modify( "$id-render" => sub { delete $_->{$channel}; } ); Jifty->bus->modify( "$id-subscriber" => sub { if ($_) { $_->unsubscribe($channel) } } ); } =head2 list [window/sessionid] Returns a lost of channel ids this session or window is subscribed to. =cut sub list { my $self = shift; unless (Jifty->config->framework('PubSub')->{'Enable'}) { Jifty->log->error("PubSub disabled, but $self->add called"); return undef; } my $id = (shift || Jifty->web->session->id); my $subscribe = Jifty->bus->modify( "$id-subscriber" ) or return (); return $subscribe->channels; } 1;