Monday, 14 June 2010

Tweetylicious - a Twitter-like microblogging app in just one file!

So, I've been playing a little with Mojolicious::Lite, and here's what I came up with :-)

Tweetylicious is a small - but rather complete - microblogging web application in a single file! It is meant to demonstrate how easy and fun it is to create your own Web applications using modern Perl 5 and jQuery!

Some features:

  • Multi-user, with homepages, search and list of followers/following
  • Nice, clean, pretty interface (at least I think so :P)
  • User avatar images provided by gravatar
  • Unicode support
  • Well structured, commented code, easy to expand and customize
  • Encrypted online sessions
  • Uses an actual database (SQLite) and stores encrypted user password

If you want it, the full code is in github. Removing just blank lines and comments, the Model has ~80 lines, the Controller ~110 lines, templates ~170 lines, plus ~90 lines of static css and ~60 of static javascript. And that's the whole app :D

How do you run it?

perl tweetylicious.pl daemon

You'll need Mojolicious and ORLite - two very lightweight modules - to run the app, and that's about it! A live Internet connection is also good, since it fetches jQuery on the fly.

Mind you, it's far from perfect (bug reports and patches always welcome!). I wrote it as a demo to show the kind of stuff you can quickly achieve with Perl. It's totally usable and might be a good fit for quick deployment and customization on internal networks, but if you're looking for a business ready microblogging solution, you might want to look at status.net (which powers identi.ca). But it's waaaay bigger ;-P


Tutorial...ish

