dirty-flag on inflated columns

24 messages Options
Embed this post
Permalink
1 2
Morgon Hed

dirty-flag on inflated columns

Reply Threaded More More options
Print post
Permalink

Hi!

I have a XML-column that I inflate/deflate to an object.

What works is to create a new instance of such an object and pass that to the column-accessor, then when updating the object is deflated and stored in the database.

The problem now is that I want to call a method on the inflated object which changes it's internal state but the problem is that now when I do an update DBIx::Class does not consider the column dirty and does not update it.

So basically what I need is way for the inflated object to mark the column as dirty when it's state has changed ...

Is there a way to do that?

Many thanks!


     

_______________________________________________
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/dbix-class@...
Moritz Onken

Re: dirty-flag on inflated columns

Reply Threaded More More options
Print post
Permalink

Am 04.05.2009 um 17:18 schrieb Morgon Hed:

>
> Hi!
>
> I have a XML-column that I inflate/deflate to an object.
>
> What works is to create a new instance of such an object and pass  
> that to the column-accessor, then when updating the object is  
> deflated and stored in the database.
>
> The problem now is that I want to call a method on the inflated  
> object which changes it's internal state but the problem is that now  
> when I do an update DBIx::Class does not consider the column dirty  
> and does not update it.
>
> So basically what I need is way for the inflated object to mark the  
> column as dirty when it's state has changed ...
>
> Is there a way to do that?

Yes there is:

http://search.cpan.org/~ribasushi/DBIx-Class-0.08102/lib/DBIx/Class/Row.pm#make_column_dirty

moritz

_______________________________________________
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/dbix-class@...
Morgon Hed

Re: dirty-flag on inflated columns

Reply Threaded More More options
Print post
Permalink
In reply to this post by Morgon Hed

Thanks!

One thing though:

I want the deflated object to mark the column as dirty, so to use "make_column_dirty" I need to keep a ref to the row in the inflated object.

So now the row would have ref to the object and the object back to the row ... so I have to be careful about not leaking memory with circular refs.

What is best practice in such cases? Weaken the ref to the row that the objects keeps?

Many thanks!

--- On Mon, 5/4/09, Moritz Onken <[hidden email]> wrote:

> From: Moritz Onken <[hidden email]>
> Subject: Re: [Dbix-class] dirty-flag on inflated columns
> To: "DBIx::Class user and developer list" <[hidden email]>
> Date: Monday, May 4, 2009, 11:25 AM
>
> Am 04.05.2009 um 17:18 schrieb Morgon Hed:
>
> >
> > Hi!
> >
> > I have a XML-column that I inflate/deflate to an
> object.
> >
> > What works is to create a new instance of such an
> object and pass that to the column-accessor, then when
> updating the object is deflated and stored in the database.
> >
> > The problem now is that I want to call a method on the
> inflated object which changes it's internal state but the
> problem is that now when I do an update DBIx::Class does not
> consider the column dirty and does not update it.
> >
> > So basically what I need is way for the inflated
> object to mark the column as dirty when it's state has
> changed ...
> >
> > Is there a way to do that?
>
> Yes there is:
>
> http://search.cpan.org/~ribasushi/DBIx-Class-0.08102/lib/DBIx/Class/Row.pm#make_column_dirty
>
> moritz
>
> _______________________________________________
> 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/dbix-class@...
>


     

_______________________________________________
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/dbix-class@...
Morgon Hed

Re: dirty-flag on inflated columns

Reply Threaded More More options
Print post
Permalink
In reply to this post by Morgon Hed

> Yes there is

Ok, I evidently I am too stupid.

I tried to store the second parameter that gets passed to the inflate-sub in the object.

This is a instance of my business-class (i.e. the one that derives from DBIx::Class).

Naively I had expected this also to be an instance of DBIx::Class::Row but it isn't...

So now I get "Can't locate object method "make_column_dirty" via package ...".

Can someone please explain to me how to get to a DBIx::Class::Row instance?

Boy - I really do hope that all of this will pay off in the future :-)





--- On Mon, 5/4/09, Moritz Onken <[hidden email]> wrote:

