PrePAN

Sign in to PrePAN

HTTP::CookieMonster Easily Read and Update your Jar of HTTP::Cookies

Good

Synopsis

    use HTTP::CookieMonster;
    use WWW::Mechanize;

    my $mech = WWW::Mechanize->new;
    $mech->get( 'http://www.nytimes.com' );

    my $monster = HTTP::CookieMonster->new( cookie_jar => $mech->cookie_jar );
    my $cookie = $monster->feeling_lucky('RMID');
    print $cookie->val;

    $cookie->val('random-stuff');
    $monster->update( $cookie );

    # send altered cookie with new request
    $mech->get( 'http://www.nytimes.com' );

Description

HTTP::Cookies gets the job done, but it's a bit weird in some ways. For instance, instead of returning you a list of cookies, you have to use a callback:

$cookie_jar->scan( \&callback )

The callback will be invoked with 11 positional parameters:

0 version 1 key 2 val 3 path 4 domain 5 port 6 path_spec 7 secure 8 expires 9 discard 10 hash

That's a lot to remember and it doesn't make for very readable code.

Now, let's say you want to save or update a cookie. Now you're back to the many positional params yet again:

$cookie_jar->set_cookie( $version, $key, $val, $path, $domain, $port, $path_spec, $secure, $maxage, $discard, \%rest )

Also not readable. Unless you have an amazing memory, you may find yourself checking the docs regularly to see if you did, in fact, get all those params in the correct order etc.

HTTP::CookieMonster gives you a simple interface for getting and setting cookies. You can fetch an ArrayRef of all your cookies:

my @cookies = $monster->all_cookies;
foreach my $cookie ( @cookies ) {
    print $cookie->key;
    print $cookie->value;
    print $cookie->secure;
    print $cookie->domain;
    # etc
}

Or, if you know for a fact exactly what will be in your cookie jar, you can fetch a cookie by name.

my $cookie = $monster->feeling_lucky( 'plack_session' );

This gives you fast access to a cookie without a callback, iterating over a list etc. It's good for quick hacks and you can dump the cookie quite easily to inspect it's contents in a highly readable way:

HTTP::CookieMonster::Cookie  { 
    Parents       Moo::Object 
    Linear @ISA   HTTP::CookieMonster::Cookie, Moo::Object 
    public methods (12) : discard, domain, expires, hash, key, new, path, path_spec, port, secure, val, version 
    private methods (0) 
    internals: { 
        discard   undef, 
        domain   ".google.ca", 
        expires   1407698600, 
        hash   {}, 
        key   "PREF", 
        path   "/", 
        path_spec   1, 
        port   undef, 
        secure   undef, 
        val   "ID=38ab0cca20346b6f:FF=0:TM=1344626600:LM=1344626600:S=B_24d7BggBJEkhwi", 
        version   0 
    } 
}

If you want to mangle the cookie before the next request, that's easy too.

$cookie->val('woohoo');
$monster->set_cookie( $cookie );
$mech->get( $url );

Or, add an entirely new cookie to the jar:

# You can add an entirely new cookie to the jar via this method
use HTTP::CookieMonster::Cookie;
my $cookie = HTTP::CookieMonster::Cookie->new
    key       => 'cookie-name',
    val       => 'cookie-val',
    path      => '/',
    domain    => '.somedomain.org',
    path_spec => 1,
    secure    => 0,
    expires   => 1376081877
);

$monster->set_cookie( $cookie );
$mech->get( $url );

Comments

Consider renaming feeling_lucky to get_cookie_by_name - or at least providing both methods. Else, "you may find yourself checking the docs regularly" because the method name is not mnemonic :)
@perltk Well, that was actually the original method name, but the problem really is that you can only get the cookie by name if no other cookes from other domains (including subdomains) exist in the jar. So, either you know exactly what you're doing, or you're feeling lucky. My intention was to make totally clear that the method may return something that surprises you. But yes, I'll think about that some more. :)
This module has now been shipped: https://metacpan.org/module/HTTP::CookieMonster
I see, you have a point. I think your documentation suits your concerns. Good job!
@perltk Well, after thinking about it I did change the method name to get_cookie(). I made it clear in the docs that in list context the method will return all cookies with that name. If not, it will return only the 1st cookie. I felt like that covered the most popular use cases and also made it clear that there could easily be multiple cookies with the same name. Thanks for your feedback on that. :)

Please sign up to post a review.