[Jifty-commit] jifty branch, cas-rewrite, created. jifty-1.10228-22-gee75a3b

Jifty commits jifty-commit at lists.jifty.org
Sun Apr 24 18:54:15 EDT 2011


The branch, cas-rewrite has been created
        at  ee75a3b4c4a54efe0fe55441b37bd56da5e6f1d6 (commit)

- Log -----------------------------------------------------------------
commit ed20dfb6e0be70cea171b06846a1ccd4f01e40f8
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Fri Jun 25 20:11:28 2010 -0400

     Jifty::CAS no longer ISA Jifty::CAS::Store, with stupid ISA tricks
    
    Instead, it has-a Jifty::CAS::Store, and proxies the relevant methods
    into it.

diff --git a/lib/Jifty.pm b/lib/Jifty.pm
index 4dd88af..3f9c021 100644
--- a/lib/Jifty.pm
+++ b/lib/Jifty.pm
@@ -167,10 +167,7 @@ sub new {
     push @Jifty::Record::ISA, $record_base_class unless $record_base_class eq 'Jifty::Record';
 
     # Configure the base class for Jifty::CAS
-    @Jifty::CAS::ISA = grep { $_ ne 'Jifty::CAS::Store' } @Jifty::CAS::ISA;
-    my $cas_base = Jifty->config->framework('CAS')->{'BaseClass'};
-    Jifty::Util->require( $cas_base );
-    push @Jifty::CAS::ISA, $cas_base unless $cas_base eq 'Jifty::CAS';
+    Jifty::CAS->setup;
 
     # Logger turn on
     Jifty->logger( Jifty::Logger->new( $args{'logger_component'} ) );
diff --git a/lib/Jifty/CAS.pm b/lib/Jifty/CAS.pm
index 5a4b55d..97939e3 100644
--- a/lib/Jifty/CAS.pm
+++ b/lib/Jifty/CAS.pm
@@ -2,7 +2,6 @@ use strict;
 use warnings;
 
 package Jifty::CAS;
-use base 'Jifty::CAS::Store';
 use Plack::Request;
 use Plack::Response;
 
@@ -115,4 +114,26 @@ sub _serve_404 {
     return Plack::Response->new(404)->finalize;
 }
 
+my $BACKEND;
+sub setup {
+    my $class = shift;
+    $BACKEND = Jifty->config->framework('CAS')->{'BaseClass'};
+    Jifty::Util->require( $BACKEND );
+}
+
+sub publish {
+    my $class = shift;
+    $BACKEND->publish(@_);
+}
+
+sub key {
+    my $class = shift;
+    $BACKEND->key(@_);
+}
+
+sub retrieve {
+    my $class = shift;
+    $BACKEND->retrieve(@_);
+}
+
 1;

commit 1c473186d13bfa1c3d02b11d0dafe9474f85afa9
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Fri Jun 25 20:12:45 2010 -0400

    Make Jifty::CAS::Store into an instance, and Moose-ify

diff --git a/lib/Jifty/CAS.pm b/lib/Jifty/CAS.pm
index 97939e3..ccd4430 100644
--- a/lib/Jifty/CAS.pm
+++ b/lib/Jifty/CAS.pm
@@ -117,8 +117,9 @@ sub _serve_404 {
 my $BACKEND;
 sub setup {
     my $class = shift;
-    $BACKEND = Jifty->config->framework('CAS')->{'BaseClass'};
-    Jifty::Util->require( $BACKEND );
+    my $store_class = Jifty->config->framework('CAS')->{'BaseClass'};
+    Jifty::Util->require( $store_class );
+    $BACKEND = $store_class->new;
 }
 
 sub publish {
diff --git a/lib/Jifty/CAS/Store.pm b/lib/Jifty/CAS/Store.pm
index 390955d..ce2c330 100644
--- a/lib/Jifty/CAS/Store.pm
+++ b/lib/Jifty/CAS/Store.pm
@@ -2,6 +2,7 @@ use strict;
 use warnings;
 
 package Jifty::CAS::Store;
+use Any::Moose;
 
 =head1 NAME
 
@@ -28,7 +29,7 @@ Returns the key.
 =cut
 
 sub publish {
-    my ($class, $domain, $name, $content, $opt) = @_;
+    my ($self, $domain, $name, $content, $opt) = @_;
     $opt ||= {};
 
     my $blob = Jifty::CAS::Blob->new(
@@ -36,7 +37,7 @@ sub publish {
             metadata => $opt,
         }
     );
-    return $class->_store( $domain, $name, $blob );
+    return $self->_store( $domain, $name, $blob );
 }
 
 =head2 _store DOMAIN NAME BLOB
@@ -46,7 +47,7 @@ Stores the BLOB (a L<Jifty::CAS::Blob>) in the backend.  Returns the key.  Don't
 =cut
 
 sub _store {
-    my ($class, $domain, $name, $blob) = @_;
+    my ($self, $domain, $name, $blob) = @_;
     my $db  = $CONTAINER{$domain} ||= {};
     my $key = $blob->key;
     $db->{DB}{$key} = $blob;
@@ -62,7 +63,7 @@ C<NAME>, or undef if none such exists.
 =cut
 
 sub key {
-    my ($class, $domain, $name) = @_;
+    my ($self, $domain, $name) = @_;
     return $CONTAINER{$domain}{KEYS}{$name};
 }
 
@@ -74,8 +75,10 @@ C<KEY>, or undef if none such exists.
 =cut
 
 sub retrieve {
-    my ($class, $domain, $key) = @_;
+    my ($self, $domain, $key) = @_;
     return $CONTAINER{$domain}{DB}{$key};
 }
 
+no Any::Moose;
+__PACKAGE__->meta->make_immutable(inline_constructor => 0);
 1;
diff --git a/lib/Jifty/CAS/Store/Memcached.pm b/lib/Jifty/CAS/Store/Memcached.pm
index b4ca792..a977285 100644
--- a/lib/Jifty/CAS/Store/Memcached.pm
+++ b/lib/Jifty/CAS/Store/Memcached.pm
@@ -2,8 +2,10 @@ use strict;
 use warnings;
 
 package Jifty::CAS::Store::Memcached;
+use Any::Moose;
+extends 'Jifty::CAS::Store';
 
-use base 'Jifty::CAS::Store';
+use Cache::Memcached;
 
 =head1 NAME
 
@@ -42,8 +44,6 @@ Jifty's CAS, see L<Jifty::CAS/DESCRIPTION>.
 
 =cut
 
-use Cache::Memcached;
-
 our $MEMCACHED;
 
 
@@ -67,11 +67,11 @@ success or undef on failure.
 =cut
 
 sub _store {
-    my ($class, $domain, $name, $blob) = @_;
+    my ($self, $domain, $name, $blob) = @_;
 
     # Default to expiring in two weeks. XXX TODO this should be configurable
     my $key = $blob->key;
-    my $success = $class->memcached->set("$domain:db:$key", $blob, 60*60*24*14);
+    my $success = $self->memcached->set("$domain:db:$key", $blob, 60*60*24*14);
 
     unless ($success) {
         my $err = "Failed to store content for key '$domain:db:$key' in memcached!";
@@ -82,10 +82,10 @@ sub _store {
         }
         Jifty->log->error($err);
 
-        if ( $class->memcached_fallback ) {
+        if ( $self->memcached_fallback ) {
             Jifty->log->error("Falling back to default, in-process memory store.  "
                              ."This is suboptimal and you should investigate the cause.");
-            return $class->SUPER::_store($domain, $name, $blob);
+            return $self->SUPER::_store($domain, $name, $blob);
         }
         else {
             # fail with undef
@@ -93,7 +93,7 @@ sub _store {
         }
     }
 
-    $success = $class->memcached->set("$domain:keys:$name", $key, 60*60*24*14);
+    $success = $self->memcached->set("$domain:keys:$name", $key, 60*60*24*14);
 
     unless ($success) {
         Jifty->log->error("Failed to store key '$domain:keys:$name' in memcached!");
@@ -111,8 +111,8 @@ C<NAME>, or undef if none such exists.
 =cut
 
 sub key {
-    my ($class, $domain, $name) = @_;
-    my $key = $class->memcached->get("$domain:keys:$name");
+    my ($self, $domain, $name) = @_;
+    my $key = $self->memcached->get("$domain:keys:$name");
     return $key if defined $key;
     return $class->SUPER::key($domain, $name) if $class->memcached_fallback;
     return;
@@ -126,8 +126,8 @@ C<KEY>, or undef if none such exists.
 =cut
 
 sub retrieve {
-    my ($class, $domain, $key) = @_;
-    my $blob = $class->memcached->get("$domain:db:$key");
+    my ($self, $domain, $key) = @_;
+    my $blob = $self->memcached->get("$domain:db:$key");
     return $blob if defined $blob;
     return $class->SUPER::retrieve($domain, $key) if $class->memcached_fallback;
     return;
@@ -175,4 +175,6 @@ sub memcached_fallback {
     Jifty->config->framework('CAS')->{'MemcachedFallback'} ? 1 : 0
 }
 
+no Any::Moose;
+__PACKAGE__->meta->make_immutable(inline_constructor => 0);
 1;

commit 07f999e4e5d7bf99227ae5d4f5045d87c7005ac0
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Fri Jun 25 19:20:33 2010 -0400

    There is no reason the memcached CAS should have an explicit expiration

diff --git a/lib/Jifty/CAS/Store/Memcached.pm b/lib/Jifty/CAS/Store/Memcached.pm
index a977285..ed735aa 100644
--- a/lib/Jifty/CAS/Store/Memcached.pm
+++ b/lib/Jifty/CAS/Store/Memcached.pm
@@ -69,9 +69,8 @@ success or undef on failure.
 sub _store {
     my ($self, $domain, $name, $blob) = @_;
 
-    # Default to expiring in two weeks. XXX TODO this should be configurable
     my $key = $blob->key;
-    my $success = $self->memcached->set("$domain:db:$key", $blob, 60*60*24*14);
+    my $success = $self->memcached->set("$domain:db:$key", $blob);
 
     unless ($success) {
         my $err = "Failed to store content for key '$domain:db:$key' in memcached!";
@@ -93,7 +92,7 @@ sub _store {
         }
     }
 
-    $success = $self->memcached->set("$domain:keys:$name", $key, 60*60*24*14);
+    $success = $self->memcached->set("$domain:keys:$name", $key);
 
     unless ($success) {
         Jifty->log->error("Failed to store key '$domain:keys:$name' in memcached!");

commit 0e0c656de2c4423ca6d051e2d600212653f2fcbc
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Fri Jun 25 19:24:55 2010 -0400

    Split the memory cache out into its own class, making Jifty::CAS::Store abstract

diff --git a/lib/Jifty/CAS.pm b/lib/Jifty/CAS.pm
index ccd4430..73fcd3f 100644
--- a/lib/Jifty/CAS.pm
+++ b/lib/Jifty/CAS.pm
@@ -33,11 +33,11 @@ recent key provided with that name.
 
 =head1 BACKENDS
 
-The default data store is an per-process, in-memory store.  A
-L<memcached|Jifty::CAS::Store::Memcached> backed store is also available and
-has the benefits of sharing the cache across all instances of a Jifty app using
-Jifty::CAS.  The memcached store is limited to objects less than 1MB in size,
-however.
+The default data store is a per-process, in-memory store via
+L<Jifty::CAS::Store::Memory>.  L<Jifty::CAS::Store::Memcached> is also
+available, and has the benefits of sharing the cache across all
+instances of a Jifty app using L<Jifty::CAS>.  The memcached store is
+limited to objects less than 1MB in size, however.
 
 =head1 METHODS
 
diff --git a/lib/Jifty/CAS/Store.pm b/lib/Jifty/CAS/Store.pm
index ce2c330..23530fb 100644
--- a/lib/Jifty/CAS/Store.pm
+++ b/lib/Jifty/CAS/Store.pm
@@ -6,20 +6,17 @@ use Any::Moose;
 
 =head1 NAME
 
-Jifty::CAS::Store - The default, per-process, in-memory store for Jifty's
-Content-Addressable Storage facility
+Jifty::CAS::Store - Abstract class for Jifty's Content-Addressed Storage
 
 =head1 DESCRIPTION
 
-This is the default backend store for L<Jifty::CAS>.  For more information, see
-L<Jifty::CAS/DESCRIPTION>.
+This is the abstract base class for a backend store for L<Jifty::CAS>.
+For more information, see L<Jifty::CAS/DESCRIPTION>.
 
 =cut
 
 use Jifty::CAS::Blob;
 
-my %CONTAINER;
-
 =head2 publish DOMAIN NAME CONTENT METADATA
 
 Publishes the given C<CONTENT> at the address C<DOMAIN> and C<NAME>.
@@ -42,41 +39,36 @@ sub publish {
 
 =head2 _store DOMAIN NAME BLOB
 
-Stores the BLOB (a L<Jifty::CAS::Blob>) in the backend.  Returns the key.  Don't use this directly, use C<publish> instead.
+Stores the BLOB (a L<Jifty::CAS::Blob>) in the backend.  Returns the
+key.  Subclasses should override this, but it should not be called
+directly -- use L</publish> instead.
 
 =cut
 
 sub _store {
-    my ($self, $domain, $name, $blob) = @_;
-    my $db  = $CONTAINER{$domain} ||= {};
-    my $key = $blob->key;
-    $db->{DB}{$key} = $blob;
-    $db->{KEYS}{$name} = $key;
-    return $key;
+    die "This is an abstract base class; use one of the provided subclasses instead\n";
 }
 
 =head2 key DOMAIN NAME
 
-Returns the most recent key for the given pair of C<DOMAIN> and
-C<NAME>, or undef if none such exists.
+Returns the most recent key for the given pair of C<DOMAIN> and C<NAME>,
+or undef if none such exists.  Subclasses should override this.
 
 =cut
 
 sub key {
-    my ($self, $domain, $name) = @_;
-    return $CONTAINER{$domain}{KEYS}{$name};
+    die "This is an abstract base class; use one of the provided subclasses instead\n";
 }
 
 =head2 retrieve DOMAIN KEY
 
 Returns a L<Jifty::CAS::Blob> for the given pair of C<DOMAIN> and
-C<KEY>, or undef if none such exists.
+C<KEY>, or undef if none such exists.  Subclasses should override this.
 
 =cut
 
 sub retrieve {
-    my ($self, $domain, $key) = @_;
-    return $CONTAINER{$domain}{DB}{$key};
+    die "This is an abstract base class; use one of the provided subclasses instead\n";
 }
 
 no Any::Moose;
diff --git a/lib/Jifty/CAS/Store.pm b/lib/Jifty/CAS/Store/Memory.pm
similarity index 55%
copy from lib/Jifty/CAS/Store.pm
copy to lib/Jifty/CAS/Store/Memory.pm
index ce2c330..c1eb3b8 100644
--- a/lib/Jifty/CAS/Store.pm
+++ b/lib/Jifty/CAS/Store/Memory.pm
@@ -1,18 +1,18 @@
 use strict;
 use warnings;
 
-package Jifty::CAS::Store;
+package Jifty::CAS::Store::Memory;
 use Any::Moose;
+extends 'Jifty::CAS::Store';
 
 =head1 NAME
 
-Jifty::CAS::Store - The default, per-process, in-memory store for Jifty's
-Content-Addressable Storage facility
+Jifty::CAS::Store::Memory - An single-process in-memory CAS store
 
 =head1 DESCRIPTION
 
-This is the default backend store for L<Jifty::CAS>.  For more information, see
-L<Jifty::CAS/DESCRIPTION>.
+This is the default backend store for L<Jifty::CAS>.  For more
+information, see L<Jifty::CAS/DESCRIPTION>.
 
 =cut
 
@@ -20,29 +20,10 @@ use Jifty::CAS::Blob;
 
 my %CONTAINER;
 
-=head2 publish DOMAIN NAME CONTENT METADATA
-
-Publishes the given C<CONTENT> at the address C<DOMAIN> and C<NAME>.
-C<METADATA> is an arbitrary hash; see L<Jifty::CAS::Blob> for more.
-Returns the key.
-
-=cut
-
-sub publish {
-    my ($self, $domain, $name, $content, $opt) = @_;
-    $opt ||= {};
-
-    my $blob = Jifty::CAS::Blob->new(
-        {   content  => $content,
-            metadata => $opt,
-        }
-    );
-    return $self->_store( $domain, $name, $blob );
-}
-
 =head2 _store DOMAIN NAME BLOB
 
-Stores the BLOB (a L<Jifty::CAS::Blob>) in the backend.  Returns the key.  Don't use this directly, use C<publish> instead.
+Stores the BLOB (a L<Jifty::CAS::Blob>) in the backend.  Returns the
+key.  Don't use this directly, use C<publish> instead.
 
 =cut
 
diff --git a/lib/Jifty/Config.pm b/lib/Jifty/Config.pm
index 1139190..d62387c 100644
--- a/lib/Jifty/Config.pm
+++ b/lib/Jifty/Config.pm
@@ -626,7 +626,7 @@ sub defaults {
                 SessionCookieName => 'JIFTY_SID_$PORT',
             },
             CAS => {
-                BaseClass => 'Jifty::CAS::Store',
+                BaseClass => 'Jifty::CAS::Store::Memory',
                 Memcached => {
                     servers     => [ '127.0.0.1:11211' ],
                     debug       => 0,

commit ece53d59487a4637c761e2009cb7a83360278043
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Fri Jun 25 19:51:52 2010 -0400

    Support different CAS backends for different domains; config version bump

diff --git a/lib/Jifty/CAS.pm b/lib/Jifty/CAS.pm
index 73fcd3f..2e32c1b 100644
--- a/lib/Jifty/CAS.pm
+++ b/lib/Jifty/CAS.pm
@@ -74,6 +74,22 @@ code set by this method (possibly for your use in the dispatcher).
 
 =cut
 
+sub config {
+    my $class = shift;
+    my $config = Jifty->config->framework('CAS');
+    if (Jifty->config->framework('ConfigFileVersion') < 6) {
+        $config = {
+            Default => {
+                Class => $config->{'BaseClass'},
+                %{ $config->{'Memcached'} || {} },
+            }
+        };
+    }
+    $config->{Default}{Class} ||= "Jifty::CAS::Store::Memory";
+    $config->{Domains} ||= {};
+    return $config;
+}
+
 sub serve_by_name {
     my ($class, $domain, $name, $incoming_key, $env) = @_;
     my $key = Jifty::CAS->key($domain, $name);
@@ -107,6 +123,29 @@ sub serve_by_name {
     return $res->finalize;
 }
 
+my %BACKENDS;
+my $DEFAULT_BACKEND;
+sub setup {
+    my $class = shift;
+    my $config = $class->config;
+
+    my %default = %{$config->{Default}};
+    my $defaultclass = delete $default{Class};
+    Jifty::Util->require( $defaultclass );
+    $DEFAULT_BACKEND = $defaultclass->new(
+        map {lc $_ => $config->{Default}{$_}} keys %default
+    );
+
+    for my $domain (keys %{$config->{Domains}}) {
+        my %domain = %{ $config->{Domains}{$domain} };
+        my $storeclass = delete $domain{Class};
+        Jifty::Util->require( $storeclass );
+        $BACKENDS{$domain} = $storeclass->new(
+            map {lc $_ => $config->{Domains}{$domain}{$_}} keys %domain
+        );
+    }
+}
+
 sub _serve_404 {
     my ($class, $domain, $name, $msg) = @_;
     $msg ||= '';
@@ -114,27 +153,28 @@ sub _serve_404 {
     return Plack::Response->new(404)->finalize;
 }
 
-my $BACKEND;
-sub setup {
+sub backend {
     my $class = shift;
-    my $store_class = Jifty->config->framework('CAS')->{'BaseClass'};
-    Jifty::Util->require( $store_class );
-    $BACKEND = $store_class->new;
+    my ($domain) = @_;
+    return $BACKENDS{$domain} || $DEFAULT_BACKEND;
 }
 
 sub publish {
     my $class = shift;
-    $BACKEND->publish(@_);
+    my ($domain) = @_;
+    ($BACKENDS{$domain} || $DEFAULT_BACKEND)->publish(@_);
 }
 
 sub key {
     my $class = shift;
-    $BACKEND->key(@_);
+    my ($domain) = @_;
+    ($BACKENDS{$domain} || $DEFAULT_BACKEND)->key(@_);
 }
 
 sub retrieve {
     my $class = shift;
-    $BACKEND->retrieve(@_);
+    my ($domain) = @_;
+    ($BACKENDS{$domain} || $DEFAULT_BACKEND)->retrieve(@_);
 }
 
 1;
diff --git a/lib/Jifty/CAS/Store/Memcached.pm b/lib/Jifty/CAS/Store/Memcached.pm
index ed735aa..f3a0955 100644
--- a/lib/Jifty/CAS/Store/Memcached.pm
+++ b/lib/Jifty/CAS/Store/Memcached.pm
@@ -4,13 +4,17 @@ use warnings;
 package Jifty::CAS::Store::Memcached;
 use Any::Moose;
 extends 'Jifty::CAS::Store';
+has 'servers'            => (is => 'rw');
+has 'debug'              => (is => 'rw');
+has 'namespace'          => (is => 'rw');
+has 'compress_threshold' => (is => 'rw');
+has 'memcached'          => (is => 'rw');
 
 use Cache::Memcached;
 
 =head1 NAME
 
-Jifty::CAS::Store::Memcached - A memcached backend for Jifty's
-Content-Addressable Storage facility
+Jifty::CAS::Store::Memcached - A memcached backend for Jifty's CAS
 
 =head1 SYNOPSIS
 
@@ -18,24 +22,20 @@ At the bare minimum, add the following to your Jifty config.yml:
 
     framework:
       CAS:
-        BaseClass: 'Jifty::CAS::Store::Memcached'
+        Default:
+          Class: 'Jifty::CAS::Store::Memcached'
 
 The options available include:
 
     framework:
       CAS:
-        BaseClass: 'Jifty::CAS::Store::Memcached'
-        Memcached:
+        Default:
+          Class: 'Jifty::CAS::Store::Memcached'
           # any options Cache::Memcached supports
-          servers:
+          Servers:
             - 10.0.0.2:11211
             - 10.0.0.3:11211
-          compress_threshold: 5120
-
-        # Turned on by default. Keeps CAS working when memcached fails by
-        # falling back to the default in-process store. It probably should
-        # be turned off in most cases (like so) after successful testing.
-        MemcachedFallback: 0
+          Compress_Threshold: 5120
 
 =head1 DESCRIPTION
 
@@ -44,8 +44,17 @@ Jifty's CAS, see L<Jifty::CAS/DESCRIPTION>.
 
 =cut
 
-our $MEMCACHED;
-
+sub BUILD {
+    my $self = shift;
+    $self->memcached(
+        Cache::Memcached->new(
+            servers => $self->servers || [ '127.0.0.1:11211' ],
+            debug => $self->debug,
+            namespace => $self->namespace || Jifty->config->framework('ApplicationName').":CAS:",
+            compress_threshold => $self->compress_threshold || 10240,
+        )
+    );
+}
 
 =head1 METHODS
 
@@ -55,10 +64,6 @@ Returns the L<Cache::Memcached> object for this class.
 
 =cut
 
-sub memcached {
-    $MEMCACHED ||= Cache::Memcached->new( $_[0]->memcached_config );
-}
-
 =head2 _store DOMAIN NAME BLOB
 
 Stores the BLOB (a L<Jifty::CAS::Blob>) in memcached.  Returns the key on
@@ -81,15 +86,8 @@ sub _store {
         }
         Jifty->log->error($err);
 
-        if ( $self->memcached_fallback ) {
-            Jifty->log->error("Falling back to default, in-process memory store.  "
-                             ."This is suboptimal and you should investigate the cause.");
-            return $self->SUPER::_store($domain, $name, $blob);
-        }
-        else {
-            # fail with undef
-            return;
-        }
+        # fail with undef
+        return;
     }
 
     $success = $self->memcached->set("$domain:keys:$name", $key);
@@ -113,7 +111,6 @@ sub key {
     my ($self, $domain, $name) = @_;
     my $key = $self->memcached->get("$domain:keys:$name");
     return $key if defined $key;
-    return $class->SUPER::key($domain, $name) if $class->memcached_fallback;
     return;
 }
 
@@ -128,52 +125,9 @@ sub retrieve {
     my ($self, $domain, $key) = @_;
     my $blob = $self->memcached->get("$domain:db:$key");
     return $blob if defined $blob;
-    return $class->SUPER::retrieve($domain, $key) if $class->memcached_fallback;
     return;
 }
 
-=head2 memcached_config
-
-Returns a hashref containing arguments to pass to L<Cache::Memcached> during
-construction. The defaults are like:
-
-  {
-      servers     => [ '127.0.0.1:11211' ],
-      debug       => 0,
-      namespace   => Jifty->config->framework('ApplicationName'),
-      compress_threshold => 10240,
-  }
-
-To change these options, set them in your Jifty application config file under
-C</framework/CAS/Memcached> like so:
-
-    framework:
-      CAS:
-        BaseClass: 'Jifty::CAS::Store::Memcached'
-        Memcached:
-            servers:
-                - 10.0.0.2:11211
-                - 10.0.0.3:11211
-            compress_threshold: 5120
-
-=cut
-
-sub memcached_config {
-    Jifty->config->framework('CAS')->{'Memcached'}
-        || Jifty->config->defaults->{'framework'}{'CAS'}{'Memcached'}
-}
-
-=head2 memcached_fallback
-
-Returns a boolean (from the config file) indicating whether or not memcached
-should fallback to the per-process, in-memory store.
-
-=cut
-
-sub memcached_fallback {
-    Jifty->config->framework('CAS')->{'MemcachedFallback'} ? 1 : 0
-}
-
 no Any::Moose;
 __PACKAGE__->meta->make_immutable(inline_constructor => 0);
 1;
diff --git a/lib/Jifty/Config.pm b/lib/Jifty/Config.pm
index d62387c..2d3c8a2 100644
--- a/lib/Jifty/Config.pm
+++ b/lib/Jifty/Config.pm
@@ -535,7 +535,7 @@ See L<Jifty::Script::App>.
 sub initial_config {
     my $self = shift;
     my $guess = $self->guess(@_);
-    $guess->{'framework'}->{'ConfigFileVersion'} = 5;
+    $guess->{'framework'}->{'ConfigFileVersion'} = 6;
 
     # These are the plugins which new apps will get by default
     $guess->{'framework'}->{'Plugins'} = [
@@ -625,16 +625,6 @@ sub defaults {
                 DefaultTemplateRoot => Jifty::Util->share_root . '/web/templates',
                 SessionCookieName => 'JIFTY_SID_$PORT',
             },
-            CAS => {
-                BaseClass => 'Jifty::CAS::Store::Memory',
-                Memcached => {
-                    servers     => [ '127.0.0.1:11211' ],
-                    debug       => 0,
-                    namespace   => $self->framework('ApplicationName').":",
-                    compress_threshold => 10240,
-                },
-                MemcachedFallback => 1,
-            },
         }
     };
 

commit ea650098f600af4237074b5c9e8ed4b79a8fb4db
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Fri Jun 25 20:01:29 2010 -0400

    Make the CAS do its own serving, as opposed to relying on CCJS
    
    This paves the way for a more generic CAS server, allowing non-CCJS to
    be served.
    
    It also removes the 'cdn' option, which was of limited use because it
    required a complete app mirror.  The more correct solution involves
    either:
     a) publishing other static content (images, etc) to the CDN, and
        rewriting the CSS to point to them
     b) rewriting the CSS to point to the application's standard images
    
    Either of these solutions, since they involve parsing and rewriting CSS,
    is also relevant to hosting a Jifty application not at the server root.

diff --git a/lib/Jifty/CAS.pm b/lib/Jifty/CAS.pm
index 2e32c1b..16376db 100644
--- a/lib/Jifty/CAS.pm
+++ b/lib/Jifty/CAS.pm
@@ -57,20 +57,16 @@ C<NAME>, or undef if none such exists.
 Returns a L<Jifty::CAS::Blob> for the given pair of C<DOMAIN> and
 C<KEY>, or undef if none such exists.
 
-=head2 serve_by_name DOMAIN NAME REQUESTED_KEY
+=head2 uri DOMAIN NAME
 
-Intelligently serves up the content of the object at NAME (B<not>
-REQUESTED_KEY) in DOMAIN.  REQUESTED_KEY is currently used only to check if the
-content at NAME equals the content requested.  If so, this method responds with
-an HTTP 304 status, indicating the content hasn't changed.  This use case
-assumes that content is served to clients from the CAS with the CAS key (an MD5
-sum) as the filename or part of it.
+Returns a URL where the given C<DOMAIN> and C<NAME> can be accessed.
 
-The C<content_type> key in the requested object's metadata is expected to be
-set and is used for the HTTP response.
+=head2 serve DOMAIN ARGUMENT ENV
 
-This method is usually called from a dispatcher rule.  Returns the HTTP status
-code set by this method (possibly for your use in the dispatcher).
+Serves a plack request in C<ENV>, given a C<DOMAIN> and an C<ARGUMENT>,
+which may wither be a key or a name.  This method is usually only called
+by L</wrap>, which calls it as appropriate for all requests under
+C</__jifty/cas/>.
 
 =cut
 
@@ -90,39 +86,6 @@ sub config {
     return $config;
 }
 
-sub serve_by_name {
-    my ($class, $domain, $name, $incoming_key, $env) = @_;
-    my $key = Jifty::CAS->key($domain, $name);
-
-    return $class->_serve_404( $domain, $name, "Unable to lookup key." )
-        if not defined $key;
-
-    my $res = Plack::Response->new(200);
-    my $req = Plack::Request->new($env);
-    if ( $req->header('If-Modified-Since') and $incoming_key eq $key ) {
-        Jifty->log->debug("Returning 304 for CAS cached $domain:$name ($key)");
-        $res->status(304);
-        return $res->finalize;
-    }
-
-    my $obj = Jifty::CAS->retrieve($domain, $key);
-
-    return $class->_serve_404( $domain, $name, "Unable to retrieve blob." )
-        if not defined $obj;
-
-    $res->content_type($obj->metadata->{content_type});
-    $res->header( 'Cache-Control' => 'max-age=31536000, public' );
-    $res->header( 'Expires' => HTTP::Date::time2str( time() + 31536000 ) );
-    $res->header( 'Content-Length' => length($obj->content) );
-    $res->header(
-      'Last-Modified' => HTTP::Date::time2str( $obj->metadata->{time} ) );
-
-    Jifty->log->debug("Sending squished $domain:$name ($key) from CAS");
-    $res->body($obj->content);
-
-    return $res->finalize;
-}
-
 my %BACKENDS;
 my $DEFAULT_BACKEND;
 sub setup {
@@ -146,11 +109,18 @@ sub setup {
     }
 }
 
-sub _serve_404 {
-    my ($class, $domain, $name, $msg) = @_;
-    $msg ||= '';
-    Jifty->log->error("Returning 404 for CAS cached $domain:$name.  $msg");
-    return Plack::Response->new(404)->finalize;
+sub wrap {
+    my ($class, $app) = @_;
+
+    sub {
+        my $env = shift;
+        if (my ($domain, $arg) = $env->{PATH_INFO} =~ m{/__jifty/cas/(.*?)/(.*?)(?:\.|$)}) {
+            return $class->serve($domain,$arg,$env);
+        }
+        else {
+            return $app->($env);
+        }
+    };
 }
 
 sub backend {
@@ -177,4 +147,16 @@ sub retrieve {
     ($BACKENDS{$domain} || $DEFAULT_BACKEND)->retrieve(@_);
 }
 
+sub uri {
+    my $class = shift;
+    my ($domain) = @_;
+    ($BACKENDS{$domain} || $DEFAULT_BACKEND)->uri(@_);
+}
+
+sub serve {
+    my $class = shift;
+    my ($domain) = @_;
+    ($BACKENDS{$domain} || $DEFAULT_BACKEND)->serve(@_);
+}
+
 1;
diff --git a/lib/Jifty/CAS/Store.pm b/lib/Jifty/CAS/Store.pm
index 23530fb..54b00f7 100644
--- a/lib/Jifty/CAS/Store.pm
+++ b/lib/Jifty/CAS/Store.pm
@@ -71,6 +71,63 @@ sub retrieve {
     die "This is an abstract base class; use one of the provided subclasses instead\n";
 }
 
+=head2 uri DOMAIN NAME
+
+Returns a URL where the given C<DOMAIN> and C<NAME> can be accessed.
+
+=cut
+
+sub uri {
+    my $self = shift;
+    my ($domain, $name) = @_;
+    return "/__jifty/cas/$domain/" . $self->key($domain, $name);
+}
+
+sub serve {
+    my ($self, $domain, $arg, $env) = @_;
+
+    my $key;
+    if ($arg =~ /^[a-f0-9]{32}$/) {
+        $key = $arg;
+    } else {
+        $key = $self->key($domain, $arg);
+        return $self->_serve_404( $domain, $arg, "Unable to lookup key." )
+            if not defined $key;
+    }
+
+    my $req = Plack::Request->new($env);
+    my $etag = $req->header('If-None-Match');
+    if ( defined $etag and $etag eq qq["$key"] ) {
+        Jifty->log->info("Returning 304 for CAS cached $domain:$key");
+        return Plack::Response->new(304)->finalize;
+    }
+
+    my $obj = Jifty::CAS->retrieve($domain, $key);
+    return $self->_serve_404( $domain, $key, "Unable to retrieve blob." )
+        if not defined $obj;
+
+    my $res = Plack::Response->new(200);
+    $res->content_type($obj->metadata->{content_type});
+    $res->header( 'Cache-Control' => 'max-age=31536000, public' );
+    $res->header( 'Expires' => HTTP::Date::time2str( time() + 31536000 ) );
+    $res->header( 'ETag' => '"'.$obj->key.'"' );
+    $res->header( 'Content-Length' => length($obj->content) );
+    $res->header(
+      'Last-Modified' => HTTP::Date::time2str( $obj->metadata->{time} ) );
+
+    Jifty->log->info("Sending squished @{[$obj->key]} from CAS");
+    $res->body($obj->content);
+
+    return $res->finalize;
+}
+
+sub _serve_404 {
+    my ($self, $domain, $name, $msg) = @_;
+    $msg ||= '';
+    Jifty->log->error("Returning 404 for CAS cached $domain:$name.  $msg");
+    return Plack::Response->new(404)->finalize;
+}
+
 no Any::Moose;
 __PACKAGE__->meta->make_immutable(inline_constructor => 0);
 1;
diff --git a/lib/Jifty/Handler.pm b/lib/Jifty/Handler.pm
index af7bb9b..b7a4a5b 100644
--- a/lib/Jifty/Handler.pm
+++ b/lib/Jifty/Handler.pm
@@ -194,6 +194,9 @@ sub psgi_app {
     }
         if Jifty->config->framework("Web")->{PSGIStatic} && $static;
 
+    # CAS wrapper
+    $app = Jifty::CAS->wrap($app);
+
     # allow plugin to wrap $app
     for ( Jifty->plugins ) {
         $app = $_->wrap($app);
diff --git a/lib/Jifty/Plugin/CompressedCSSandJS.pm b/lib/Jifty/Plugin/CompressedCSSandJS.pm
index 627ba6f..d133a79 100644
--- a/lib/Jifty/Plugin/CompressedCSSandJS.pm
+++ b/lib/Jifty/Plugin/CompressedCSSandJS.pm
@@ -24,7 +24,6 @@ Jifty::Plugin::CompressedCSSandJS - Compression of CSS and javascript files
         js: 1
         css: 1
         jsmin: /path/to/jsmin
-        cdn: 'http://yourcdn.for.static.prefix/'
         skipped_js:
             - complex.js
         generate_early: 1
@@ -52,7 +51,7 @@ by default.
 
 =cut
 
-__PACKAGE__->mk_accessors(qw(css js jsmin cdn skipped_js generate_early));
+__PACKAGE__->mk_accessors(qw(css js jsmin skipped_js generate_early));
 
 =head2 init
 
@@ -70,7 +69,6 @@ sub init {
     $self->css( $opt{css} );
     $self->js( $opt{js} );
     $self->jsmin( $opt{jsmin} );
-    $self->cdn( $opt{cdn} || '');
     $self->generate_early( exists $opt{generate_early} ? $opt{generate_early} : 1 );
 
     if ( $self->js_enabled ) {
@@ -121,8 +119,8 @@ sub _include_javascript {
 
     $self->generate_javascript;
     Jifty->web->out(
-        qq[<script type="text/javascript" src="@{[ $self->cdn ]}/__jifty/js/]
-          . Jifty::CAS->key( 'ccjs', 'js-all' )
+        qq[<script type="text/javascript" src="]
+          . Jifty::CAS->uri("ccjs","js-all")
           . qq[.js"></script>] );
 
     my $skipped_js = $self->skipped_js;
@@ -139,8 +137,8 @@ sub _include_css {
     my $self = shift;
     $self->generate_css;
     Jifty->web->out(
-    qq{<link rel="stylesheet" type="text/css" href="@{[ $self->cdn ]}/__jifty/css/}
-    . Jifty::CAS->key('ccjs', 'css-all') . '.css" />');
+    qq{<link rel="stylesheet" type="text/css" href="}
+        . Jifty::CAS->uri('ccjs', 'css-all').'.css" />');
     return 0;
 }
 
@@ -269,36 +267,4 @@ sub _js_is_skipped {
     return grep { $file eq $_ } @{ $self->skipped_js };
 }
 
-=head2 wrap
-
-psgi app wrapper to serve url controlled by us
-
-=cut
-
-sub wrap {
-    my ($self, $app) = @_;
-
-    sub {
-        my $env = shift;
-        if (my ($mode, $arg) = $env->{PATH_INFO} =~ m{/__jifty/(css|js)/(.*)}) {
-            if ( $arg !~ /^[0-9a-f]{32}\.$mode$/ ) {
-                # This doesn't look like a real request for squished JS or CSS,
-                # so redirect to a more failsafe place
-                my $res = Plack::Response->new;
-                $res->redirect( "/static/$mode/$arg" );
-                return $res->finalize;
-            }
-
-            my $method = "generate_".($mode eq 'js' ? 'javascript' : 'css');
-            $self->can($method)->($self);
-            $arg =~ s/\.$mode//;
-            return Jifty::CAS->serve_by_name( 'ccjs', $mode.'-all', $arg, $env );
-        }
-        else {
-            return $app->($env);
-        }
-    };
-}
-
-
 1;

commit c6a9a458e1b763483469b18b122fffb124b6b4b9
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Sun Apr 24 16:04:01 2011 -0400

    Add a way for a CAS backend to advertize itself as "durable"

diff --git a/lib/Jifty/CAS/Store.pm b/lib/Jifty/CAS/Store.pm
index 54b00f7..496bd5e 100644
--- a/lib/Jifty/CAS/Store.pm
+++ b/lib/Jifty/CAS/Store.pm
@@ -128,6 +128,16 @@ sub _serve_404 {
     return Plack::Response->new(404)->finalize;
 }
 
+=head2 durable
+
+Returns true if the backing store is durable -- that is, if there is a
+guarantee that data placed there will be accessible from all proccesses at
+all later times.
+
+=cut
+
+sub durable { 0 }
+
 no Any::Moose;
 __PACKAGE__->meta->make_immutable(inline_constructor => 0);
 1;

commit b6ba25ce595b872f97eb9118d8acbf041075539b
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Fri Jun 25 20:03:51 2010 -0400

    Add a durable local file CAS
    
    This allows for a durable backend for generated CAS data, unlike memcached.

diff --git a/lib/Jifty/CAS.pm b/lib/Jifty/CAS.pm
index 16376db..d17cebc 100644
--- a/lib/Jifty/CAS.pm
+++ b/lib/Jifty/CAS.pm
@@ -38,6 +38,8 @@ L<Jifty::CAS::Store::Memory>.  L<Jifty::CAS::Store::Memcached> is also
 available, and has the benefits of sharing the cache across all
 instances of a Jifty app using L<Jifty::CAS>.  The memcached store is
 limited to objects less than 1MB in size, however.
+L<Jifty::CAS::Store::LocalFile> provides a durable store, which is
+well-suited for sharing the cache across instances and restarts.
 
 =head1 METHODS
 
diff --git a/lib/Jifty/CAS/Store/LocalFile.pm b/lib/Jifty/CAS/Store/LocalFile.pm
new file mode 100644
index 0000000..a0afb65
--- /dev/null
+++ b/lib/Jifty/CAS/Store/LocalFile.pm
@@ -0,0 +1,107 @@
+use strict;
+use warnings;
+
+package Jifty::CAS::Store::LocalFile;
+use Any::Moose;
+extends 'Jifty::CAS::Store';
+has 'path' => ( is => 'rw');
+
+use Storable qw(lock_store lock_retrieve);
+
+=head1 NAME
+
+Jifty::CAS::Store::LocalFile - A local file backend for Jifty's CAS
+
+=head1 DESCRIPTION
+
+This is a local file backend for L<Jifty::CAS>, which provides a
+B<durable> backend, unlike L<Jifty::CAS::Store::Memory> and
+L<Jifty::CAS::Store::Memcached>.  For more information about Jifty's
+CAS, see L<Jifty::CAS/DESCRIPTION>.
+
+Configuration requires providing a directory which is writable by the
+web user:
+
+    framework:
+      CAS:
+        Default:
+          Class: 'Jifty::CAS::Store::LocalFile'
+          Path: %var/cas%
+
+=cut
+
+=head1 METHODS
+
+=head2 _store DOMAIN NAME BLOB
+
+Stores the BLOB (a L<Jifty::CAS::Blob>) on disk.  Returns the key on
+success or undef on failure.
+
+=cut
+
+sub _store {
+    my ($self, $domain, $name, $blob) = @_;
+    mkdir($self->path) unless -d $self->path;
+    my $dir = $self->path . "/" . $domain;
+    mkdir($dir) unless -d $dir;
+
+    my $path = $dir . "/key-" . $blob->key;
+    unless (-e $path) {
+        lock_store($blob, $path)
+            or warn("Write of blob failed: $!") and return;
+    }
+
+    # Update the symlink
+    my $link = $dir . "/name-" . $name;
+    my $tmp  = $dir . "/tmp-"  . $name;
+    symlink( "key-".$blob->key, $tmp )
+        or warn("Symlink failed: $!") and return;
+    rename( $tmp, $link )
+        or warn("Rename of symlink failed: $!") and return;
+
+    return $blob->key;
+}
+
+=head2 key DOMAIN NAME
+
+Returns the most recent key for the given pair of C<DOMAIN> and
+C<NAME>, or undef if none such exists.
+
+=cut
+
+sub key {
+    my ($self, $domain, $name) = @_;
+    my $link = $self->path . "/" . $domain . "/name-" . $name;
+    return unless -l $link;
+    $link = readlink($link);
+    $link =~ s/^key-//;
+    return $link;
+}
+
+=head2 retrieve DOMAIN KEY
+
+Returns a L<Jifty::CAS::Blob> for the given pair of C<DOMAIN> and
+C<KEY>, or undef if none such exists.
+
+=cut
+
+sub retrieve {
+    my ($self, $domain, $key) = @_;
+    my $data = $self->path . "/" . $domain . "/key-" . $key;
+    return unless -r $data;
+
+    return lock_retrieve($data);
+}
+
+=head2 durable
+
+Since presumably the files on disk will not simply vanish, the local
+file store is durable.
+
+=cut
+
+sub durable { 1 }
+
+no Any::Moose;
+__PACKAGE__->meta->make_immutable(inline_constructor => 0);
+1;

commit 642cb05fce5ee9823e262875fe08080829df6f36
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Fri Jun 25 20:05:20 2010 -0400

    Add a tool to generate CCJS externally, on command
    
    This allows minification to occur outside the webserver, as it is
    written to a durable store which the webserver can later read.  This is
    key to reducing server startup times.

diff --git a/lib/Jifty/Plugin/CompressedCSSandJS.pm b/lib/Jifty/Plugin/CompressedCSSandJS.pm
index d133a79..cd92dc2 100644
--- a/lib/Jifty/Plugin/CompressedCSSandJS.pm
+++ b/lib/Jifty/Plugin/CompressedCSSandJS.pm
@@ -51,7 +51,7 @@ by default.
 
 =cut
 
-__PACKAGE__->mk_accessors(qw(css js jsmin skipped_js generate_early));
+__PACKAGE__->mk_accessors(qw(css js jsmin skipped_js generate_early external_publish));
 
 =head2 init
 
@@ -70,6 +70,11 @@ sub init {
     $self->js( $opt{js} );
     $self->jsmin( $opt{jsmin} );
     $self->generate_early( exists $opt{generate_early} ? $opt{generate_early} : 1 );
+    $self->external_publish( $opt{external_publish} );
+    if ($self->external_publish and not Jifty::CAS->backend("ccjs")->durable) {
+        $self->log->warn("External publishing does not work with non-durable CAS stores; disabling");
+        $self->external_publish(0);
+    }
 
     if ( $self->js_enabled ) {
         Jifty::Web->add_trigger(
@@ -78,7 +83,7 @@ sub init {
             abortable => 1,
         );
         Jifty->add_trigger( post_init => sub { $self->generate_javascript })
-            if $self->generate_early;
+            if $self->generate_early and not $self->external_publish;
     }
 
     if ( $self->css_enabled ) {
@@ -88,7 +93,7 @@ sub init {
             abortable => 1,
         );
         Jifty->add_trigger( post_init => sub { $self->generate_css })
-            if $self->generate_early;
+            if $self->generate_early and not $self->external_publish;
     }
 }
 
@@ -117,7 +122,7 @@ sub css_enabled {
 sub _include_javascript {
     my $self = shift;
 
-    $self->generate_javascript;
+    $self->generate_javascript unless $self->external_publish;
     Jifty->web->out(
         qq[<script type="text/javascript" src="]
           . Jifty::CAS->uri("ccjs","js-all")
@@ -135,7 +140,7 @@ sub _include_javascript {
 
 sub _include_css {
     my $self = shift;
-    $self->generate_css;
+    $self->generate_css unless $self->external_publish;
     Jifty->web->out(
     qq{<link rel="stylesheet" type="text/css" href="}
         . Jifty::CAS->uri('ccjs', 'css-all').'.css" />');
@@ -153,7 +158,9 @@ and caches it. (In devel mode, it always regenerates it)
 sub generate_css {
     my $self = shift;
 
-    return if Jifty::CAS->key('ccjs', 'css-all') && !Jifty->config->framework('DevelMode');
+    return if !$self->external_publish
+        && Jifty::CAS->key('ccjs', 'css-all')
+        && !Jifty->config->framework('DevelMode');
 
     $self->log->debug("Generating CSS...");
 
@@ -180,7 +187,9 @@ and caches it.
 sub generate_javascript {
     my $self = shift;
 
-    return if Jifty::CAS->key('ccjs', 'js-all') && !Jifty->config->framework('DevelMode');
+    return if !$self->external_publish
+        && Jifty::CAS->key('ccjs', 'js-all')
+        && !Jifty->config->framework('DevelMode');
 
     my $js = $self->_generate_javascript_nocache;
 
diff --git a/lib/Jifty/Script.pm b/lib/Jifty/Script.pm
index b2adc74..b4c1e72 100644
--- a/lib/Jifty/Script.pm
+++ b/lib/Jifty/Script.pm
@@ -68,6 +68,7 @@ The alias table lets users type C<fastcgi> in place of C<FastCGI>.
 sub alias {
     return (
             fastcgi => "FastCGI",
+            writeccjs => "WriteCCJS",
            )
 }
 
diff --git a/lib/Jifty/Script/WriteCCJS.pm b/lib/Jifty/Script/WriteCCJS.pm
new file mode 100644
index 0000000..b65a71c
--- /dev/null
+++ b/lib/Jifty/Script/WriteCCJS.pm
@@ -0,0 +1,54 @@
+use strict;
+use warnings;
+
+package Jifty::Script::WriteCCJS;
+
+use base qw/Jifty::Script/;
+
+=head1 NAME
+
+Jifty::Script::WriteCCJS
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=head2 options
+
+Takes no options.
+
+=head2 run
+
+=cut
+
+sub run {
+    my $self = shift;
+    Jifty->new;
+
+    my ($ccjs) = Jifty->find_plugin('Jifty::Plugin::CompressedCSSandJS');
+
+    die "CompressedCSSandJS is not enabled for @{[Jifty->app_class]}\n"
+        unless $ccjs;
+
+    die "External generation is not enabled\n"
+        unless $ccjs->external_publish;
+
+    $ccjs->generate_css;
+    print "Wrote CSS to ".Jifty::CAS->key("ccjs","css-all").".css\n";
+    $ccjs->generate_javascript;
+    print "Wrote JS  to ".Jifty::CAS->key("ccjs","js-all").".js\n";
+}
+
+=head1 SEE ALSO
+
+L<Jifty::Plugin::CompressedCSSandJS>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright 2010, Best Practical Solutions.
+
+This is free software and may be modified and distributed under the same terms as Perl itself.
+
+=cut
+
+1;

commit f0adf7502095d37c81e037aba2ad26bb64692f36
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Fri Jun 25 20:17:44 2010 -0400

    generate_early is never what you want if you have external_publish
    
    generate_early was a hack to attempt to accomplish what external_publish
    does better; remove it.

diff --git a/lib/Jifty/Plugin/CompressedCSSandJS.pm b/lib/Jifty/Plugin/CompressedCSSandJS.pm
index cd92dc2..542daa6 100644
--- a/lib/Jifty/Plugin/CompressedCSSandJS.pm
+++ b/lib/Jifty/Plugin/CompressedCSSandJS.pm
@@ -26,7 +26,6 @@ Jifty::Plugin::CompressedCSSandJS - Compression of CSS and javascript files
         jsmin: /path/to/jsmin
         skipped_js:
             - complex.js
-        generate_early: 1
 
 
 =head1 DESCRIPTION
@@ -44,14 +43,9 @@ configure jsmin feature.
 
 skipped_js is a list of js that you don't want to compress for some reason.
 
-generate_early tells the plugin to compress the CSS and JS at process start
-rather than on the first request.  This can save time, especially if your
-JS minifier is slow, for the poor sucker who makes the first request.  Enabled
-by default.
-
 =cut
 
-__PACKAGE__->mk_accessors(qw(css js jsmin skipped_js generate_early external_publish));
+__PACKAGE__->mk_accessors(qw(css js jsmin skipped_js external_publish));
 
 =head2 init
 
@@ -69,7 +63,6 @@ sub init {
     $self->css( $opt{css} );
     $self->js( $opt{js} );
     $self->jsmin( $opt{jsmin} );
-    $self->generate_early( exists $opt{generate_early} ? $opt{generate_early} : 1 );
     $self->external_publish( $opt{external_publish} );
     if ($self->external_publish and not Jifty::CAS->backend("ccjs")->durable) {
         $self->log->warn("External publishing does not work with non-durable CAS stores; disabling");
@@ -82,8 +75,6 @@ sub init {
             callback  => sub { $self->_include_javascript(@_) },
             abortable => 1,
         );
-        Jifty->add_trigger( post_init => sub { $self->generate_javascript })
-            if $self->generate_early and not $self->external_publish;
     }
 
     if ( $self->css_enabled ) {
@@ -92,8 +83,6 @@ sub init {
             callback => sub { $self->_include_css(@_) },
             abortable => 1,
         );
-        Jifty->add_trigger( post_init => sub { $self->generate_css })
-            if $self->generate_early and not $self->external_publish;
     }
 }
 

commit ee75a3b4c4a54efe0fe55441b37bd56da5e6f1d6
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Fri Jun 25 20:05:59 2010 -0400

    Add a tiered CAS store; this allows a combination of durability and speed
    
    The most frequent use is likely a memory or memcached store in front of
    a durable file store.  Writes are pushed through all layers, and reads
    stop on the first hit.  Cache misses in higher tiers are written back on
    a later successful read.

diff --git a/lib/Jifty/CAS.pm b/lib/Jifty/CAS.pm
index d17cebc..c36d790 100644
--- a/lib/Jifty/CAS.pm
+++ b/lib/Jifty/CAS.pm
@@ -40,6 +40,9 @@ instances of a Jifty app using L<Jifty::CAS>.  The memcached store is
 limited to objects less than 1MB in size, however.
 L<Jifty::CAS::Store::LocalFile> provides a durable store, which is
 well-suited for sharing the cache across instances and restarts.
+Because of its speed, however, L<Jifty::CAS::Store::Nested> is suggested
+to provide a layered cache, most probably with fast memory-based cache
+in front of a durable, file-based cache.
 
 =head1 METHODS
 
diff --git a/lib/Jifty/CAS/Store/Nested.pm b/lib/Jifty/CAS/Store/Nested.pm
new file mode 100644
index 0000000..937e88b
--- /dev/null
+++ b/lib/Jifty/CAS/Store/Nested.pm
@@ -0,0 +1,126 @@
+use strict;
+use warnings;
+
+package Jifty::CAS::Store::Nested;
+use Any::Moose;
+extends 'Jifty::CAS::Store';
+has 'parts' => (is => 'rw');
+
+=head1 NAME
+
+Jifty::CAS::Store::Nested - A layered CAS store
+
+=head1 DESCRIPTION
+
+This is a layered backend for L<Jifty::CAS>, which provides a way to
+combine multiple CAS backends.  Writes are passed through to every
+layer, whereas reads stop on the first layer which contains the data.
+This allows a fast in-memory store to be layered on top of a durable
+file store, for instance.
+
+Configuration requires providing two or more CAS classes:
+
+    framework:
+      CAS:
+        Default:
+          Class: Jifty::CAS::Store::Nested
+          Parts:
+            - Class: Jifty::CAS::Store::Memory
+            - Class: Jifty::CAS::Store::LocalFile
+              Path: %var/cas%
+
+=cut
+
+=head1 METHODS
+
+=cut
+
+sub BUILD {
+    my $self = shift;
+    my @parts;
+    for my $part (@{ $self->parts || [] }) {
+        my %part = %{ $part };
+        my $storeclass = delete $part{Class};
+        Jifty::Util->require( $storeclass );
+        push @parts, $storeclass->new(
+            map {lc $_ => $part->{$_}} grep {$_ ne "Class"} keys %part
+        );
+    }
+    $self->parts( \@parts );
+}
+
+=head2 _store DOMAIN NAME BLOB
+
+Stores the BLOB (a L<Jifty::CAS::Blob>) in all parts, starting at the
+bottom.  Returns the key on success or undef on failure.
+
+=cut
+
+sub _store {
+    my ($self, $domain, $name, $blob) = @_;
+    # Writes start on the bottom
+    $_->_store($domain, $name, $blob) for reverse @{ $self->parts };
+}
+
+=head2 key DOMAIN NAME
+
+Returns the most recent key for the given pair of C<DOMAIN> and
+C<NAME>, or undef if none such exists.
+
+=cut
+
+sub key {
+    my ($self, $domain, $name) = @_;
+    # Reads start at the top
+    my @missing;
+    my $found;
+    for my $part (@{$self->parts}) {
+        if ($found = $part->key($domain, $name)) {
+            if (@missing) {
+                # If there were cache misses higher on the stack, write
+                # the correct value back to them
+                my $blob = $part->retrieve($domain, $found);
+                $_->_store($domain, $name, $blob) for @missing;
+            }
+            return $found;
+        }
+        push @missing, $part;
+    }
+    return;
+}
+
+=head2 retrieve DOMAIN KEY
+
+Returns a L<Jifty::CAS::Blob> for the given pair of C<DOMAIN> and
+C<KEY>, or undef if none such exists.
+
+=cut
+
+sub retrieve {
+    my ($self, $domain, $key) = @_;
+    # We don't have a way of storing just the blob at a key location,
+    # so we can't freshen the higher levels in this case.
+    for my $part (@{$self->parts}) {
+        my $found = $part->retrieve($domain, $key);
+        return $found if $found;
+    }
+    return;
+}
+
+=head2 durable
+
+If any of the parts are durable, the entire nested CAS backend is durable.
+
+=cut
+
+sub durable {
+    my $self = shift;
+    for my $part (@{$self->parts}) {
+        return 1 if $part->durable;
+    }
+    return 0;
+}
+
+no Any::Moose;
+__PACKAGE__->meta->make_immutable(inline_constructor => 0);
+1;

-----------------------------------------------------------------------


More information about the Jifty-commit mailing list