I tried making the commits linear and modular, so newcomers can take a look at git log for a "tutorial":

  1. initial commit - diff, full file
  2. adding index page (and route) - diff, full file
  3. separating common html into a shareable 'layout' - diff, full file
  4. adding (all) css - sorry, this is not a css tutorial :P - diff, full file
  5. users will need to 'login' and 'join' (register)! - diff, full file
  6. first jQuery contact: turning links into buttons - diff, full file
  7. creating the 'User' model schema in our database - diff, full file
  8. registering users: the template - diff, full file
  9. registering users: the controller - diff, full file
  10. registering users: validating registration data - diff, full file
  11. registering users: prevent usernames that are part of a route - diff, full file
  12. user login: the template (and basic route) - diff, full file
  13. user login: the controller (handling form submission) - diff, full file
  14. user logout: controller, and option in template - diff, full file
  15. the user's homepage (template) - diff, full file
  16. the user's homepage (controller) - diff, full file
  17. adding a 'not found' page - diff, full file
  18. making 'login' and 'join' redirect to user's homepage - diff, full file
  19. updating our model: posts! - diff, full file
  20. making user's homepage show posts (but user can't create them just yet - diff, full file
  21. updating our controller: creating posts - diff, full file
  22. updating our controller: deleting posts - diff, full file
  23. updating our controller: turning common posting auth code into a ladder - diff, full file
  24. more jQuery: styling post submit into a button too - diff, full file
  25. more jQuery: showing how many characters are left in a post - diff, full file
  26. more jQuery: highlighting posts on hover - diff, full file
  27. more jQuery: formatting our content for RTs (@user) - diff, full file
  28. creating posts via Ajax (rather, Ajaj, since we're using JSON ;) - diff, full file
  29. deleting posts via Ajax (rather, Ajaj, since we're using JSON ;) - diff, full file
  30. searching posts: search form template - diff, full file
  31. searching posts: jQuery effects - diff, full file
  32. searching posts: model - diff, full file
  33. searching posts: controller - diff, full file
  34. searching posts: results template - diff, full file
  35. searching posts: jQuery (naive) formatting for topics (#topic) - diff, full file
  36. followers and following: the model - diff, full file
  37. followers and following: the controller - diff, full file
  38. followers and following: template changes - diff, full file
Newer commits will likely be not as organized, and mostly bugfixing, but this should get people going - hopefully :P

Well, that's it. Have fun!

13 comments:

  1. if you hurry, you can see it in action here.

    Thanks vti for hosting a demo! You can find me there too =)

    ReplyDelete
  2. Well, it's a single file application only in the sense that you don't count all of the other files it requires as dependencies. That is, it's not a stand-alone application as you imply.

    ReplyDelete
  3. @brian: If by "all of the other files it requires as dependencies" you mean Mojolicious, ORLite and jQuery, I did mention them. Sure enough, they also depend on other modules (and files), but fortunately they're all core, and bundled with perl. If you want to be thorough, perl itself is a dependency too, and you won't be able to take much advantage of it without a web browser client either.

    It *is* a single file application in the sense that the app itself is in tweetylicious.pl and nowhere else, so hopefully a beginner will be able to look at the app and not only understand what does what, but easily tweak things as he/she feels necessary - I did mention it is a demo, didn't I? - It's also pretty small without actually making any effort in so, which again is to show how much a beginner can achieve with Perl even using minimalistic frameworks such as Mojolicious::Lite and ORLite.

    I'm sorry if it wasn't clear enough. Not only I'm not dismissing dependencies, I couldn't have done it like so without them. My point was the little dependencies Tweetylicious has are very lightweight and, as such, it should be very easy to install, run and fiddle with.

    ReplyDelete
  4. Eh, ignore the trolls, it's a single file :-)

    I thought that your use of source control is a *really* cool way to do a tutorial. That would be an AWESOME way to show people how to develop really any kind of application or module. I hope to do something similar at some point. Thanks for the inspiration!

    ReplyDelete
  5. @fREW: Thanks! Actually, I got that idea from the Cat Book, which presents code change in a diff-like format and lets you download the resulting repository.

    Using git for that *really* helped, btw. Just do your regular development and then have fun with git rebase. You can do pretty much anything with your commits as long as you don't push them upstream, including merging, editing, deleting (I know you're a fan and know all this already, I'm just letting other people into the secret too ;P)

    ReplyDelete
  6. Excellent post, $full_examples_as_tutorials++, the git diffs are nice.

    Maybe it just need another file (tests) and some POD in the code but it's nice this way too :-)

    I've learned last years CGI, Catalyst and Dancer, this post (and the websocket support seen in other examples) is encouraging me, to learn mojolicious too.

    Greetings

    ReplyDelete
  7. @poisonbit: thanks!

    Regarding tests and Pod and whatnot, I did it this way just to cause a bigger impact on the occasional viewer. They'll open the repository, see only that single file and go "wow, this is for real" - one can hope at least :D

    If people are interested, I might put it in CPAN after a couple updates, in which case it will include standard Makefile.PL and all that (which would in turn make it even easier to install using things like miyagawa's cpanm).

    As for learning Mojolicious, do it! If you know Dancer, it has about the same structure and syntax, only Mojolicious has much more features - at this point at least :)

    Check out the documentation for Mojolicious::Lite. It should have everything you need. You can also reach #mojo in irc.perl.org or the mailing list if you need help!

    ReplyDelete
  8. It is faster than the original Twitter written on Ruby.

    ReplyDelete
  9. It doesn't work :-(

    Tue Jun 22 16:20:06 2010 debug Mojolicious::Plugin::EpRenderer:38 [39639]: Caching template "/usr/home/as/prg/perl/mojo/mojo/tweetylicious/templates/index.html.ep" with stash "callback, content, rendered, started".
    Tue Jun 22 16:20:06 2010 debug Mojolicious::Plugin::EpRenderer:38 [39639]: Caching template "/usr/home/as/prg/perl/mojo/mojo/tweetylicious/templates/layouts/main.html.ep" with stash "callback, content, rendered, started".
    String found where operator expected at (eval 292) line 15, near "session 'name'"
    (Do you need to predeclare session?)
    String found where operator expected at (eval 292) line 16, near "session 'name'"
    (Do you need to predeclare session?)
    Tue Jun 22 16:20:06 2010 error Mojolicious::Plugin::EplRenderer:84 [39639]: Template error in "layouts/main.html.ep": Error around line 15.
    13: <div id="header"><a href="/"><div id="logo">Tweetylicious!</div></a>
    14: <div class="options">
    15: % if (session 'name') {
    16: <a href="/<%= session 'name' %>">Home</a><a href="/logout">Sign-Out</a>
    17: % } else {

    But /static.css and /static.js served OK.

    Не работает. Хотя статику (стили и скрипты) успешно выдаёт.

    ReplyDelete
  10. @Shoorick: that's very odd. Can you please make sure you have the latest CPAN version of Mojolicious?

    http://search.cpan.org/perldoc?Mojolicious

    спасибо!

    ReplyDelete
  11. The username and bio display chinese as alien chars.

    Patch Orlite.pm as below:

    my $dbh = DBI->connect( $dsn, undef, undef, {
    PrintError => 0,
    RaiseError => 1,
    sqlite_unicode => 1, #patch enable default deflating utf8
    } );

    In tweetymojolicious.pl, find the fllowing in two places:

    <%= b($post->{content})->decode('UTF-8')->to_string %>

    change to:
    <%= $post->{content} %>

    Then non latin chars display normal in everywhere.

    This is a good mojo example.

    ReplyDelete
  12. http://louboutinoutletmall.com
    Top shop for christian louboutin intern crested velvet loafers Shoes,luxury French Shoes On Sale.The Christian Louboutin Asteroid 160mm Shoes professed goal is to make a woman look sexy, beautiful, to make her legs look as long as they can.As expected,we offers you this year's new collections of christian louboutin alfred flat Shoes,up to 90%% off,free delivery.free gifts.ggvgdfdsfcxcs

    ReplyDelete
  13. could federation be plugged into it?

    eg ostatus, dfrn or any other suitable protocol?


    would love to see something like Friendica or StatusNet for perl!

    perl+sqlite might be more suitable for those little raspberry pi servers than php/mysql (which needs too much ram) .. and as a bonus sqlite has a quite good fulltext search capability if you set a couple of flags when compiling it (sqlite FTS 3/4)

    ReplyDelete