> From: Moritz Onken <[hidden email]>
> Subject: Re: [Dbix-class] dirty-flag on inflated columns
> To: "DBIx::Class user and developer list" <[hidden email]>
> Date: Monday, May 4, 2009, 11:25 AM
>
> Am 04.05.2009 um 17:18 schrieb Morgon Hed:
>
> >
> > Hi!
> >
> > I have a XML-column that I inflate/deflate to an
> object.
> >
> > What works is to create a new instance of such an
> object and pass that to the column-accessor, then when
> updating the object is deflated and stored in the database.
> >
> > The problem now is that I want to call a method on the
> inflated object which changes it's internal state but the
> problem is that now when I do an update DBIx::Class does not
> consider the column dirty and does not update it.
> >
> > So basically what I need is way for the inflated
> object to mark the column as dirty when it's state has
> changed ...
> >
> > Is there a way to do that?
>
> Yes there is:
>
> http://search.cpan.org/~ribasushi/DBIx-Class-0.08102/lib/DBIx/Class/Row.pm#make_column_dirty
>
> moritz
>
> _______________________________________________
> 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/dbix-class@...
>


     

_______________________________________________
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/dbix-class@...
Morgon Hed

Re: dirty-flag on inflated columns

Reply Threaded More More options
Print post
Permalink
In reply to this post by Morgon Hed

> Yes there is:

Not in my version it seems.

I have 0.08013 and in this version there does not seem to be a "make_column_dirty" method in DBIx::Class::Row (I just inspected the code).



--- On Mon, 5/4/09, Moritz Onken <[hidden email]> wrote:

> From: Moritz Onken <[hidden email]>
> Subject: Re: [Dbix-class] dirty-flag on inflated columns
> To: "DBIx::Class user and developer list" <[hidden email]>
> Date: Monday, May 4, 2009, 11:25 AM
>
> Am 04.05.2009 um 17:18 schrieb Morgon Hed:
>
> >
> > Hi!
> >
> > I have a XML-column that I inflate/deflate to an
> object.
> >
> > What works is to create a new instance of such an
> object and pass that to the column-accessor, then when
> updating the object is deflated and stored in the database.
> >
> > The problem now is that I want to call a method on the
> inflated object which changes it's internal state but the
> problem is that now when I do an update DBIx::Class does not
> consider the column dirty and does not update it.
> >
> > So basically what I need is way for the inflated
> object to mark the column as dirty when it's state has
> changed ...
> >
> > Is there a way to do that?
>
> Yes there is:
>
> http://search.cpan.org/~ribasushi/DBIx-Class-0.08102/lib/DBIx/Class/Row.pm#make_column_dirty
>
> moritz
>
> _______________________________________________
> 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/dbix-class@...
>


     

_______________________________________________
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/dbix-class@...
Matt S Trout

Re: dirty-flag on inflated columns

Reply Threaded More More options
Print post
Permalink
On Mon, May 04, 2009 at 09:35:08AM -0700, Morgon Hed wrote:
>
> > Yes there is:
>
> Not in my version it seems.
>
> I have 0.08013 and in this version there does not seem to be a "make_column_dirty" method in DBIx::Class::Row (I just inspected the code).

You're going to need 08100+ for that method.

The inflator gets passed the value and the original row object, among other
things - look at the InflateColumn docs for more info and if in doubt Dumper
@_ in your inflator to get a feel for it.

As for the circular reference, Scalar::Util::weaken will save you here.

--
        Matt S Trout         Catalyst and DBIx::Class consultancy with a clue
     Technical Director      and a commit bit: http://shadowcat.co.uk/catalyst/
 Shadowcat Systems Limited
  mst (@) shadowcat.co.uk        http://shadowcat.co.uk/blog/matt-s-trout/

_______________________________________________
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/dbix-class@...
Morgon Hed

Re: dirty-flag on inflated columns

Reply Threaded More More options
Print post
Permalink
In reply to this post by Morgon Hed

> You're going to need 08100+ for that method.

Thanks.

Any idea about when this will be become available on CPAN ?

--- On Tue, 5/5/09, Matt S Trout <[hidden email]> wrote:

