* Matt S Trout <[email protected]> [090514 08:49]:
> On Wed, May 13, 2009 at 12:10:18PM -0700, Marc Mims wrote:
> > Ok.  So, now how to fix it?
> > 
> > I need a unique filename to deflate to, but I only want to create it
> > once.  Rather than using the deflated value {_column_data}{$column}, I
> > can create {_fs_column_filename}{$column} and use it, instead.  I need
> > to populate {_fs_column_filename} when a row is read from the db.  Looks
> > like I can do that by extending inflate_result.
> 
> Hmm. Maybe you need to generate an extra accessor group so you can tag the
> Path::Class or whatever object on the way in?

I'm not sure what you have in mind, here.  I'll poke you on IRC if the
following is off target.

I refactored my code based on the idea I outlined (quoted above).  It
passes all the existing tests, plus one I added to simulate the proposed
change to make_column_dirty:

    $book->make_column_dirty('cover_image');
    delete $book->{_column_data}{cover_image};
    $book->update;

I have attached an svn diff, but it's a bit difficult to read.  So, here
are the key methods I've overridden.  Does this look like a sane
approach?

    sub inflate_result {
        my ($class, $source, $me, $prefetch) = @_;

        my $new = $class->next::method($source, $me, $prefetch);
        
        while ( my($column, $data) = each %{$new->{_column_data}} ) {
            if ( $source->column_info($column)->{is_fs_column} && defined $data 
) {
                $new->{_fs_column_filename}{$column} = $data;
            }
        }
        
        return $new;
    }


    sub set_column {
        my ($self, $column, $new_value) = @_;

        #Deletes file storage when an fs_column is set to undef.
        if ( !defined $new_value && 
$self->result_source->column_info($column)->{is_fs_column}
                && $self->{_fs_column_filename}{$column} ) {
            $self->_fs_column_storage($column)->remove;
            delete $self->{_fs_column_filename}{$column};
        }

        return $self->next::method($column, $new_value);
    }

    sub set_inflated_column {
        my ($self, $column, $inflated) = @_;

        $self->next::method($column, $inflated);

        # reinflate
        if ( defined $inflated && ref $inflated && ref $inflated ne 'SCALAR'
                && $self->result_source->column_info($column)->{is_fs_column} ) 
{
            $inflated = $self->{_inflated_column}{$column} = 
$self->_fs_column_storage($column);
        }
        return $inflated;
    }


        -Marc
Index: t/01-fs_columns.t
===================================================================
--- t/01-fs_columns.t	(revision 5892)
+++ t/01-fs_columns.t	(working copy)
@@ -2,7 +2,7 @@
 use warnings;
 use strict;
 use DBICx::TestDatabase;
-use Test::More tests => 19;
+use Test::More tests => 20;
 use Path::Class qw/file/;
 use File::Compare;
 use lib qw(t/lib);
@@ -99,3 +99,12 @@
 $book->update({ cover_image => $file, cover_image_2 => $file });
 is( $book->cover_image, $cover_image, 'backing filename did not change' );
 isnt( $book->cover_image_2, $cover_image_2, 'backing filename did change for fs_new_on_update column' );
+
+
+# ensure FS works with the proposed change for DBIC: make_column_dirty to delete {_column_data}{$column}
+$storage = $book->cover_image;
+
+$book->make_column_dirty('cover_image');
+delete $book->{_column_data}{cover_image};
+$book->update;
+is( $book->cover_image, $storage, 'file backikng filename unchanged')
Index: lib/DBIx/Class/InflateColumn/FS.pm
===================================================================
--- lib/DBIx/Class/InflateColumn/FS.pm	(revision 5891)
+++ lib/DBIx/Class/InflateColumn/FS.pm	(working copy)
@@ -3,12 +3,12 @@
 use strict;
 use warnings;
 use DBIx::Class::UUIDColumns;
-use File::Spec;
-use File::Path;
+use File::Spec ();
+use File::Path ();
 use File::Copy ();
-use Path::Class;
+use Path::Class ();
 
-our $VERSION = '0.01003';
+our $VERSION = '0.01004';
 
 =head1 NAME
 
@@ -63,6 +63,25 @@
 
 =cut
 
