Yarnline (a Ravelry Mashup)

August 4th, 2012
Perl

A while ago, I spotted TimelineJS, a JavaScript library to create gorgeous timelines, and earmarked it to a future app. Last week, I finally get to play with it by creating Yarnline, an itsy-bitsy mashup app that takes the projects of a Ravelry user and display them in a chronological manner, like so:

Yarnline Screenshot

The app is running on my home server (but requires you to have a Ravelry account). The code itself is, for once, fairly banal. It’s using Dancer (and its beautiful, no-fuss serializing capacities), and HTML::Mason as its template system (which is slightly overkill considering that one of the pages of the app is purely static, and the other one has one — yes, one — variable), with a dash of Dancer::Plugin::Cache::CHI to keep project lists around for a while. Slightly more interesting, it also uses Net::OAuth to communicate with Ravelry’s REST web service (I also looked at

Net::OAuth::Simple, but at the end decided it wasn't much simpler than its big brother).

One wouldn’t know by looking at the code, but getting the OAuth was… quite the challenge. Between the request tokens, the access tokens, the passing of urls between the client, the service provider and the app, I have to admit that at some points I was finding the whole thing oddly reminescent of this video. Mind you, the documentation of the module was generally helpful, but left some questions unanswered. Like, how get back your access token without running back to the web service every time you need it. After a few hours of head-desk interfacting, though, I think I figured it out:

use Net::OAuth::Client;
use Net::OAuth::AccessToken;

get '/auth' => sub {
    # that one is simple...
    redirect auth_client->authorize_url;
};

get '/auth/callback' => sub {

    # get the access token from the web service...
    my $access = auth_client->get_access_token(
        param( 'oauth_token' ),
        param( 'oauth_verifier' ),
    );

    # ... and keep the information you need to rebuild it
    session rav_token        => $access->token;
    session rav_token_secret => $access->token_secret;
    session username         => param 'username';

    forward '/';
};

get '/timeline/:raveler' => sub {

    # access_token() is the good part there
    my $projects = from_json(
        access_token->get(
            join '/', '/projects', $raveler, 'list.json'
        )->content
    );

    # and do something with $projects...
};

# and yes, those two functions just scream to
# be Dancer::Pluginified

sub auth_client() {
    my $rav = config->{ravelry};

    return Net::OAuth::Client->new(
        $rav->{tokens}{consumer_key},
        $rav->{tokens}{consumer_secret},
        site                => 'https://api.ravelry.com',
        request_token_path  => '/oauth/request_token',
        authorize_path      => '/oauth/authorize',
        access_token_path   => '/oauth/access_token',
        callback            => uri_for( $rav->{callback} ),
        session             => &session,
    );
}

sub access_token {
    return Net::OAuth::AccessToken->new(
        client       => auth_client(),
        token        => session( 'rav_token' ),
        token_secret => session( 'rav_token_secret' ),
    );
}

Oh yeah, and today I began to switch the templates from Mason to my experiment-in-progress, Template::Caribou. How that went? Well, that’s a story for another blog entry…

Seen a typo or an error? Submit an edit on GitHub!