> From: Matt S Trout <[hidden email]>
> Subject: Re: [Dbix-class] dirty-flag on inflated columns
> To: "DBIx::Class user and developer list" <[hidden email]>
> Date: Tuesday, May 5, 2009, 7:04 AM
> On Mon, May 04, 2009 at 09:35:08AM
> -0700, Morgon Hed wrote:
> >
> > > Yes there is:
> >
> > Not in my version it seems.
> >
> > I have 0.08013 and in this version there does not seem
> to be a "make_column_dirty" method in DBIx::Class::Row (I
> just inspected the code).
>
> You're going to need 08100+ for that method.
>
> The inflator gets passed the value and the original row
> object, among other
> things - look at the InflateColumn docs for more info and
> if in doubt Dumper
> @_ in your inflator to get a feel for it.
>
> As for the circular reference, Scalar::Util::weaken will
> save you here.
>
> --
>         Matt S Trout   
>      Catalyst and DBIx::Class
> consultancy with a clue
>      Technical Director   
>   and a commit bit: http://shadowcat.co.uk/catalyst/
>  Shadowcat Systems Limited
>   mst (@) shadowcat.co.uk       
> http://shadowcat.co.uk/blog/matt-s-trout/
>
> _______________________________________________
> 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/dbix-class@...
>




_______________________________________________
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/dbix-class@...
Peter Rabbitson-2

Re: dirty-flag on inflated columns

Reply Threaded More More options
Print post
Permalink
Morgon Hed wrote:
>> You're going to need 08100+ for that method.
>
> Thanks.
>
> Any idea about when this will be become available on CPAN ?
>

About 3 weeks ago?

_______________________________________________
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/dbix-class@...
Ash Berlin

Re: dirty-flag on inflated columns

Reply Threaded More More options
Print post
Permalink
In reply to this post by Morgon Hed


On Tue, 5 May 2009 04:58:06 -0700 (PDT), Morgon Hed <[hidden email]>
wrote:
>
>> You're going to need 08100+ for that method.
>
> Thanks.
>
> Any idea about when this will be become available on CPAN ?
>

It's was released on Apr 19, and there have been 2 maintenance releases
since.

-ash


_______________________________________________
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/dbix-class@...
Morgon Hed

Re: dirty-flag on inflated columns

Reply Threaded More More options
Print post
Permalink
In reply to this post by Morgon Hed

Ok - I've updated to 0.08102 and now I have a make_column_dirty but it does not seem to work.

Here is what I do for trying it out:

I have a class "SMU::Wrapper" that simply wraps some xml, it has an accessor "stuff" that allows me to get/set the wrapped xml.

I now inflate/deflate column "xml" like this:

__PACKAGE__->inflate_column('xml',
                                  {
                                   inflate => sub { SMU::Wrapper->new( stuff => $_[0], row => $_[1], column => "xml" ) },
                                   deflate => sub { $_[0]->stuff },
                                  }
                           );


Now I do a search and a find like this:

my $rs = $schema->resultset('Stream')->search(
                                               {},
                                                   {
                                                     select => [ 'id', { 'xmltype.getCLobVal' => 'xml' } ],
                                                     as     => [qw/id xml/],
                                                   }
                                             );


my $s = $rs->find(1);

This allows me to retrieve my object representing primary key 1 with inflated column, so far so good.

I can now do the following:

print $s->xml->stuff; # this prints the correct xml read from the db

$s->xml->stuff("<hubba/>");   # now I change it

print $s->xml->stuff;      # print it again to verify it really has changed (in memory)

$s->make_column_dirty('xml');

$s->update;

SMU::Schema->commit;

Now I trace with DBIC_TRACE=1 and I can see that indeed the call to make_column_dirty results in an update to the database, but unfortunately it is not updated to the new value I have set it to but again to the old value it had in the database before I changed to "<hubba/>"...

Can someone explain to me what I am doing wrong?

Many thanks!



--- On Tue, 5/5/09, Matt S Trout <[hidden email]> wrote:

> From: Matt S Trout <[hidden email]>
> Subject: Re: [Dbix-class] dirty-flag on inflated columns
> To: "DBIx::Class user and developer list" <[hidden email]>
> Date: Tuesday, May 5, 2009, 7:04 AM
> On Mon, May 04, 2009 at 09:35:08AM
> -0700, Morgon Hed wrote:
> >
> > > Yes there is:
> >
> > Not in my version it seems.
> >
> > I have 0.08013 and in this version there does not seem
> to be a "make_column_dirty" method in DBIx::Class::Row (I
> just inspected the code).
>
> You're going to need 08100+ for that method.
>
> The inflator gets passed the value and the original row
> object, among other
> things - look at the InflateColumn docs for more info and
> if in doubt Dumper
> @_ in your inflator to get a feel for it.
>
> As for the circular reference, Scalar::Util::weaken will
> save you here.
>
> --
>         Matt S Trout   
>      Catalyst and DBIx::Class
> consultancy with a clue
>      Technical Director   
>   and a commit bit: http://shadowcat.co.uk/catalyst/
>  Shadowcat Systems Limited
>   mst (@) shadowcat.co.uk       
> http://shadowcat.co.uk/blog/matt-s-trout/
>
> _______________________________________________
> 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/dbix-class@...
>