+=head2 inflate_result
+
+=cut
+
+sub inflate_result {
+    my ($class, $source, $me, $prefetch) = @_;
+
+    my $new = $class->next::method($source, $me, $prefetch);
+    
+    while ( my($column, $data) = each %{$new->{_column_data}} ) {
+        if ( $source->column_info($column)->{is_fs_column} && defined $data ) {
+            $new->{_fs_column_filename}{$column} = $data;
+        }
+    }
+    
+    return $new;
+}
+
+
 =head2 register_column
 
 =cut
@@ -99,23 +118,18 @@
 }
 
 sub _fs_column_storage {
-    my ( $self, $column, $deflate ) = @_;
+    my ( $self, $column ) = @_;
 
     my $column_info = $self->result_source->column_info($column);
     $self->throw_exception("$column is not an fs_column")
         unless $column_info->{is_fs_column};
 
-    if ( (!$column_info->{fs_new_on_update} || !$deflate) && ( my $filename = $self->{_column_data}{$column} ) ) {
-        return Path::Class::File->new($column_info->{fs_column_path}, $filename);
-    }
-    else {
-        $filename = $self->fs_file_name($column, $column_info);
-        return Path::Class::File->new(
-            $column_info->{fs_column_path},
-            $self->_fs_column_dirs($filename),
-            $filename
-        );
-    }
+    $self->{_fs_column_filename}{$column} ||= do {
+        my $filename = $self->fs_file_name($column, $column_info);
+        File::Spec->catfile($self->_fs_column_dirs($filename), $filename);
+    };
+
+    return Path::Class::File->new($column_info->{fs_column_path}, $self->{_fs_column_filename}{$column});
 }
 
 =head2 _fs_column_dirs
@@ -146,27 +160,18 @@
 
     foreach my $col ( keys %$col_data ) {
         my $column_info = $self->result_source->column_info($col);
-        if ( $column_info->{is_fs_column}
-             && defined $col_data->{$col} ) {  # nothing special required for NULLs
-            $col_data->{$col} = undef;
+        if ( $column_info->{is_fs_column} && defined $col_data->{$col} ) {  # nothing special required for NULLs
+            delete $col_data->{$col};
             
             # pass the original file to produce a copy on deflate
-            my $accessor = $column_info->{accessor} || $col;
-            $changes->{$col} ||= $self->$accessor;
+            $changes->{$col} = $self->get_inflated_column($col);
         }
     }
 
     my $temp = bless { _column_data => $col_data }, ref $self;
     $temp->result_source($self->result_source);
 
-    my $copy = $temp->next::method($changes);
-
-    # force reinflation of fs colmuns on next access
-    delete $copy->{_inflated_column}{$_}
-        for grep { $self->result_source->column_info($_)->{is_fs_column} }
-            keys %$col_data;
-
-   return $copy;
+    return $temp->next::method($changes);
 }
 
 =head2 delete
