http://www.perlmonks.org?node_id=11132395

I needed to save the cookies in HTTP::Cookies into Netscape format (aka 'cookies.txt'). Unfortunately I discovered that this functionality is offered by HTTP::Cookies::Netscape alone, which is a subclass of HTTP::Cookies overriding parent-class's load() and save() methods.

However, if one has no control over the creation of the cookie jar (that is, someone has created it and passed it on to us) then it's a bit of a puzzle on how to do that, easily. Actually I did not find a way apart from doing it manually myself by copy-pasting the contents of HTTP::Cookies::Netscape::save() into my own "static" sub which takes in a HTTP::Cookies and saves it in "Netscape" format. But that's a round-about way which also suffers from missing on any patches on the original code.

What I eventually did was to bless the original HTTP::Cookies object into a HTTP::Cookies::Netscape. Call the save() method, which is now different. And when that's done, bless-back to HTTP::Cookies. I just hope this method is as clean as it looks like without side-effects, provided that nobody touches the object in-between its many blessings.

Here is code demostrating the bless/re-bless:

use HTTP::Cookies; use HTTP::Cookies::Netscape; use LWP::UserAgent; my $cookie_jar = HTTP::Cookies->new(); my $browser = LWP::UserAgent->new( ); $browser->cookie_jar( $cookie_jar ); # hit some pages # now re-bless the cookie jar to obtain ::Netscape functionality bless $cookie_jar => 'HTTP::Cookies::Netscape'; $cookie_jar->save("mycookies.txt"); # and now re-bless back to original bless $cookie_jar => 'HTTP::Cookies';

bw, bliako

Replies are listed 'Best First'.
Re: Saving HTTP::Cookies into Netscape format using bless/re-bless
by tangent (Parson) on May 11, 2021 at 15:57 UTC
    You could also use the scan() method to duplicate the stored cookies. It takes a callback which is invoked for each cookie and given arguments in the same order needed for set_cookie()
    my $cookie_jar = HTTP::Cookies->new(); # ... my $netscape_cookie_jar = HTTP::Cookies::Netscape->new(); my $callback = sub { $netscape_cookie_jar->set_cookie(@_); }; $cookie_jar->scan( $callback ); $netscape_cookie_jar->save("mycookies.txt");

      Your approach looks to me to be the safest way for the particular case of cookie-transformation.

Re: Saving HTTP::Cookies into Netscape format using bless/re-bless
by tobyink (Canon) on May 18, 2021 at 19:12 UTC

    Assuming $jar is your original cookie jar, you can just do:

    require HTTP::Cookies::Netscape; $jar->HTTP::Cookies::Netscape::save("mycookies.txt");

    No need to rebless. It could cause problems if save calls helper methods on $self which aren't defined in the parent class. But reblessing also could cause problems, so shruggles.

      Hmmm, you are doing this: HTTP::Cookies::Netscape::save($jar, "mycookies.txt"); which as you say has the problem if internally save() calls any of the "overwritten" methods (quoted because they are not overwritten at all). In this case these are just save() and load(). And I see one more special-but-common case: if it recurses save(). Also calling a logger helper method would be printing parent class signature.

      I am reblessing from a parent class and not just any class, so all state variables and internal methods that may be needed by the child will be inherited. That minimizes the risks. But are there still any?

      I see one, if subclass has its own state variables which are set/changed during operations prior to save() for example if it implements its own set_cookie() or new(). That bug will hurt! But it will not affect subclass-specific constants and such, these will assume their initial value upon reblessing.

      The use-case I had in mind is to use this "method" in order to transform from one cookie format to another using all those subclasses under HTTP::Cookies but I will have to examine each of these for above problems. I am not sure if other OO-based languages avoid this problem. I don't think Java does that or indeed can do that. It's up to the programmer to adhere to the "princaiples" (sic).

      bw, bliako

        Actually, these behave differently:

        Some::Class::method( $obj, @args ); $obj->Some::Class::method( @args );

        If Some::Class doesn't contain a sub called method, the former will fail, but the latter will walk Some::Class's @ISA.

Re: Saving HTTP::Cookies into Netscape format using bless/re-bless
by Fletch (Bishop) on May 11, 2021 at 14:21 UTC

    You might could use Storable (or whatever your favourite deep clone module is) to get a full copy of your source instance and then instead re-bless that copy into the Netscape class (and then you could toss that clone when done). That might ensure that you're (probably) completely divested from your other instance.

    The cake is a lie.
    The cake is a lie.
    The cake is a lie.