_______________________________________________
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/dbix-class@...
Matt S Trout

Re: dirty-flag on inflated columns

Reply Threaded More More options
Print post
Permalink
On Tue, May 05, 2009 at 08:37:37AM -0700, Morgon Hed wrote:

>
> Ok - I've updated to 0.08102 and now I have a make_column_dirty but it does not seem to work.
>
> Here is what I do for trying it out:
>
> I have a class "SMU::Wrapper" that simply wraps some xml, it has an accessor "stuff" that allows me to get/set the wrapped xml.
>
> I now inflate/deflate column "xml" like this:
>
> __PACKAGE__->inflate_column('xml',
>                                   {
>                                    inflate => sub { SMU::Wrapper->new( stuff => $_[0], row => $_[1], column => "xml" ) },
>                                    deflate => sub { $_[0]->stuff },
>                                   }
>                            );
>
>
> Now I do a search and a find like this:
>
> my $rs = $schema->resultset('Stream')->search(
>                                                {},
>                                                    {
>                                                      select => [ 'id', { 'xmltype.getCLobVal' => 'xml' } ],
>                                                      as     => [qw/id xml/],
>                                                    }
>                                              );
>
>
> my $s = $rs->find(1);
>
> This allows me to retrieve my object representing primary key 1 with inflated column, so far so good.
>
> I can now do the following:
>
> print $s->xml->stuff; # this prints the correct xml read from the db
>
> $s->xml->stuff("<hubba/>");   # now I change it
>
> print $s->xml->stuff;      # print it again to verify it really has changed (in memory)
>
> $s->make_column_dirty('xml');
>
> $s->update;
>
> SMU::Schema->commit;
>
> Now I trace with DBIC_TRACE=1 and I can see that indeed the call to make_column_dirty results in an update to the database, but unfortunately it is not updated to the new value I have set it to but again to the old value it had in the database before I changed to "<hubba/>"...

Oh FFS, that's not triggering the deflate of course.

I suspect what you actually want for the moment is:

$s->xml($s->xml);

Thoughts on whether make_column_dirty should clear the deflated value if an
inflated one is present, guys?

--
        Matt S Trout         Catalyst and DBIx::Class consultancy with a clue
     Technical Director      and a commit bit: http://shadowcat.co.uk/catalyst/
 Shadowcat Systems Limited
  mst (@) shadowcat.co.uk        http://shadowcat.co.uk/blog/matt-s-trout/

_______________________________________________
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/dbix-class@...
Peter Rabbitson-2

Re: dirty-flag on inflated columns

Reply Threaded More More options
Print post
Permalink
Matt S Trout wrote:

