[Jifty-commit] r2071 - in jifty/trunk: . lib lib/Jifty
lib/Jifty/Action lib/Jifty/Plugin/REST lib/Jifty/Script
lib/Jifty/Subs lib/Jifty/Upgrade lib/Jifty/Web
share/web/static/js share/web/static/js/jsan
share/web/templates/= share/web/templates/_elements t/TestApp/t
jifty-commit at lists.jifty.org
jifty-commit at lists.jifty.org
Fri Oct 27 03:58:35 EDT 2006
Author: jesse
Date: Fri Oct 27 03:58:30 2006
New Revision: 2071
Added:
jifty/trunk/lib/Jifty/Event.pm
jifty/trunk/lib/Jifty/Subs/
jifty/trunk/lib/Jifty/Subs.pm (contents, props changed)
jifty/trunk/lib/Jifty/Subs/Render.pm (contents, props changed)
jifty/trunk/share/web/static/js/jifty_subs.js
jifty/trunk/share/web/static/js/jsan/Push.js
jifty/trunk/share/web/templates/=/
jifty/trunk/share/web/templates/=/subs
jifty/trunk/t/TestApp/t/instance_id.t
Modified:
jifty/trunk/ (props changed)
jifty/trunk/MANIFEST
jifty/trunk/Makefile.PL
jifty/trunk/lib/Jifty.pm
jifty/trunk/lib/Jifty/Action.pm
jifty/trunk/lib/Jifty/Action/Record.pm
jifty/trunk/lib/Jifty/Action/Record/Create.pm
jifty/trunk/lib/Jifty/Action/Record/Delete.pm
jifty/trunk/lib/Jifty/Action/Record/Update.pm
jifty/trunk/lib/Jifty/ClassLoader.pm
jifty/trunk/lib/Jifty/Config.pm
jifty/trunk/lib/Jifty/Everything.pm
jifty/trunk/lib/Jifty/Handle.pm
jifty/trunk/lib/Jifty/Handler.pm
jifty/trunk/lib/Jifty/Plugin/REST/Dispatcher.pm
jifty/trunk/lib/Jifty/Script/Schema.pm
jifty/trunk/lib/Jifty/Script/Server.pm
jifty/trunk/lib/Jifty/Upgrade/Internal.pm
jifty/trunk/lib/Jifty/Web.pm
jifty/trunk/lib/Jifty/Web/PageRegion.pm
jifty/trunk/share/web/static/js/jifty.js
jifty/trunk/share/web/templates/_elements/wrapper
Log:
Merge of the Jifty pubsub hackathon branch.
Audrey, Jesse, CL and Schwern hacked out a PubSub message bus and a
preliminary Comet implementation.
----------------------------------------------------------------------
r29425 (orig r147): (no author) | 2006-10-27 00:34:15 -0700
r29424 at pinglin: jesse | 2006-10-27 00:34:14 -0700
* Removing some warnings
----------------------------------------------------------------------
r29423 (orig r146): (no author) | 2006-10-27 00:21:05 -0700
r29422 at pinglin: jesse | 2006-10-27 00:21:02 -0700
* Action monikers didn't always work in a non-jifty context, which broke tests for hiveminder.com
----------------------------------------------------------------------
r29419 (orig r143): (no author) | 2006-10-26 16:53:16 -0700
r29416 at pinglin: jesse | 2006-10-26 16:50:28 -0700
* ability to enable and disable pubsub programatically
----------------------------------------------------------------------
r29410 (orig r138): (no author) | 2006-10-25 23:12:31 -0700
pull jifty trunk.
----------------------------------------------------------------------
r29406 (orig r136): (no author) | 2006-10-25 22:08:31 -0700
r29404 at pinglin: jesse | 2006-10-25 22:07:30 -0700
* Events on record actions
----------------------------------------------------------------------
r29401 (orig r133): (no author) | 2006-10-25 21:38:02 -0700
Have Jifty.Subs automagically created when we have any subscrption.
----------------------------------------------------------------------
r29389 (orig r128): (no author) | 2006-10-25 18:19:53 -0700
* Jifty::Even - Remove two warnings.
----------------------------------------------------------------------
r29383 (orig r124): (no author) | 2006-10-25 17:54:36 -0700
* Jifty::Event - add ->data
* Jifty::Subs - allow unsubscribing on channel name alone
----------------------------------------------------------------------
r29382 (orig r123): (no author) | 2006-10-25 17:53:02 -0700
* Jifty::Subs::Render: If there's zero subscription don't die
with weird can't call ->get_all method.
----------------------------------------------------------------------
r29380 (orig r122): (no author) | 2006-10-25 17:47:21 -0700
Refactor the region rendering using subrequest bit.
----------------------------------------------------------------------
r29376 (orig r120): (no author) | 2006-10-25 17:27:36 -0700
* Jifty->subs->list
----------------------------------------------------------------------
r29375 (orig r119): (no author) | 2006-10-25 17:26:20 -0700
* Jifty->Subs->cancel
----------------------------------------------------------------------
r29374 (orig r118): (no author) | 2006-10-25 17:25:13 -0700
Refactor /=/subs/ into Jifty::Sub::Render.
----------------------------------------------------------------------
r29371 (orig r117): (no author) | 2006-10-25 17:12:06 -0700
subs
----------------------------------------------------------------------
r29368 (orig r115): (no author) | 2006-10-25 16:59:04 -0700
* $r is now $region
----------------------------------------------------------------------
r29367 (orig r114): (no author) | 2006-10-25 16:57:11 -0700
* Sanity, ergonomics and misc. review from obra++
----------------------------------------------------------------------
r29366 (orig r113): (no author) | 2006-10-25 16:30:20 -0700
User-defined events are now auto-loaded.
----------------------------------------------------------------------
r29363 (orig r110): (no author) | 2006-10-25 16:22:29 -0700
* Jifty: Updating subs to take advantage of the new Jifty::Event model
----------------------------------------------------------------------
r29361 (orig r108): (no author) | 2006-10-25 15:34:40 -0700
Use bus modify to store subs and renderinfo.
----------------------------------------------------------------------
r29357 (orig r105): (no author) | 2006-10-25 15:16:28 -0700
r29354 at pinglin: jesse | 2006-10-25 15:15:30 -0700
* doc updates
----------------------------------------------------------------------
r29356 (orig r104): (no author) | 2006-10-25 15:16:09 -0700
r29348 at pinglin: jesse | 2006-10-25 15:11:33 -0700
* Event.pm doc updates
----------------------------------------------------------------------
r29351 (orig r101): (no author) | 2006-10-25 15:13:49 -0700
* Republished query order no longer matters.
----------------------------------------------------------------------
r29350 (orig r100): (no author) | 2006-10-25 15:13:19 -0700
default subs channel now works.
----------------------------------------------------------------------
r29349 (orig r99): (no author) | 2006-10-25 15:08:30 -0700
* send payload
----------------------------------------------------------------------
r29346 (orig r98): (no author) | 2006-10-25 15:05:21 -0700
* Jifty::Even 0th cut.
----------------------------------------------------------------------
r29343 (orig r96): (no author) | 2006-10-25 14:31:32 -0700
r29339 at pinglin: jesse | 2006-10-25 14:30:49 -0700
* Added event baseclasses
----------------------------------------------------------------------
r29341 (orig r94): (no author) | 2006-10-25 14:18:05 -0700
instance_id -> app_instance_id.
----------------------------------------------------------------------
r29331 (orig r89): (no author) | 2006-10-25 12:11:19 -0700
* Jifty.pm: Init PubSub always
----------------------------------------------------------------------
r29326 (orig r84): (no author) | 2006-10-25 10:34:49 -0700
r29324 at pinglin: jesse | 2006-10-25 10:33:38 -0700
* The messagebus now falls back to jiftydbi
----------------------------------------------------------------------
r29320 (orig r82): (no author) | 2006-10-25 10:06:56 -0700
* Use instance_id in memcached.
----------------------------------------------------------------------
r29319 (orig r81): (no author) | 2006-10-25 10:06:37 -0700
r29318 at pinglin: jesse | 2006-10-25 10:06:07 -0700
* Added a unique instance ID per instance of your Jifty application (stored in the db)
----------------------------------------------------------------------
r29315 (orig r79): (no author) | 2006-10-25 10:00:06 -0700
* PubSub 0.11
----------------------------------------------------------------------
r29314 (orig r78): (no author) | 2006-10-25 09:59:42 -0700
* Port to use IPC::PubSub
----------------------------------------------------------------------
r29307 (orig r73): (no author) | 2006-10-25 00:23:15 -0700
subscription channel.
----------------------------------------------------------------------
r29306 (orig r72): (no author) | 2006-10-25 00:03:29 -0700
*default for J::S
----------------------------------------------------------------------
r29305 (orig r71): (no author) | 2006-10-25 00:00:21 -0700
add viewmap
----------------------------------------------------------------------
r29303 (orig r69): (no author) | 2006-10-24 23:48:08 -0700
primitive jifty->subs.
----------------------------------------------------------------------
r29297 (orig r63): (no author) | 2006-10-24 22:51:12 -0700
subscription related js files.
----------------------------------------------------------------------
r29289 (orig r55): (no author) | 2006-10-24 22:04:01 -0700
* Jifty::Script::Server - Allow custom Server backends to allow
forking support from a standalone httpd.
----------------------------------------------------------------------
r29288 (orig r54): (no author) | 2006-10-24 22:03:44 -0700
* jifty.js: Allow multiple message chunks.
----------------------------------------------------------------------
r29282 (orig r48): (no author) | 2006-10-24 19:32:25 -0700
tolower case for content nodeName.
----------------------------------------------------------------------
r29278 (orig r44): (no author) | 2006-10-24 19:17:43 -0700
more update refactor
----------------------------------------------------------------------
r29276 (orig r42): (no author) | 2006-10-24 19:10:28 -0700
more update refactor.
----------------------------------------------------------------------
r29263 (orig r35): (no author) | 2006-10-24 18:46:39 -0700
refactor prepare_element_for_update
----------------------------------------------------------------------
r29249 (orig r26): (no author) | 2006-10-24 18:19:37 -0700
publish jifty trunk
----------------------------------------------------------------------
r29248 (orig r25): (no author) | 2006-10-24 18:18:15 -0700
push!
----------------------------------------------------------------------
Modified: jifty/trunk/MANIFEST
==============================================================================
--- jifty/trunk/MANIFEST (original)
+++ jifty/trunk/MANIFEST Fri Oct 27 03:58:30 2006
@@ -45,6 +45,7 @@
inc/Module/Install/WriteAll.pm
lib/Email/Send/Jifty/Test.pm
lib/Jifty.pm
+lib/Jifty/Subs.pm
lib/Jifty/Action.pm
lib/Jifty/Action/Autocomplete.pm
lib/Jifty/Action/Record.pm
Modified: jifty/trunk/Makefile.PL
==============================================================================
--- jifty/trunk/Makefile.PL (original)
+++ jifty/trunk/Makefile.PL Fri Oct 27 03:58:30 2006
@@ -40,6 +40,7 @@
requires('HTTP::Server::Simple::Recorder');
requires('Hash::Merge');
requires('Hook::LexWrap');
+requires('IPC::PubSub' => '0.11' );
requires('Jifty::DBI' => '0.25' ); # Jifty::DBI::Collection Jifty::DBI::Handle Jifty::DBI::Record::Cachable Jifty::DBI::SchemaGenerator
requires('Locale::Maketext::Extract' => '0.20');
requires('Locale::Maketext::Lexicon' => '0.60');
Modified: jifty/trunk/lib/Jifty.pm
==============================================================================
--- jifty/trunk/lib/Jifty.pm (original)
+++ jifty/trunk/lib/Jifty.pm Fri Oct 27 03:58:30 2006
@@ -2,10 +2,11 @@
use strict;
package Jifty;
+use IPC::PubSub;
use encoding 'utf8';
# Work around the fact that Time::Local caches thing on first require
BEGIN { local $ENV{'TZ'} = "GMT"; require Time::Local;}
-$Jifty::VERSION = '0.60912';
+$Jifty::VERSION = '0.61025';
=head1 NAME
@@ -62,7 +63,7 @@
use base qw/Jifty::Object/;
use Jifty::Everything;
-use vars qw/$HANDLE $CONFIG $LOGGER $HANDLER $API $CLASS_LOADER @PLUGINS/;
+use vars qw/$HANDLE $CONFIG $LOGGER $HANDLER $API $CLASS_LOADER $PUB_SUB @PLUGINS/;
=head1 METHODS
@@ -245,6 +246,51 @@
return $HTML::Mason::Commands::JiftyWeb;
}
+=head2 subs
+
+An accessor for the L<Jifty::Subs> object that the subscription uses.
+
+=cut
+
+sub subs {
+ return Jifty::Subs->new;
+}
+
+=head2 bus
+
+Returns an IPC::PubSub object for the current application.
+
+=cut
+
+sub bus {
+
+ unless ($PUB_SUB) {
+ my @args;
+
+ my $backend = Jifty->config->framework('PubSub')->{'Backend'};
+ if ( $backend eq 'Memcached' ) {
+ require IO::Socket::INET;
+
+ # If there's a running memcached on the default port. this should become configurable
+ if ( IO::Socket::INET->new('127.0.0.1:11211') ) {
+ @args = ( Jifty->app_instance_id );
+ } else {
+ $backend = 'JiftyDBI';
+ }
+ }
+
+ if ($backend eq 'JiftyDBI' ) {
+ @args = (
+ db_config => Jifty->handle->{db_config},
+ table_prefix => '_jifty_pubsub_',
+ );
+ }
+ $PUB_SUB = IPC::PubSub->new( $backend => @args );
+
+ }
+ return $PUB_SUB;
+}
+
=head2 plugins
Returns a list of L<Jifty::Plugin> objects for this Jifty application.
@@ -306,9 +352,28 @@
}
}
+
+=head2 app_instance_id
+
+Returns a globally unique id for this instance of this jifty
+application. This value is generated the first time it's accessed
+
+=cut
+
+sub app_instance_id {
+ my $self = shift;
+ my $app_instance_id = Jifty::Model::Metadata->load("application_instance_uuid");
+ unless ($app_instance_id) {
+ $app_instance_id = Data::UUID->new->create_str();
+ Jifty::Model::Metadata->store(application_instance_uuid => $app_instance_id );
+ }
+ return $app_instance_id;
+}
+
+
=head1 LICENSE
-Jifty is Copyright 2005 Best Practical Solutions, LLC.
+Jifty is Copyright 2005-2006 Best Practical Solutions, LLC.
Jifty is distributed under the same terms as Perl itself.
=head1 SEE ALSO
Modified: jifty/trunk/lib/Jifty/Action.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Action.pm (original)
+++ jifty/trunk/lib/Jifty/Action.pm Fri Oct 27 03:58:30 2006
@@ -170,7 +170,9 @@
# Increment the per-request moniker digest counter, for the case of looped action generation
my $digest = md5_hex("@stack");
- my $serial = ++(Jifty->handler->stash->{monikers}{$digest});
+ # We should always have a stash. but if we don't, fake something up
+ # (some hiveminder tests create actions outside of a Jifty::Web)
+ my $serial = Jifty->handler->stash ? ++(Jifty->handler->stash->{monikers}{$digest}) : rand();
my $moniker = "auto-$digest-$serial";
$self->log->debug("Generating moniker $moniker from stack for $self");
return $moniker;
Modified: jifty/trunk/lib/Jifty/Action/Record.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Action/Record.pm (original)
+++ jifty/trunk/lib/Jifty/Action/Record.pm Fri Oct 27 03:58:30 2006
@@ -1,6 +1,7 @@
use warnings;
use strict;
use Date::Manip ();
+use UNIVERSAL::require;
package Jifty::Action::Record;
@@ -332,6 +333,33 @@
);
}
+sub _setup_event_before_action {
+ my $self = shift;
+
+ my $event_info = {};
+ $event_info->{as_hash_before} = $self->record->as_hash;
+ $event_info->{record_id} = $self->record->id;
+ $event_info->{record_class} = ref($self->record);
+ $event_info->{action_class} = ref($self);
+ $event_info->{action_arguments} = $self->argument_values; # XXX does this work?
+ $event_info->{current_user_id} = $self->current_user->id || 0;
+ return ($event_info);
+}
+
+sub _setup_event_after_action {
+ my $self = shift;
+ my $event_info = shift;
+ $event_info->{result} = $self->result;
+ $event_info->{timestamp} = time();
+ $event_info->{as_hash_after} = $self->record->as_hash;
+
+ my $event_class = $event_info->{'record_class'};
+ $event_class =~ s/::Model::/::Event::Model::/g;
+ Jifty::Util->require($event_class);
+ $event_class->new($event_info)->publish;
+}
+
+
=head1 SEE ALSO
L<Jifty::Action>, L<Jifty::Record>, L<Jifty::DBI::Record>,
Modified: jifty/trunk/lib/Jifty/Action/Record/Create.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Action/Record/Create.pm (original)
+++ jifty/trunk/lib/Jifty/Action/Record/Create.pm Fri Oct 27 03:58:30 2006
@@ -56,6 +56,9 @@
my $self = shift;
my $record = $self->record;
+ my $event_info = $self->_setup_event_before_action();
+
+
my %values;
# Virtual arguments aren't really ever backed by data structures. they're added by jifty for things like confirmations
for (grep { defined $self->argument_value($_) && !$self->arguments->{$_}->{virtual} } $self->argument_names) {
@@ -74,18 +77,19 @@
($id,$msg) = $msg->as_array;
}
- unless ( $record->id ) {
+ if (! $record->id ) {
$self->log->debug(_("Create of %1 failed: %2", ref($record), $msg));
$self->result->error($msg || _("An error occurred. Try again later"));
- return;
}
-
- # Return the id that we created
- $self->result->content(id => $self->record->id);
- $self->report_success if not $self->result->failure;
+ else {
+ # Return the id that we created
+ $self->result->content(id => $self->record->id);
+ $self->report_success if not $self->result->failure;
+ }
+ $self->_setup_event_after_action($event_info) ;
- return 1;
+ return ($self->record->id);
}
=head2 report_success
Modified: jifty/trunk/lib/Jifty/Action/Record/Delete.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Action/Record/Delete.pm (original)
+++ jifty/trunk/lib/Jifty/Action/Record/Delete.pm Fri Oct 27 03:58:30 2006
@@ -52,12 +52,13 @@
sub take_action {
my $self = shift;
+ my $event_info = $self->_setup_event_before_action();
+
my ( $val, $msg ) = $self->record->delete;
- $self->result->error($msg)
- if not $val and $msg;
+ $self->result->error($msg) if not $val and $msg;
- $self->report_success
- if not $self->result->failure;
+ $self->report_success if not $self->result->failure;
+ $self->_setup_event_after_action($event_info);
return 1;
}
Modified: jifty/trunk/lib/Jifty/Action/Record/Update.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Action/Record/Update.pm (original)
+++ jifty/trunk/lib/Jifty/Action/Record/Update.pm Fri Oct 27 03:58:30 2006
@@ -84,6 +84,8 @@
my $self = shift;
my $changed = 0;
+ my $event_info = $self->_setup_event_before_action();
+
for my $field ( $self->argument_names ) {
# Skip values that weren't submitted
next unless $self->has_argument($field);
@@ -136,6 +138,8 @@
$self->report_success
if $changed and not $self->result->failure;
+ $self->_setup_event_after_action($event_info);
+
return 1;
}
Modified: jifty/trunk/lib/Jifty/ClassLoader.pm
==============================================================================
--- jifty/trunk/lib/Jifty/ClassLoader.pm (original)
+++ jifty/trunk/lib/Jifty/ClassLoader.pm Fri Oct 27 03:58:30 2006
@@ -53,6 +53,10 @@
An empty class that descends from L<Jifty::Record> is created.
+=item I<Application>::Event
+
+An empty class that descends from L<Jifty::Event> is created.
+
=item I<Application>::Collection
An empty class that descends from L<Jifty::Collection> is created.
@@ -101,7 +105,6 @@
# This subroutine's name is fully qualified, as perl will ignore a 'sub INC'
sub Jifty::ClassLoader::INC {
my ( $self, $module ) = @_;
-
my $base = $self->{base};
return undef unless ( $module and $base );
@@ -122,7 +125,7 @@
# . "use base qw/Jifty::Action/; sub _autogenerated { 1 };\n"
# . "1;" );
# }
- elsif ( $module =~ m!^(?:$base)::(Record|Collection|Notification|Dispatcher|Bootstrap|Upgrade|Handle)$! ) {
+ elsif ( $module =~ m!^(?:$base)::(Record|Collection|Notification|Dispatcher|Bootstrap|Upgrade|Handle|Event|Event::Model)$! ) {
return $self->return_class(
"use warnings; use strict; package $module;\n"
. "use base qw/Jifty::$1/; sub _autogenerated { 1 };\n"
@@ -138,6 +141,18 @@
. "use base qw/@{[$base]}::Collection/;\n"
. "sub record_class { '@{[$base]}::Model::$1' }\n"
. "1;" );
+ } elsif ( $module =~ m!^(?:$base)::Event::Model::([^\.]+)$! ) {
+ my $modelclass = $base . "::Model::" . $1;
+ Jifty::Util->require($modelclass);
+
+ return undef unless eval { $modelclass->table };
+
+ return $self->return_class(
+ "use warnings; use strict; package $module;\n"
+ . "use base qw/${base}::Event::Model/;\n"
+ . "sub record_class { '$modelclass' };\n"
+ . "sub autogenerated { 1 };\n"
+ . "1;" );
} elsif ( $module =~ m!^(?:$base)::Action::(Create|Update|Delete|Search)([^\.]+)$! ) {
my $modelclass = $base . "::Model::" . $2;
Jifty::Util->require($modelclass);
@@ -186,10 +201,9 @@
return unless ($base);
Jifty::Util->require($base);
Jifty::Util->require($base."::CurrentUser");
-
Jifty::Module::Pluggable->import(
search_path =>
- [ map { $base . "::" . $_ } 'Model', 'Action', 'Notification' ],
+ [ map { $base . "::" . $_ } 'Model', 'Action', 'Notification', 'Event' ],
require => 1,
except => qr/\.#/,
inner => 0
Modified: jifty/trunk/lib/Jifty/Config.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Config.pm (original)
+++ jifty/trunk/lib/Jifty/Config.pm Fri Oct 27 03:58:30 2006
@@ -241,6 +241,9 @@
ApplicationClass => $app_class,
ApplicationName => $app_name,
LogLevel => 'INFO',
+ PubSub => {
+ Backend => 'Memcached',
+ },
Database => {
Database => $db_name,
Driver => "SQLite",
Added: jifty/trunk/lib/Jifty/Event.pm
==============================================================================
--- (empty file)
+++ jifty/trunk/lib/Jifty/Event.pm Fri Oct 27 03:58:30 2006
@@ -0,0 +1,143 @@
+use warnings;
+use strict;
+
+package Jifty::Event;
+
+use Storable 'nfreeze';
+use Digest::MD5 qw(md5_hex);
+use vars qw/%PUBLISHER/;
+
+=head1 NAME
+
+Jifty::Event
+
+=head1 DESCRIPTION
+
+An event object from the Jifty::PubSub stream.
+
+=head1 METHODS
+
+=head2 new($payload)
+
+Constructor. Takes any kind of payload and blesses a scalar reference to it
+into an Event object.
+
+=cut
+
+sub new {
+ my $class = shift;
+ my $payload = shift;
+ bless \$payload, $class;
+}
+
+=head2 publish()
+
+Inserts the event into the pubsub stream. If Jifty is configured into
+synchronous republishing, then this method runs a C<republish> on itself
+with all current subscriptions implicitly. If not, it's simply inserted
+into its main channel for asynchronous republishing later.
+
+=cut
+
+sub publish {
+ my $self = shift;
+ my $class = ref($self) || $self;
+
+ return undef unless (Jifty->config->framework('PubSub')->{'Enable'});
+
+ # Always publish to the main stream (needed for async & debugging)
+ # if ($ASYNC || $DEBUGGING) {
+ # ($PUBLISHER{$class} ||= Jifty->bus->new_publisher($class))->msg($$self);
+ # return;
+ # }
+
+ # Synchronized auto-republishing
+ # TODO - Prioritize current-user subscriptions first?
+ my $subscriptions = Jifty->bus->modify("$class-subscriptions") || {};
+ while (my ($channel, $queries) = each %$subscriptions) {
+ if ($self->filter(@$queries)) {
+ ($PUBLISHER{$channel} ||= Jifty->bus->new_publisher($channel))->msg($$self);
+ }
+ }
+}
+
+=head2 filter(@query)
+
+Takes multiple class-specific queries, which are evaluated in order by calling L</match>.
+
+=cut
+
+sub filter {
+ my $self = shift;
+ $self->match($_) or return 0 for @_;
+ return 1;
+}
+
+=head2 republish(@query)
+
+Run C<filter> with the queries; if they all succeed, the event is republished
+into that query-specific channel.
+
+=cut
+
+sub republish {
+ my $self = shift;
+ $self->filter(@_) or return;
+
+ my $channel = $self->encode_queries(@_);
+ ($PUBLISHER{$channel} ||= Jifty->bus->new_publisher($channel))->msg($$self);
+}
+
+
+=head2 encode_queries(@query)
+
+Encode queries into some sort of canonical md5 encoding.
+
+=cut
+
+sub encode_queries {
+ my $self = shift;
+ my $class = ref($self) || $self;
+ return $class unless @_;
+
+ local $Storable::canonical = 1;
+ return $class . '-' . md5_hex(join('', sort map { Storable::nfreeze($_) } @_));
+}
+
+
+=head2 match($query)
+
+Takes a class-specific query and returns whether it matches.
+
+You almost always want to override this; the default implementation
+simply always return true;
+
+=cut
+
+sub match {
+ 1;
+}
+
+=head2 render_arguments()
+
+A list of additional things to push into the C<%ARGS> of the region that
+is about to render this event; see L<Jifty::Subs::Render> for more information.
+
+=cut
+
+sub render_arguments {
+ ();
+}
+
+=head2 data()
+
+This event's payload as a scalar value.
+
+=cut
+
+sub data {
+ ${$_[0]}
+}
+
+
+1;
Modified: jifty/trunk/lib/Jifty/Everything.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Everything.pm (original)
+++ jifty/trunk/lib/Jifty/Everything.pm Fri Oct 27 03:58:30 2006
@@ -73,6 +73,9 @@
use Jifty::Web::Form::Field ();
use Jifty::Web::Menu ();
+use Jifty::Subs ();
+use Jifty::Subs::Render ();
+
use Jifty::Module::Pluggable;
Jifty::Module::Pluggable->import(search_path => ['Jifty::Web::Form::Field'],
require => 1,
Modified: jifty/trunk/lib/Jifty/Handle.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Handle.pm (original)
+++ jifty/trunk/lib/Jifty/Handle.pm Fri Oct 27 03:58:30 2006
@@ -90,6 +90,7 @@
$lc_db_config{lc($_)} = $db_config{$_};
}
$self->SUPER::connect( %lc_db_config , %args);
+ $self->{db_config} = { %lc_db_config , %args };
$self->dbh->{LongReadLen} = Jifty->config->framework('MaxAttachmentSize') || '10000000';
}
Modified: jifty/trunk/lib/Jifty/Handler.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Handler.pm (original)
+++ jifty/trunk/lib/Jifty/Handler.pm Fri Oct 27 03:58:30 2006
@@ -70,7 +70,7 @@
Jifty::Util->require( $self->dispatcher );
$self->dispatcher->import_plugins;
$self->dispatcher->dump_rules;
-
+
$self->mason( Jifty::View::Mason::Handler->new( $self->mason_config ) );
$self->static_handler(Jifty::View::Static::Handler->new());
Modified: jifty/trunk/lib/Jifty/Plugin/REST/Dispatcher.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Plugin/REST/Dispatcher.pm (original)
+++ jifty/trunk/lib/Jifty/Plugin/REST/Dispatcher.pm Fri Oct 27 03:58:30 2006
@@ -49,7 +49,6 @@
if($prefix) {
@prefix = map {s/::/./g; $_} @$prefix;
$url = Jifty->web->url(path => join '/', '=', at prefix);
- warn "my preifx is ".join(',', at prefix) .$url;
}
@@ -109,7 +108,6 @@
my $prefix = shift;
my $url = shift;
my $content = shift;
- warn "REndering $prefix $url $content";
if (ref($content) eq 'ARRAY') {
return start_html(-encoding => 'UTF-8', -declare_xml => 1, -title => 'models'),
ul(map {
Modified: jifty/trunk/lib/Jifty/Script/Schema.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Script/Schema.pm (original)
+++ jifty/trunk/lib/Jifty/Script/Schema.pm Fri Oct 27 03:58:30 2006
@@ -272,6 +272,15 @@
# Commit it all
Jifty->handle->commit;
}
+
+ Jifty::Util->require('IPC::PubSub');
+ IPC::PubSub->new(
+ JiftyDBI => (
+ db_config => Jifty->handle->{db_config},
+ table_prefix => '_jifty_pubsub_',
+ db_init => 1,
+ )
+ );
$log->info("Set up version $appv, jifty version $jiftyv");
}
Modified: jifty/trunk/lib/Jifty/Script/Server.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Script/Server.pm (original)
+++ jifty/trunk/lib/Jifty/Script/Server.pm Fri Oct 27 03:58:30 2006
@@ -18,7 +18,6 @@
use Jifty::Everything;
-use Jifty::Server;
use File::Path ();
use constant PIDFILE => 'var/jifty-server.pid';
@@ -72,6 +71,9 @@
# Purge stale mason cache data
my $data_dir = Jifty->config->framework('Web')->{'DataDir'};
+ my $server_class = Jifty->config->framework('Web')->{'ServerClass'} || 'Jifty::Server';
+ Jifty::Util->require($server_class);
+
if (-d $data_dir) {
File::Path::rmtree(["$data_dir/cache", "$data_dir/obj"]);
}
@@ -86,10 +88,10 @@
$ENV{JIFTY_SERVER_SIGREADY} ||= $self->{sigready}
if $self->{sigready};
- Log::Log4perl->get_logger("Jifty::Server")->less_logging(3)
+ Log::Log4perl->get_logger($server_class)->less_logging(3)
if $self->{quiet};
- Jifty::Server->new(port => $self->{port})->run;
+ $server_class->new(port => $self->{port})->run;
}
1;
Added: jifty/trunk/lib/Jifty/Subs.pm
==============================================================================
--- (empty file)
+++ jifty/trunk/lib/Jifty/Subs.pm Fri Oct 27 03:58:30 2006
@@ -0,0 +1,131 @@
+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
+
+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 = join('::' =>
+ Jifty->config->framework("ApplicationClass"),
+ '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";
+}
+
+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) }
+ }
+ );
+}
+
+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;
Added: jifty/trunk/lib/Jifty/Subs/Render.pm
==============================================================================
--- (empty file)
+++ jifty/trunk/lib/Jifty/Subs/Render.pm Fri Oct 27 03:58:30 2006
@@ -0,0 +1,80 @@
+package Jifty::Subs::Render;
+use strict;
+use warnings;
+
+=head1 NAME
+
+Jifty::Subs::Render -
+
+=head1 SYNOPSIS
+
+ Jifty::Subs::Render->render($id, $callback);
+
+=head1 DESCRIPTION
+
+
+
+=head2 render($id, $callback)
+
+Render all outstanding messges, and call C<$callback> with render
+mode, region name, and content.
+
+=cut
+
+sub render {
+ my ( $class, $id, $callback ) = @_;
+ my $got;
+
+ # Get the IPC::PubSub::Subscriber object and do one fetch of all new
+ # events it subscribes to, and put those into $got.
+ my $subs
+ = Jifty->bus->modify( "$id-subscriber", sub { $got = $_ ? $_->get_all : {} } );
+
+ return 0 unless %$got;
+
+ # Now we the render options for those channels (calling ->modify instead
+ # of ->fetch because we want to block if someone else is touching it;
+ # it's equivalent to ->modify("$id-render", sub { $_ }).
+ my $render = Jifty->bus->modify("$id-render");
+
+ while ( my ( $channel, $msgs ) = each(%$got) ) {
+ foreach my $rv (@$msgs) {
+
+ # XXX - We don't yet use $timestamp here.
+ my ( $timestamp, $msg ) = @$rv;
+
+ # Channel name is always App::Event::Class-MD5QUERIES
+ my $event_class = $channel;
+ $event_class =~ s/-.*//;
+
+ unless ( UNIVERSAL::can( $event_class => 'new' ) ) {
+ Jifty->log->error("Receiving unknown event $event_class from the Bus");
+ $event_class = Jifty->config->framework('ApplicationClass')."::Event";
+ }
+
+ my $render_info = $render->{$channel};
+ my $region = Jifty::Web::PageRegion->new(
+ name => $render_info->{region},
+ path => $render_info->{render_with},
+ );
+ delete Jifty->web->{'regions'}{ $region->qualified_name };
+
+ # Finally render the region. In addition to the user-supplied arguments
+ # in $render_info, we always pass the target $region and the event object
+ # into its %ARGS.
+ my $region_content = '';
+ my $event_object = $event_class->new($msg);
+ $region->render_as_subrequest( \$region_content,
+ { %{ $render_info->{arguments} || {} },
+ event => $event_object,
+ $event_object->render_arguments,
+ }
+ );
+ $callback->(
+ $render_info->{mode}, $region->qualified_name, $region_content
+ );
+ }
+ }
+}
+
+1;
Modified: jifty/trunk/lib/Jifty/Upgrade/Internal.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Upgrade/Internal.pm (original)
+++ jifty/trunk/lib/Jifty/Upgrade/Internal.pm Fri Oct 27 03:58:30 2006
@@ -17,6 +17,17 @@
=cut
+since '0.61025' => sub {
+ Jifty::Util->require('IPC::PubSub');
+ IPC::PubSub->new(
+ JiftyDBI => (
+ db_config => Jifty->handle->{db_config},
+ table_prefix => '_jifty_pubsub_',
+ db_init => 1,
+ )
+ );
+};
+
since '0.60427' => sub {
my @v = Jifty->handle->fetch_result("SELECT major, minor, rev FROM _db_version");
Jifty->handle->simple_query("DROP TABLE _db_version");
Modified: jifty/trunk/lib/Jifty/Web.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Web.pm (original)
+++ jifty/trunk/lib/Jifty/Web.pm Fri Oct 27 03:58:30 2006
@@ -31,6 +31,7 @@
__PACKAGE__->javascript_libs([qw(
jsan/JSAN.js
+ jsan/Push.js
setup_jsan.js
jsan/Upgrade/Array/push.js
jsan/DOM/Events.js
@@ -47,6 +48,7 @@
formatDate.js
jifty.js
jifty_utils.js
+ jifty_subs.js
jifty_smoothscroll.js
calendar.js
dom-drag.js
Modified: jifty/trunk/lib/Jifty/Web/PageRegion.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Web/PageRegion.pm (original)
+++ jifty/trunk/lib/Jifty/Web/PageRegion.pm Fri Oct 27 03:58:30 2006
@@ -260,15 +260,35 @@
. qq|<div id="region-| . $self->qualified_name . qq|">|;
}
+ $self->render_as_subrequest(\$result, \%arguments);
+ $result .= qq|</div>| if ( $self->region_wrapper );
+
+ return $result;
+}
+
+=head2 render_as_subrequest
+
+=cut
+
+sub render_as_subrequest {
+ my ($self, $out_method, $arguments, $enable_actions) = @_;
+
+ my $orig_out = Jifty->handler->mason->interp->out_method || \&Jifty::View::Mason::Handler::out_method;
+
+ Jifty->handler->mason->interp->out_method($out_method);
+
# Make a fake request and throw it at the dispatcher
my $subrequest = Jifty->web->request->clone;
$subrequest->argument( region => $self );
- $subrequest->argument( $_ => $arguments{$_}) for keys %arguments;
+ # XXX: use ->arguments?
+ $subrequest->argument( $_ => $arguments->{$_}) for keys %$arguments;
$subrequest->path( $self->path );
$subrequest->top_request( Jifty->web->request->top_request );
# Remove all of the actions
- $_->active(0) for ($subrequest->actions);
+ unless ($enable_actions) {
+ $_->active(0) for ($subrequest->actions);
+ }
# $subrequest->clear_actions;
local Jifty->web->{request} = $subrequest;
@@ -276,16 +296,13 @@
# onto a variable and not send headers when it does so
#XXX TODO: There's gotta be a better way to localize it
my $region_content = '';
- Jifty->handler->mason->interp->out_method( \$region_content );
# Call into the dispatcher
Jifty->handler->dispatcher->handle_request;
- $result .= $region_content;
- $result .= qq|</div>| if ( $self->region_wrapper );
- Jifty->handler->mason->interp->out_method( \&Jifty::View::Mason::Handler::out_method );
+ Jifty->handler->mason->interp->out_method($orig_out);
- return $result;
+ return;
}
=head2 render
Modified: jifty/trunk/share/web/static/js/jifty.js
==============================================================================
--- jifty/trunk/share/web/static/js/jifty.js (original)
+++ jifty/trunk/share/web/static/js/jifty.js Fri Oct 27 03:58:30 2006
@@ -207,13 +207,13 @@
disable_input_fields: function() {
var disable = function() {
- var elt = arguments[0];
- // Disabling hidden elements seems to make IE sad for some reason
- if(elt.type != 'hidden') {
- // Triggers https://bugzilla.mozilla.org/show_bug.cgi?id=236791
- elt.blur();
- elt.disabled = true;
- }
+ var elt = arguments[0];
+ // Disabling hidden elements seems to make IE sad for some reason
+ if(elt.type != 'hidden') {
+ // Triggers https://bugzilla.mozilla.org/show_bug.cgi?id=236791
+ elt.blur();
+ elt.disabled = true;
+ }
};
this.fields().each(disable);
this.buttons().each(disable);
@@ -456,7 +456,7 @@
Element.addClassName( e, "jifty_enter_handler_attached" );
}
},
- "#messages, #errors": function(e) {
+ ".messages": function(e) {
if ( !Element.hasClassName( e, "jifty_enter_handler_attached" ) ) {
e.innerHTML=
'<a href="#" id="dismiss_'+e.id+'" title="Dismiss" onmousedown="this.onfocus=this.blur;" onmouseup="this.onfocus=window.clientInformation?null:window.undefined" onclick="Effect.Fade(this.parentNode); return false;">Dismiss</a>' + e.innerHTML;
@@ -569,57 +569,10 @@
// Keep track of the state variables.
var current_args = $H();
-// Update a region. Takes a hash of named parameters, including:
-// - 'actions' is an array of monikers to submit
-// - 'fragments' is an array of hashes, which may have:
-// - 'region' is the name of the region to update
-// - 'args' is a hash of arguments to override
-// - 'path' is the path of the fragment (if this is a new fragment)
-// - 'element' is the CSS selector of the element to update, if 'region' isn't supplied
-// - 'mode' is one of 'Replace', or the name of a Prototype Insertion
-// - 'effect' is the name of a Prototype Effect
-function update() {
- // If we don't have XMLHttpRequest, bail and fallback on full-page
- // loads
- if(!Ajax.getTransport()) return true;
- // XXX: prevent default behavior in IE
- if(window.event) {
- window.event.returnValue = false;
- }
-
- show_wait_message();
- var named_args = arguments[0];
- var trigger = arguments[1];
-
- // The YAML/JSON data structure that will be sent
- var request = $H();
-
- // Set request base path
- request['path'] = '/__jifty/webservices/xml';
-
- // Grab extra arguments (from a button)
- var button_args = Form.Element.buttonFormElements(trigger);
-
- // Build actions structure
- request['actions'] = $H();
- for (var moniker in named_args['actions']) {
- var disable = named_args['actions'][moniker];
- var a = new Action(moniker, button_args);
- if (a.register) {
- if (a.hasUpload())
- return true;
- if(disable) {
- a.disable_input_fields();
- }
- request['actions'][moniker] = a.data_structure();
- }
- }
-
- request['fragments'] = $H();
- // Build fragments structure
- for (var i = 0; i < named_args['fragments'].length; i++) {
- var f = named_args['fragments'][i];
+// Prepare element for use in update()
+// - 'fragment' is a hash, see fragments in update()
+function prepare_element_for_update(f) {
var name = f['region'];
// Find where we are going to go
@@ -635,14 +588,14 @@
// If we can't find out where we're going, bail
if (element == null)
- continue;
+ return;
// If we're removing the element, do it now
// XXX TODO: Effects on this?
if (f['mode'] == "Delete") {
fragments[name] = null;
Element.remove(element);
- continue;
+ return;
}
f['is_new'] = (fragments[name] ? false : true);
@@ -668,13 +621,129 @@
// If they set the 'toggle' flag, and clicking wouldn't change the path
Element.update(element, '');
fragments[name].path = null;
- continue;
+ return;
} else if (f['path'] == null) {
// If they didn't know the path, fill it in now
f['path'] == fragments[name].path;
}
+ return f;
+}
+// applying updates from a fragment
+// - fragment: the fragment from the server
+// - f: fragment spec
+var apply_fragment_updates = function(fragment, f) {
+ // We found the right fragment
+ var dom_fragment = fragments[f['region']];
+ var new_dom_args = $H();
+ var element = f['element'];
+ for (var fragment_bit = fragment.firstChild;
+ fragment_bit != null;
+ fragment_bit = fragment_bit.nextSibling) {
+ if (fragment_bit.nodeName == 'argument') {
+ // First, update the fragment's arguments
+ // with what the server actually used --
+ // this is needed in case there was
+ // argument mapping going on
+ var textContent = '';
+ if (fragment_bit.textContent) {
+ textContent = fragment_bit.textContent;
+ } else if (fragment_bit.firstChild) {
+ textContent = fragment_bit.firstChild.nodeValue;
+ }
+ new_dom_args[fragment_bit.getAttribute("name")] = textContent;
+ } else if (fragment_bit.nodeName.toLowerCase() == 'content') {
+ var textContent = '';
+ if (fragment_bit.textContent) {
+ textContent = fragment_bit.textContent;
+ } else if (fragment_bit.firstChild) {
+ textContent = fragment_bit.firstChild.nodeValue;
+ }
+
+ // Once we find it, do the insertion
+ if (f['mode'] && (f['mode'] != 'Replace')) {
+ var insertion = eval('Insertion.'+f['mode']);
+ new insertion(element, textContent.stripScripts());
+ } else {
+ Element.update(element, textContent.stripScripts());
+ }
+ // We need to give the browser some "settle" time before we eval scripts in the body
+ setTimeout((function() { this.evalScripts() }).bind(textContent), 10);
+ Behaviour.apply(element);
+ }
+ }
+ dom_fragment.setArgs(new_dom_args);
+
+ // Also, set us up the effect
+ if (f['effect']) {
+ try {
+ var effect = eval('Effect.'+f['effect']);
+ var effect_args = f['effect_args'] || {};
+ if (effect) {
+ if (f['is_new'])
+ Element.hide($('region-'+f['region']));
+ (effect)($('region-'+f['region']), effect_args);
+ }
+ } catch ( e ) {
+ // Don't be sad if the effect doesn't exist
+ }
+ }
+}
+
+// Update a region. Takes a hash of named parameters, including:
+// - 'actions' is an array of monikers to submit
+// - 'fragments' is an array of hashes, which may have:
+// - 'region' is the name of the region to update
+// - 'args' is a hash of arguments to override
+// - 'path' is the path of the fragment (if this is a new fragment)
+// - 'element' is the CSS selector of the element to update, if 'region' isn't supplied
+// - 'mode' is one of 'Replace', or the name of a Prototype Insertion
+// - 'effect' is the name of a Prototype Effect
+function update() {
+ // If we don't have XMLHttpRequest, bail and fallback on full-page
+ // loads
+ if(!Ajax.getTransport()) return true;
+ // XXX: prevent default behavior in IE
+ if(window.event) {
+ window.event.returnValue = false;
+ }
+
+ show_wait_message();
+ var named_args = arguments[0];
+ var trigger = arguments[1];
+
+ // The YAML/JSON data structure that will be sent
+ var request = $H();
+
+ // Set request base path
+ request['path'] = '/__jifty/webservices/xml';
+
+ // Grab extra arguments (from a button)
+ var button_args = Form.Element.buttonFormElements(trigger);
+
+ // Build actions structure
+ request['actions'] = $H();
+ for (var moniker in named_args['actions']) {
+ var disable = named_args['actions'][moniker];
+ var a = new Action(moniker, button_args);
+ if (a.register) {
+ if (a.hasUpload())
+ return true;
+ if(disable) {
+ a.disable_input_fields();
+ }
+ request['actions'][moniker] = a.data_structure();
+ }
+ }
+
+ request['fragments'] = $H();
+ // Build fragments structure
+ for (var i = 0; i < named_args['fragments'].length; i++) {
+ var f = named_args['fragments'][i];
+ f = prepare_element_for_update(f);
+ if (!f) continue;
// Update with all new values
+ var name = f['region'];
var fragment_request = fragments[name].data_structure(f['path'], f['args']);
if (f['is_new'])
@@ -689,81 +758,23 @@
var onSuccess = function(transport, object) {
// Grab the XML response
var response = transport.responseXML.documentElement;
-
- // For each fragment we requested
- for (var i = 0; i < named_args['fragments'].length; i++) {
- var f = named_args['fragments'][i];
- var element = f['element'];
-
- // Change insertion mode if need be
- var insertion = null;
- if (f['mode'] && (f['mode'] != 'Replace')) {
- insertion = eval('Insertion.'+f['mode']);
- }
-
- // Loop through the result looking for it
- for (var response_fragment = response.firstChild;
- response_fragment != null;
- response_fragment = response_fragment.nextSibling) {
- if (response_fragment.nodeName == 'fragment') {
- if (response_fragment.getAttribute("id") == f['region']) {
- // We found the right fragment
- var dom_fragment = fragments[f['region']];
- var new_dom_args = $H();
-
- for (var fragment_bit = response_fragment.firstChild;
- fragment_bit != null;
- fragment_bit = fragment_bit.nextSibling) {
- if (fragment_bit.nodeName == 'argument') {
- // First, update the fragment's arguments
- // with what the server actually used --
- // this is needed in case there was
- // argument mapping going on
- var textContent = '';
- if (fragment_bit.textContent) {
- textContent = fragment_bit.textContent;
- } else if (fragment_bit.firstChild) {
- textContent = fragment_bit.firstChild.nodeValue;
- }
- new_dom_args[fragment_bit.getAttribute("name")] = textContent;
- } else if (fragment_bit.nodeName == 'content') {
- var textContent = '';
- if (fragment_bit.textContent) {
- textContent = fragment_bit.textContent;
- } else if (fragment_bit.firstChild) {
- textContent = fragment_bit.firstChild.nodeValue;
- }
-
- // Once we find it, do the insertion
- if (insertion) {
- new insertion(element, textContent.stripScripts());
- } else {
- Element.update(element, textContent.stripScripts());
- }
- // We need to give the browser some "settle" time before we eval scripts in the body
- setTimeout((function() { this.evalScripts() }).bind(textContent), 10);
- Behaviour.apply(element);
- }
- }
- dom_fragment.setArgs(new_dom_args);
- }
- }
- }
-
- // Also, set us up the effect
- if (f['effect']) {
- try {
- var effect = eval('Effect.'+f['effect']);
- var effect_args = f['effect_args'] || {};
- if (effect) {
- if (f['is_new'])
- Element.hide($('region-'+f['region']));
- (effect)($('region-'+f['region']), effect_args);
- }
- } catch ( e ) {
- // Don't be sad if the effect doesn't exist
- }
- }
+ // Loop through the result looking for it
+ for (var response_fragment = response.firstChild;
+ response_fragment != null && response_fragment.nodeName == 'fragment';
+ response_fragment = response_fragment.nextSibling) {
+
+ var f;
+ for (var i = 0; i < named_args['fragments'].length; i++) {
+ f = named_args['fragments'][i];
+ if (response_fragment.getAttribute("id") == f['region'])
+ break;
+ }
+ if (response_fragment.getAttribute("id") != f['region'])
+ continue;
+
+ try {
+ apply_fragment_updates(response_fragment, f);
+ }catch (e) { alert(e) }
}
for (var result = response.firstChild;
result != null;
Added: jifty/trunk/share/web/static/js/jifty_subs.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/web/static/js/jifty_subs.js Fri Oct 27 03:58:30 2006
@@ -0,0 +1,26 @@
+if (typeof Jifty == "undefined") Jifty = { };
+
+{
+
+ var onPushHandler = function(t) {
+ var mode = t.attributes['mode'].nodeValue;
+ var rid = t.firstChild.attributes['id'].nodeValue;
+ var f = { region: rid, path: '', mode: mode };
+ f = prepare_element_for_update(f);
+ apply_fragment_updates(t.firstChild, f);
+ };
+
+
+ Jifty.Subs = function(args) {
+ var window_id = args.window_id; // XXX: not yet
+ var uri = args.uri;
+ if (!uri)
+ uri = "/=/subs?";
+ var push = new HTTP.Push({ "uri": uri, interval : 100,
+ "onPush" : onPushHandler});
+
+ this.start = function() {
+ push.start();
+ };
+ }
+}
Added: jifty/trunk/share/web/static/js/jsan/Push.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/web/static/js/jsan/Push.js Fri Oct 27 03:58:30 2006
@@ -0,0 +1,76 @@
+/*
+
+*/
+
+if (typeof(HTTP) == "undefined") { HTTP = {}; }
+
+HTTP.Push = {};
+HTTP.Push.VERSION = '0.04';
+
+/*
+
+*/
+
+HTTP.Push = function(args) {
+ if (args == undefined) { throw "Push must be passed an argument hash!"; }
+ if (args.uri == undefined) { throw "Must specify push URI!"; }
+ if (args.onPush == undefined) { throw "Must specify onPush handler!"; }
+ if (args.interval == undefined) { args.interval = 100; }
+
+
+ var body = document.getElementsByTagName("body")[0];
+ var iframe = document.createElement("iframe");
+ iframe.style.border = "0px";
+ iframe.style.height = "0px";
+ iframe.style.width = "0px";
+ iframe.src = args.uri;
+
+ var interval = undefined;
+
+/*
+
+*/
+
+ this.start = function() {
+ body.appendChild(iframe);
+ interval = setInterval(function() { flushIframe(); }, args.interval);
+ }
+
+
+// TODO: make the stop function work in IE
+// this.stop = function() {
+// body.removeChild(iframe);
+// clearInterval(interval);
+// }
+
+
+ function flushIframe() {
+ var doc;
+ if (iframe.contentDocument) { // For NS6
+ doc = iframe.contentDocument;
+ } else if (iframe.contentWindow) { // For IE5.5 and IE6
+ doc = iframe.contentWindow.document;
+ } else if (iframe.document) { // For IE5
+ doc = iframe.document;
+ } else {
+ return;
+ }
+
+ var body = doc.body;
+
+ while (body && body.hasChildNodes()) {
+ var node = body.firstChild;
+ try {
+ args.onPush(node);
+ }
+ catch (e) { };
+ body.removeChild(node);
+ }
+ }
+
+ return this;
+}
+
+/*
+
+*/
Added: jifty/trunk/share/web/templates/=/subs
==============================================================================
--- (empty file)
+++ jifty/trunk/share/web/templates/=/subs Fri Oct 27 03:58:30 2006
@@ -0,0 +1,43 @@
+<%init>
+
+$r->content_type("text/html");
+$r->send_http_header;
+
+my $writer = XML::Writer->new;
+$writer->xmlDecl( "UTF-8", "yes" );
+
+my $begin = <<'END';
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/2002/REC-xhtml1-20020801/DTD/xhtml1-strict.dtd">
+<html><head><title></title></head>
+END
+chomp $begin;
+my $whitespace = " " x (1024 - length $begin);
+$begin =~ s/<body>$/$whitespace<body>/s;
+
+
+$m->print($begin);
+$m->flush_buffer;
+
+$writer->startTag( "body" );
+
+my $id = Jifty->web->session->id;
+
+while (1) {
+ Jifty::Subs::Render->render(
+ $id,
+ sub {
+ my ( $mode, $name, $content ) = @_;
+ $writer->startTag( "pushfrag", mode => $mode );
+ $writer->startTag( "fragment", id => $name );
+ $writer->dataElement( "content", $content );
+ $writer->endTag();
+ $writer->endTag();
+ } );
+
+ flush STDOUT;
+ sleep 1;
+}
+$writer->endTag();
+
+</%init>
Modified: jifty/trunk/share/web/templates/_elements/wrapper
==============================================================================
--- jifty/trunk/share/web/templates/_elements/wrapper (original)
+++ jifty/trunk/share/web/templates/_elements/wrapper Fri Oct 27 03:58:30 2006
@@ -17,6 +17,11 @@
</div>
<div id="jifty-wait-message" style="display: none"><%_('Loading...')%></div>
% Jifty::Mason::Halo->render_component_tree() if (Jifty->config->framework('DevelMode') );
+%# This is required for jifty server push. If you maintain your own
+%# wrapper, make sure you have this as well.
+% if ( Jifty->config->framework('PubSub')->{'Enable'} && Jifty::Subs->list ) {
+<script>new Jifty.Subs({}).start();</script>
+% }
</body>
</html>
% Jifty->handler->stash->{'in_body'} = 0;
Added: jifty/trunk/t/TestApp/t/instance_id.t
==============================================================================
--- (empty file)
+++ jifty/trunk/t/TestApp/t/instance_id.t Fri Oct 27 03:58:30 2006
@@ -0,0 +1,25 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+
+=head1 DESCRIPTION
+
+This is a template for your own tests. Copy it and modify it.
+
+=cut
+
+use lib 't/lib';
+use Jifty::SubTest;
+
+use Jifty::Test tests => 3;
+
+ok(1, "Loaded the test script");
+
+my $app_instance = Jifty->app_instance_id;
+ok(Jifty->app_instance_id, "We have an instance id ". Jifty->app_instance_id);
+is($app_instance, Jifty->app_instance_id, "We have an instance id ". Jifty->app_instance_id);
+
+
+1;
+
More information about the Jifty-commit
mailing list