@@ -178,49 +183,52 @@
 sub delete {
     my ( $self, @rest ) = @_;
 
-    for ( $self->columns ) {
-        if ( $self->result_source->column_info($_)->{is_fs_column} ) {
-            next unless $self->$_;
-            $self->$_->remove;
+    for my $column ( $self->columns ) {
+        my $column_info = $self->result_source->column_info($column);
+        if ( $column_info->{is_fs_column} ) {
+            my $accessor = $column_info->{accessor} || $column;
+            $self->$accessor && $self->$accessor->remove;
         }
     }
 
     return $self->next::method(@rest);
 }
 
-=head2 update
+=head2 set_column
 
-Deletes the associated file system storage when a column is set to null.
+Deletes file storage when an fs_column is set to undef.
 
 =cut
 
-sub update {
-    my ($self, $upd) = @_;
+sub set_column {
+    my ($self, $column, $new_value) = @_;
 
-    my %changed = ($self->get_dirty_columns, %{$upd || {}});
+    if ( !defined $new_value && $self->result_source->column_info($column)->{is_fs_column}
+            && $self->{_fs_column_filename}{$column} ) {
+        $self->_fs_column_storage($column)->remove;
+        delete $self->{_fs_column_filename}{$column};
+    }
 
-    # cache existing fs_colums before update so we can delete storge
-    # afterwards if necessary
-    my $s = $self->result_source;
-    my %fs_column =
-        map  { ($_, $self->$_) }
-        grep { $s->column_info($_)->{is_fs_column} }
-        keys %changed;
+    return $self->next::method($column, $new_value);
+}
 
-    # attempt super update, first, so it can throw on DB errors
-    # and perform other checks
-    $self->next::method($upd);
+=head2 set_inflated_column
 
-    while ( my ($column, $value) = each %changed ) {
-        if ( $s->column_info($column)->{is_fs_column} ) {
-            # remove the storage if the column was set to NULL
-            $fs_column{$column}->remove if !defined $value;
+Re-inflates after setting an fs_column.
 
-            # force reinflation on next access
-            delete $self->{_inflated_column}{$column};
-        }
+=cut
+
+sub set_inflated_column {
+    my ($self, $column, $inflated) = @_;
+
+    $self->next::method($column, $inflated);
+
+    # reinflate
+    if ( defined $inflated && ref $inflated && ref $inflated ne 'SCALAR'
+            && $self->result_source->column_info($column)->{is_fs_column} ) {
+        $inflated = $self->{_inflated_column}{$column} = $self->_fs_column_storage($column);
     }
-    return $self;
+    return $inflated;
 }
 
 =head2 _inflate_fs_column
@@ -233,6 +241,7 @@
     my ( $self, $column, $value ) = @_;
     return unless defined $value;
 
+    $self->{_fs_column_filename}{$column} = $value;
     return $self->_fs_column_storage($column);
 }
 
@@ -246,18 +255,20 @@
 
 sub _deflate_fs_column {
     my ( $self, $column, $value ) = @_;
-    
-    # already deflated?
-    return $value unless ref $value;
-    my $fs_new_on_update = $self->result_source->column_info($column)->{fs_new_on_update};
-    my $file = $self->_fs_column_storage($column, 1);
-    
-    if ( $fs_new_on_update && (my $oldfile = $self->{_column_data}{$column}) ) {
-        my $column_info = $self->result_source->column_info($column);
-        Path::Class::File->new($column_info->{fs_column_path}, $oldfile)->remove;
+
+    my $column_info = $self->result_source->column_info($column);
+
+    # kill the old storage, rather than overwrite, if fs_new_on_update
+    if ( $column_info->{fs_new_on_update} && $self->{_fs_column_filename}{$column} ) {
+        my $oldfile = $self->_fs_column_storage($column);
+        if ( $oldfile ne $value ) {
+            $oldfile->remove;
+            delete $self->{_fs_column_filename}{$column};
+        }
     }
     
-    if ( $fs_new_on_update || $value ne $file ) {
+    my $file = $self->_fs_column_storage($column);
+    if ( $value ne $file ) {
         File::Path::mkpath([$file->dir]);
 
         # get a filehandle if we were passed a Path::Class::File
@@ -268,8 +279,7 @@
         # force re-inflation on next access
         delete $self->{_inflated_column}{$column};
     }
-    my $basename = $file->basename;
-    return File::Spec->catfile($self->_fs_column_dirs($basename), $basename);
+    return $self->{_fs_column_filename}{$column};
 }
 
 =head2 table
Index: Changes
===================================================================
--- Changes	(revision 5891)
+++ Changes	(working copy)
@@ -1,3 +1,6 @@
+0.01004 2009-05-14
+    - don't rely on {_column_data} for deflate values
+
 0.01003 2009-04-17
     - use DBIx::Class::UUIDColumns for get_uuid rather than inheriting from it
     - fixed infinite recursion on create with fs_new_on_update column(s)
Index: README
===================================================================
--- README	(revision 5889)
+++ README	(working copy)
@@ -45,6 +45,7 @@
     updated.
 
 METHODS
+  inflate_result
   register_column
   fs_file_name
     Provides the file naming algorithm. Override this method to change it.
@@ -62,9 +63,12 @@
   delete
     Deletes the associated file system storage when a row is deleted.
 
-  update
-    Deletes the associated file system storage when a column is set to null.
+  set_column
+    Deletes file storage when an fs_column is set to undef.
 
+  set_inflated_column
+    Re-inflates after setting an fs_column.
+
   _inflate_fs_column
     Inflates a file column to a Path::Class::File object.
 
_______________________________________________
List: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/dbix-class
IRC: irc.perl.org#dbix-class
SVN: http://dev.catalyst.perl.org/repos/bast/DBIx-Class/
Searchable Archive: http://www.grokbase.com/group/[email protected]

Reply via email to