[Jifty-commit] jifty branch, wip/cas-rework, created. fec2d5bc55f00088f8b075654c955562a94ba2d7

Jifty commits jifty-commit at lists.jifty.org
Fri Jun 25 20:22:58 EDT 2010


The branch, wip/cas-rework has been created
        at  fec2d5bc55f00088f8b075654c955562a94ba2d7 (commit)

- Log -----------------------------------------------------------------
commit 3016b991cdd72b91bab3bc8d534b202dedac0ca3
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

diff --git a/lib/Jifty.pm b/lib/Jifty.pm
index 477ac74..2edf7d5 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 55207728237d2f1e4f0b1995f40c84ee8b1d8100
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Fri Jun 25 20:12:45 2010 -0400

    Make backend 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 60229c00c7cdd52bad921f80b5102d4d12c074b0
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Fri Jun 25 19:20:33 2010 -0400

    No reason memcached should have 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 d933093842e05bb4e1d2bc4e89198524f628186d
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Fri Jun 25 19:24:55 2010 -0400

    Split memory cache out

diff --git a/lib/Jifty/CAS/Store.pm b/lib/Jifty/CAS/Store.pm
index ce2c330..540df19 100644
--- a/lib/Jifty/CAS/Store.pm
+++ b/lib/Jifty/CAS/Store.pm
@@ -46,15 +46,6 @@ Stores the BLOB (a L<Jifty::CAS::Blob>) in the backend.  Returns the key.  Don't
 
 =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;
-}
-
 =head2 key DOMAIN NAME
 
 Returns the most recent key for the given pair of C<DOMAIN> and
@@ -62,11 +53,6 @@ C<NAME>, or undef if none such exists.
 
 =cut
 
-sub key {
-    my ($self, $domain, $name) = @_;
-    return $CONTAINER{$domain}{KEYS}{$name};
-}
-
 =head2 retrieve DOMAIN KEY
 
 Returns a L<Jifty::CAS::Blob> for the given pair of C<DOMAIN> and
@@ -74,10 +60,6 @@ C<KEY>, or undef if none such exists.
 
 =cut
 
-sub retrieve {
-    my ($self, $domain, $key) = @_;
-    return $CONTAINER{$domain}{DB}{$key};
-}
 
 no Any::Moose;
 __PACKAGE__->meta->make_immutable(inline_constructor => 0);
diff --git a/lib/Jifty/CAS/Store.pm b/lib/Jifty/CAS/Store/Memory.pm
similarity index 68%
copy from lib/Jifty/CAS/Store.pm
copy to lib/Jifty/CAS/Store/Memory.pm
index ce2c330..705d5a3 100644
--- a/lib/Jifty/CAS/Store.pm
+++ b/lib/Jifty/CAS/Store/Memory.pm
@@ -1,8 +1,9 @@
 use strict;
 use warnings;
 
-package Jifty::CAS::Store;
+package Jifty::CAS::Store::Memory;
 use Any::Moose;
+extends 'Jifty::CAS::Store';
 
 =head1 NAME
 
@@ -20,29 +21,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 0613c49..5a78e90 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 580b73d22e4b2615c30ff159a89d75048890454e
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Fri Jun 25 19:51:52 2010 -0400

    Multiple backends, config update

diff --git a/lib/Jifty/CAS.pm b/lib/Jifty/CAS.pm
index ccd4430..358d2ab 100644
--- a/lib/Jifty/CAS.pm
+++ b/lib/Jifty/CAS.pm
@@ -74,6 +74,23 @@ code set by this method (possibly for your use in the dispatcher).
 
 =cut
 
+sub config {
+    my $class = shift;
+    my $config = Jifty->config->framework('CAS');
+    warn YAML::Dump($config);
+    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 +124,27 @@ sub serve_by_name {
     return $res->finalize;
 }
 
+my %BACKENDS;
+my $DEFAULT_BACKEND;
+sub setup {
+    my $class = shift;
+    my $config = $class->config;
+
+    my $defaultclass = delete $config->{Default}{Class};
+    Jifty::Util->require( $defaultclass );
+    $DEFAULT_BACKEND = $defaultclass->new(
+        map {lc $_ => $config->{Default}{$_}} keys %{$config->{Default}}
+    );
+
+    for my $domain (keys %{$config->{Domains}}) {
+        my $storeclass = delete $config->{Domains}{$domain}{Class};
+        Jifty::Util->require( $storeclass );
+        $BACKENDS{$domain} = $storeclass->new(
+            map {lc $_ => $config->{Domains}{$domain}{$_}} keys %{ $config->{Domains}{$domain} }
+        );
+    }
+}
+
 sub _serve_404 {
     my ($class, $domain, $name, $msg) = @_;
     $msg ||= '';
@@ -114,27 +152,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..abba2c9 100644
--- a/lib/Jifty/CAS/Store/Memcached.pm
+++ b/lib/Jifty/CAS/Store/Memcached.pm
@@ -4,6 +4,11 @@ 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;
 
@@ -44,8 +49,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').":",
+            compress_threshold => $self->compress_threshold || 10240,
+        )
+    );
+}
 
 =head1 METHODS
 