> On Tue, May 05, 2009 at 08:37:37AM -0700, Morgon Hed wrote:
>> Ok - I've updated to 0.08102 and now I have a make_column_dirty but it does not seem to work.
>>
>> Here is what I do for trying it out:
>>
>> I have a class "SMU::Wrapper" that simply wraps some xml, it has an accessor "stuff" that allows me to get/set the wrapped xml.
>>
>> I now inflate/deflate column "xml" like this:
>>
>> __PACKAGE__->inflate_column('xml',
>>                                   {
>>                                    inflate => sub { SMU::Wrapper->new( stuff => $_[0], row => $_[1], column => "xml" ) },
>>                                    deflate => sub { $_[0]->stuff },
>>                                   }
>>                            );
>>
>>
>> Now I do a search and a find like this:
>>
>> my $rs = $schema->resultset('Stream')->search(
>>                                                {},
>>                                                    {
>>                                                      select => [ 'id', { 'xmltype.getCLobVal' => 'xml' } ],
>>                                                      as     => [qw/id xml/],
>>                                                    }
>>                                              );
>>
>>
>> my $s = $rs->find(1);
>>
>> This allows me to retrieve my object representing primary key 1 with inflated column, so far so good.
>>
>> I can now do the following:
>>
>> print $s->xml->stuff; # this prints the correct xml read from the db
>>
>> $s->xml->stuff("<hubba/>");   # now I change it
>>
>> print $s->xml->stuff;      # print it again to verify it really has changed (in memory)
>>
>> $s->make_column_dirty('xml');
>>
>> $s->update;
>>
>> SMU::Schema->commit;
>>
>> Now I trace with DBIC_TRACE=1 and I can see that indeed the call to make_column_dirty results in an update to the database, but unfortunately it is not updated to the new value I have set it to but again to the old value it had in the database before I changed to "<hubba/>"...
>
> Oh FFS, that's not triggering the deflate of course.
>
> I suspect what you actually want for the moment is:
>
> $s->xml($s->xml);
>
> Thoughts on whether make_column_dirty should clear the deflated value if an
> inflated one is present, guys?
>

Yes, it should.

_______________________________________________
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/dbix-class@...
Marc Mims

Re: dirty-flag on inflated columns

Reply Threaded More More options
Print post
Permalink
In reply to this post by Matt S Trout
* Matt S Trout <[hidden email]> [090511 10:29]:
> Thoughts on whether make_column_dirty should clear the deflated value if an
> inflated one is present, guys?

That would break DBIx::Class::InflateColumn::FS, causing it to leave an
orphaned file in the file system.

        -Marc

_______________________________________________
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/dbix-class@...
Matt S Trout

Re: dirty-flag on inflated columns

Reply Threaded More More options
Print post
Permalink
On Mon, May 11, 2009 at 12:14:07PM -0700, Marc Mims wrote:
> * Matt S Trout <[hidden email]> [090511 10:29]:
> > Thoughts on whether make_column_dirty should clear the deflated value if an
> > inflated one is present, guys?
>
> That would break DBIx::Class::InflateColumn::FS, causing it to leave an
> orphaned file in the file system.

Why?

--
        Matt S Trout         Catalyst and DBIx::Class consultancy with a clue
     Technical Director      and a commit bit: http://shadowcat.co.uk/catalyst/
 Shadowcat Systems Limited
  mst (@) shadowcat.co.uk        http://shadowcat.co.uk/blog/matt-s-trout/

_______________________________________________
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/dbix-class@...
Marc Mims

Re: dirty-flag on inflated columns

Reply Threaded More More options
Print post
Permalink
* Matt S Trout <[hidden email]> [090512 11:34]:
> On Mon, May 11, 2009 at 12:14:07PM -0700, Marc Mims wrote:
> > * Matt S Trout <[hidden email]> [090511 10:29]:
> > > Thoughts on whether make_column_dirty should clear the deflated value if an
> > > inflated one is present, guys?
> >
> > That would break DBIx::Class::InflateColumn::FS, causing it to leave an
> > orphaned file in the file system.
>
> Why?

FS uses the deflated value to determine whether it needs to overwrite an
existing file or create a new file.  If the deflated value is cleared by
make_column_dirty, FS will always make a copy, potentially orphaning a
file in fs_column_path.

The deflated value is a unique filename in a path specified by
fs_column_path.  On deflation:

   - If the inflated value (a Path::Class::File object) and the deflated
     value represent the SAME file in fs_column_path, deflation is a
     no-op.

   - If there is no deflated value, a new, unique file is created in
     fs_column_path.

   - Otherwise, the contents of the file specified by the deflated value
     is overwritten by the contents of the file represented by the
     inflated value.

Then FS clears the inflated value (the Path::Class::File object, NOT the
source file itself).  That forces re-inflation on the next access so
that the inflated Path::Class::File points to the unique file in
fs_column_path, not the source file it was copied from.

If make_column_dirty clears the deflated value, all three cases above
will be treated like the second case.  In the first case, X will be
copied to a new, unique file, leaving X orphaned.

If make_column_dirty clears the deflated value, I will have some work to
do on FS to accommodate the change.

        -Marc

_______________________________________________
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/dbix-class@...
Matt S Trout

Re: dirty-flag on inflated columns