@@ -55,10 +69,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 +91,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 +116,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 +130,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 5a78e90..241433b 100644
--- a/lib/Jifty/Config.pm
+++ b/lib/Jifty/Config.pm
@@ -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 55bffe9eeebe78ff7f86289e55bd55e05db5636d
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Fri Jun 25 20:01:29 2010 -0400

    CAS does its own serving; invalidates cdn

diff --git a/lib/Jifty/CAS.pm b/lib/Jifty/CAS.pm
index 358d2ab..5f4c1e4 100644
--- a/lib/Jifty/CAS.pm
+++ b/lib/Jifty/CAS.pm
@@ -91,39 +91,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 {
@@ -145,11 +112,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 {
@@ -176,4 +150,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 540df19..0187001 100644
--- a/lib/Jifty/CAS/Store.pm
+++ b/lib/Jifty/CAS/Store.pm
@@ -60,6 +60,57 @@ C<KEY>, or undef if none such exists.
 
 =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);
diff --git a/lib/Jifty/Handler.pm b/lib/Jifty/Handler.pm
index 25bc953..380709a 100644
--- a/lib/Jifty/Handler.pm
+++ b/lib/Jifty/Handler.pm
@@ -187,6 +187,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..4396dd5 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,9 +119,9 @@ 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[.js"></script>] );
+        qq[<script type="text/javascript" src="]
+          . Jifty::CAS->uri("ccjs","js-all")
+          . qq["></script>] );
 
     my $skipped_js = $self->skipped_js;
     if ( $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').'" />');
     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 4e3c56024099e5f3eaf10044f3943efb73786de5
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Fri Jun 25 20:03:51 2010 -0400

    Add local file caching

diff --git a/lib/Jifty/CAS/Store/LocalFile.pm b/lib/Jifty/CAS/Store/LocalFile.pm
new file mode 100644
index 0000000..e015db2
--- /dev/null
+++ b/lib/Jifty/CAS/Store/LocalFile.pm
@@ -0,0 +1,87 @@
+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 SYNOPSIS
+
+
+=head1 DESCRIPTION
+
+This is a local file backend for L<Jifty::CAS>.  For more information
+about Jifty's CAS, see L<Jifty::CAS/DESCRIPTION>.
+
+=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 . "/" . $blob->key;
+    unless (-e $path) {
+        lock_store($blob, $path)
+            or warn("Write of blob failed: $!") and return;
+    }
+
+    # Update the symlink
+    my $link = $dir . "/" . $name;
+    symlink( $blob->key, "$link.tmp")
+        or warn("Symlink failed: $!") and return;
+    rename( "$link.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;
+    return unless -l $link;
+    return readlink($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;
+    return unless -r $data;
+
+    return lock_retrieve($data);
+}
+
+no Any::Moose;
+__PACKAGE__->meta->make_immutable(inline_constructor => 0);
+1;

commit 287c934ee40d33525e1d8359c4ae715ae80c8e8e
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

diff --git a/lib/Jifty/Plugin/CompressedCSSandJS.pm b/lib/Jifty/Plugin/CompressedCSSandJS.pm
index 4396dd5..4bf0756 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 Jifty::CAS->backend("ccjs")->isa("Jifty::CAS::Store::Memory")) {
+        $self->log->warn("External publishing does not work with the in-memory CAS; 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').'" />');
@@ -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 63a26ab..ca60810 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..f85337f
--- /dev/null
+++ b/lib/Jifty/Script/WriteCCJS.pm
@@ -0,0 +1,52 @@
+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 unabled\n"
+        unless $ccjs->external_publish;
+
+    $ccjs->generate_css;
+    $ccjs->generate_javascript;
+}
+
+=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 bf758e06a4506a4f67c4b5d6e8d351ec3010c5a4
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

diff --git a/lib/Jifty/Plugin/CompressedCSSandJS.pm b/lib/Jifty/Plugin/CompressedCSSandJS.pm
index 4bf0756..fdab99e 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 Jifty::CAS->backend("ccjs")->isa("Jifty::CAS::Store::Memory")) {
         $self->log->warn("External publishing does not work with the in-memory CAS; 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 af4bf342044da770b01ec85c77bbdb174396eca4
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Fri Jun 25 20:05:59 2010 -0400

    Add a CAS store which allows, say, a memcached in front of a file store

diff --git a/lib/Jifty/CAS/Store/Nested.pm b/lib/Jifty/CAS/Store/Nested.pm
new file mode 100644
index 0000000..cdb8bb7
--- /dev/null
+++ b/lib/Jifty/CAS/Store/Nested.pm
@@ -0,0 +1,61 @@
+use strict;
+use warnings;
+
+package Jifty::CAS::Store::Nested;
+use Any::Moose;
+extends 'Jifty::CAS::Store';
+has 'parts' => (is => 'rw');
+
+sub BUILD {
+    my $self = shift;
+    my @parts;
+    for my $part (@{ $self->parts || [] }) {
+        my $storeclass = delete $part->{Class};
+        Jifty::Util->require( $storeclass );
+        push @parts, $storeclass->new(
+            map {lc $_ => $part->{$_}} keys %{ $part }
+        );
+    }
+    $self->parts( \@parts );
+}
+
+sub _store {
+    my ($self, $domain, $name, $blob) = @_;
+    # Writes go to the bottom
+    $_->_store($domain, $name, $blob) for reverse @{ $self->parts };
+}
+
+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)) {
+            warn "Found $domain:$name in $part";
+            if (@missing) {
+                my $blob = $part->retrieve($domain, $found);
+                warn "Got $blob to store in @missing";
+                $_->_store($domain, $name, $blob) for @missing;;
+            }
+            return $found;
+        }
+        push @missing, $part;
+    }
+    return;
+}
+
+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;
+}
+
+no Any::Moose;
+__PACKAGE__->meta->make_immutable(inline_constructor => 0);
+1;

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

    Add a not-quite-working S3 store

diff --git a/lib/Jifty/CAS/Store/AmazonS3.pm b/lib/Jifty/CAS/Store/AmazonS3.pm
new file mode 100644
index 0000000..87f267c
--- /dev/null
+++ b/lib/Jifty/CAS/Store/AmazonS3.pm
@@ -0,0 +1,133 @@
+use strict;
+use warnings;
+
+package Jifty::CAS::Store::AmazonS3;
+use Any::Moose;
+extends 'Jifty::CAS::Store';
+has 's3' => ( is => 'rw', isa => 'Net::Amazon::S3');
+has 'accesskey' => ( is => 'rw' );
+has 'secret' => ( is => 'rw' );
+has 'bucketprefix' => ( is => 'rw' );
+has 'known_good' => ( is => 'rw' );
+
+use Net::Amazon::S3;
+
+=head1 NAME
+
+Jifty::CAS::Store::AmazonS3 - An S3 backend for Jifty's CAS
+
+=head1 SYNOPSIS
+
+
+=head1 DESCRIPTION
+
+This is an S3 backend for L<Jifty::CAS>.  For more information about
+Jifty's CAS, see L<Jifty::CAS/DESCRIPTION>.
+
+=cut
+
+=head1 METHODS
+
+=cut
+
+sub BUILD {
+    my $self = shift;
+    $self->s3( Net::Amazon::S3->new(
+        {
+            aws_access_key_id     => $self->accesskey,
+            aws_secret_access_key => $self->secret,
+            retry                 => 1,
+        }
+    ) );
+
+    $self->known_good({});
+    my $response = $self->s3->buckets or die "Getting buckets";
+    for my $bucket ( @{ $response->{buckets} }) {
+        next unless index($bucket->bucket, $self->bucketprefix) == 0;
+        $self->known_good->{$bucket->bucket} = $bucket;
+    }
+}
+
+sub get_bucket {
+    my $self = shift;
+    my $domain = shift;
+    return $self->known_good->{$domain} if $self->known_good->{$domain};
+
+    my $bucket = $self->s3->add_bucket( {
+        bucket => $self->bucketprefix . $domain,
+        acl_short => "public-read",
+    }) or die "Adding bucket for $domain";
+    return $self->known_good->{$domain} = $bucket;
+}
+
+=head2 _store DOMAIN NAME BLOB
+
+Stores the BLOB (a L<Jifty::CAS::Blob>) on S3.  Returns the key on
+success or undef on failure.
+
+=cut
+
+sub _store {
+    my ($self, $domain, $name, $blob) = @_;
+    my $bucket = $self->get_bucket($domain);
+    $bucket->add_key( $blob->key, $blob->content, $blob->metadata )
+        or die "Adding key for ".$blob->key." for $name";
+    $bucket->set_acl({acl_short => "public-read", key => $blob->key } );
+    $bucket->copy_key(
+        $name,
+        "/" . $bucket->bucket . "/" . $blob->key,
+    ) or die "Copying key for $name";
+    $bucket->set_acl({acl_short => "public-read", key => $blob->key } );
+    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 $bucket = $self->get_bucket($domain);
+    my $data = $bucket->head_key($name) or die "Getting HEAD of $name";
+    return $data->{etag};
+}
+
+=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 $bucket = $self->get_bucket($domain);
+    my $data = $bucket->get_key($key);
+    return unless $data;
+    my $blob = Jifty::CAS::Blob->new();
+    $blob->content( $data->{value} );
+    $blob->key( $data->{etag} );
+    $blob->metadata( { $data->{meta}, content_type => $data->{content_type} });
+    return $blob;
+}
+
+sub uri {
+    my $self = shift;
+    my ($domain, $name) = @_;
+    return "http://s3.amazonaws.com/" . $self->bucketprefix . $domain . "/" . $self->key($domain, $name);
+}
+
+sub serve {
+    my ($self, $domain, $arg, $env) = @_;
+
+    my $res = Plack::Response->new(302);
+    $res->header( Location => "http://s3.amazonaws.com/" . $self->bucketprefix . "$domain/$arg" );
+    return $res->finalize;
+}
+
+no Any::Moose;
+__PACKAGE__->meta->make_immutable(inline_constructor => 0);
+1;

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


More information about the Jifty-commit mailing list