Reply Threaded More More options
Print post
Permalink
On Tue, May 12, 2009 at 01:51:00PM -0700, Marc Mims wrote:

> * Matt S Trout <[hidden email]> [090512 11:34]:
> > On Mon, May 11, 2009 at 12:14:07PM -0700, Marc Mims wrote:
> > > * Matt S Trout <[hidden email]> [090511 10:29]:
> > > > Thoughts on whether make_column_dirty should clear the deflated value if an
> > > > inflated one is present, guys?
> > >
> > > That would break DBIx::Class::InflateColumn::FS, causing it to leave an
> > > orphaned file in the file system.
> >
> > Why?
>
> FS uses the deflated value to determine whether it needs to overwrite an
> existing file or create a new file.  If the deflated value is cleared by
> make_column_dirty, FS will always make a copy, potentially orphaning a
> file in fs_column_path.

That's a bug. Deflation should not be dependent on the deflated value even
being there - store_inflated_column for e.g. deletes the deflated value.

So your code was already broken, you just hadn't noticed yet - doc patches
to ::InflateColumn on writing safe inflators very welcome :)

--
        Matt S Trout         Catalyst and DBIx::Class consultancy with a clue
     Technical Director      and a commit bit: http://shadowcat.co.uk/catalyst/
 Shadowcat Systems Limited
  mst (@) shadowcat.co.uk        http://shadowcat.co.uk/blog/matt-s-trout/

_______________________________________________
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/dbix-class@...
Marc Mims

Re: dirty-flag on inflated columns

Reply Threaded More More options
Print post
Permalink
* Matt S Trout <[hidden email]> [090513 09:29]:

> On Tue, May 12, 2009 at 01:51:00PM -0700, Marc Mims wrote:
> > * Matt S Trout <[hidden email]> [090512 11:34]:
> > > On Mon, May 11, 2009 at 12:14:07PM -0700, Marc Mims wrote:
> > > > * Matt S Trout <[hidden email]> [090511 10:29]:
> > > > > Thoughts on whether make_column_dirty should clear the deflated value if an
> > > > > inflated one is present, guys?
> > > >
> > > > That would break DBIx::Class::InflateColumn::FS, causing it to leave an
> > > > orphaned file in the file system.
> > >
> > > Why?
> >
> > FS uses the deflated value to determine whether it needs to overwrite an
> > existing file or create a new file.  If the deflated value is cleared by
> > make_column_dirty, FS will always make a copy, potentially orphaning a
> > file in fs_column_path.
>
> That's a bug. Deflation should not be dependent on the deflated value even
> being there - store_inflated_column for e.g. deletes the deflated value.

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.

Does that sound like the correct way for FS to deal with it?

> So your code was already broken, you just hadn't noticed yet - doc patches
> to ::InflateColumn on writing safe inflators very welcome :)

Obviously, I don't know how to write a safe inflator, yet. :)

        -Marc

_______________________________________________
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/dbix-class@...
Matt S Trout

Re: dirty-flag on inflated columns

Reply Threaded More More options
Print post
Permalink
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?

--
        Matt S Trout         Catalyst and DBIx::Class consultancy with a clue
     Technical Director      and a commit bit: http://shadowcat.co.uk/catalyst/
 Shadowcat Systems Limited
  mst (@) shadowcat.co.uk        http://shadowcat.co.uk/blog/matt-s-trout/

_______________________________________________
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/dbix-class@...
Marc Mims

Re: dirty-flag on inflated columns

Reply Threaded More More options
Print post
Permalink
* Matt S Trout <[hidden email]> [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/dbix-class@...
Matt S Trout

Re: dirty-flag on inflated columns

Reply Threaded More More options
Print post
Permalink
On Thu, May 14, 2009 at 12:51:02PM -0700, Marc Mims wrote:

> * Matt S Trout <[hidden email]> [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.

Look at Class::Accessor::Grouped

create get_fs_column and set_fs_column methods. Create accessors appropriately.

Basic InflateColumn is kind of inelegant here.

--
        Matt S Trout         Catalyst and DBIx::Class consultancy with a clue
     Technical Director      and a commit bit: http://shadowcat.co.uk/catalyst/
 Shadowcat Systems Limited
  mst (@) shadowcat.co.uk        http://shadowcat.co.uk/blog/matt-s-trout/

_______________________________________________
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/dbix-class@...
1 2